ingo@1115: /* ingo@1115: * Copyright (c) 2010 by Intevation GmbH ingo@1115: * ingo@1115: * This program is free software under the LGPL (>=v2.1) ingo@1115: * Read the file LGPL.txt coming with the software for details ingo@1115: * or visit http://www.gnu.org/licenses/ if it does not exist. ingo@1115: */ ingo@1115: ingo@621: package de.intevation.gnv.utils; ingo@621: sascha@779: import de.intevation.artifactdatabase.Config; sascha@779: import de.intevation.artifactdatabase.XMLUtils; sascha@779: sascha@779: import de.intevation.artifacts.ArtifactNamespaceContext; sascha@779: sascha@779: import de.intevation.gnv.wms.LayerInfo; sascha@779: ingo@622: import java.io.File; ingo@622: import java.io.FileNotFoundException; ingo@622: import java.io.FileWriter; ingo@622: import java.io.IOException; ingo@622: import java.io.StringWriter; ingo@622: import java.io.Writer; sascha@779: ingo@622: import java.util.ArrayList; ingo@622: import java.util.Date; ingo@622: import java.util.List; ingo@622: ingo@622: import javax.xml.xpath.XPathConstants; ingo@622: ingo@621: import org.apache.log4j.Logger; sascha@779: ingo@622: import org.apache.velocity.Template; ingo@622: import org.apache.velocity.VelocityContext; sascha@779: ingo@622: import org.apache.velocity.app.VelocityEngine; sascha@779: ingo@622: import org.w3c.dom.Document; ingo@622: import org.w3c.dom.Node; ingo@622: import org.w3c.dom.NodeList; ingo@621: ingo@621: /** ingo@806: * This class iterates over a bunch of directories, searches for meta ingo@806: * information coresponding to shapefiles and creates a mapfile which is used by ingo@806: * a MapServer. sascha@807: * sascha@780: * @author Ingo Weinzierl ingo@621: */ ingo@621: public class MapfileGenerator ingo@621: extends Thread ingo@621: { ingo@806: /** ingo@806: * The configured template path. ingo@806: */ ingo@622: public static final String TEMPLATE_PATH = ingo@625: "/artifact-database/gnv/map-generator/templates/path/text()"; ingo@622: ingo@806: /** ingo@806: * The configured template mapfile. ingo@806: */ ingo@622: public static final String TEMPLATE_MAPFILE = ingo@625: "/artifact-database/gnv/map-generator/templates/maptemplate/text()"; ingo@621: ingo@806: /** ingo@806: * The configured mapfile path. ingo@806: */ ingo@621: public static final String MAPFILE_PATH = ingo@625: "/artifact-database/gnv/map-generator/mapfile/@path"; ingo@622: ingo@806: /** ingo@806: * The configured shapefile base directory where the subdirectories for each ingo@806: * artifact are stored. ingo@806: */ ingo@622: public static final String SHAPEFILE_BASE_DIR = ingo@625: "/artifact-database/gnv/shapefile-directory/@path"; ingo@622: ingo@806: /** ingo@806: * The configured velocity logfile. ingo@806: */ ingo@624: public static final String VELOCITY_LOGFILE = ingo@624: "/artifact-database/velocity/logfile/@path"; ingo@624: ingo@806: /** ingo@840: * The URL for calling the mapserver ingo@840: */ ingo@840: public static final String MAPSERVER_URL = ingo@840: "/artifact-database/mapserver/server/@path"; ingo@840: ingo@840: /** ingo@806: * The name of the file storing meta information coresponding to shapefiles. ingo@806: */ ingo@622: public static final String META_FILE_NAME = "meta.xml"; ingo@622: ingo@806: /** ingo@806: * The XPath to layer nodes in the meta information xml file. ingo@806: */ ingo@622: public static final String XPATH_LAYER = "/art:meta/art:layer"; sascha@807: ingo@622: public static final String XPATH_LAYER_NAME = "art:name"; ingo@730: public static final String XPATH_LAYER_TITLE = "art:title"; ingo@622: public static final String XPATH_LAYER_TYPE = "art:type"; ingo@622: public static final String XPATH_LAYER_DATA = "art:data"; ingo@622: public static final String XPATH_LAYER_STATUS = "art:status"; ingo@622: public static final String XPATH_LAYER_MODEL = "art:model"; ingo@621: ingo@621: protected static final long SLEEPTIME = 10 * 1000L; // 10 seconds ingo@621: ingo@621: private static Logger logger = Logger.getLogger(MapfileGenerator.class); ingo@621: ingo@621: private static MapfileGenerator instance; ingo@621: ingo@625: private File mapfile; ingo@840: private String mapServerUrl; ingo@625: private String shapefileDirectory; ingo@625: private String templatePath; ingo@625: private String velocityLogfile; ingo@625: ingo@622: private VelocityEngine velocityEngine; ingo@621: private boolean lock[]; ingo@621: ingo@621: ingo@621: ingo@621: private MapfileGenerator() { ingo@621: lock = new boolean[1]; ingo@621: } ingo@621: ingo@621: ingo@806: /** ingo@806: * A main method which can be used to create a mapfile without a running ingo@806: * artifact server.
ingo@806: * NOTE: This method is not implemented yet! sascha@807: * ingo@806: * @param args Arguments. ingo@806: */ ingo@621: public static void main(String[] args) { ingo@622: // TODO IMPLEMENT ME ingo@621: } ingo@621: ingo@621: ingo@806: /** ingo@806: * Returns the instance of this generator. ingo@806: * ingo@806: * @return the instance. ingo@806: */ ingo@621: public static synchronized MapfileGenerator getInstance() { ingo@621: if (instance == null) { ingo@621: instance = new MapfileGenerator(); ingo@621: instance.setDaemon(true); ingo@621: instance.start(); ingo@621: } ingo@621: ingo@621: return instance; ingo@621: } ingo@621: ingo@621: ingo@806: /** ingo@806: * Triggers the mapfile generation process. ingo@806: */ ingo@621: public void update() { ingo@621: synchronized (lock) { ingo@622: logger.debug("update"); ingo@621: lock[0] = true; ingo@621: lock.notify(); ingo@621: } ingo@621: } ingo@621: ingo@621: ingo@806: /** ingo@806: * Thread to generate a mapfile. ingo@806: */ ingo@806: @Override ingo@621: public void run() { ingo@622: logger.debug("Start MapfileGenerator thread..."); ingo@621: try { ingo@621: for (;;) { ingo@621: synchronized (lock) { ingo@621: while (!lock[0]) { ingo@621: lock.wait(SLEEPTIME); ingo@621: } ingo@621: lock[0] = false; ingo@621: } ingo@622: ingo@622: logger.debug("Start sync process now..."); ingo@621: generate(); ingo@621: } ingo@621: } ingo@621: catch (InterruptedException ie) { ingo@622: logger.debug("MapfileGenerator thread got an interrupt."); ingo@622: } ingo@622: finally { ingo@622: logger.debug("THREAD END"); ingo@621: } ingo@621: } sascha@778: ingo@806: /** ingo@806: * Method to check the existance of a template file. ingo@806: * ingo@806: * @param templateID The name of a template. ingo@806: * @return true, of the template exists - otherwise false. ingo@806: */ tim@724: public boolean templateExists(String templateID){ tim@724: Template template = getTemplateByName(templateID); tim@724: return template != null; tim@724: } ingo@621: ingo@621: ingo@806: /** ingo@806: * Method which starts searching for meta information file and mapfile ingo@806: * generation. ingo@806: */ ingo@622: protected void generate() { ingo@622: File basedir = new File(getShapefileBaseDir()); ingo@622: List layers = new ArrayList(); ingo@622: searchMetaInformation(basedir, layers); ingo@622: writeMapfile(layers); ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@806: * Returns the VelocityEngine used for the template mechanism. ingo@806: * ingo@806: * @return the velocity engine. ingo@806: */ ingo@622: protected VelocityEngine getVelocityEngine() { ingo@622: if (velocityEngine == null) { ingo@622: velocityEngine = new VelocityEngine(); ingo@622: try { ingo@622: setupVelocity(velocityEngine); ingo@622: } ingo@622: catch (Exception e) { ingo@622: logger.error(e, e); ingo@622: return null; ingo@622: } ingo@622: } ingo@622: return velocityEngine; ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@806: * Initialize velocity. ingo@806: * ingo@806: * @param engine Velocity engine. ingo@806: * @throws Exception if an error occured while initializing velocity. ingo@806: */ ingo@622: protected void setupVelocity(VelocityEngine engine) ingo@622: throws Exception ingo@622: { ingo@624: engine.setProperty( ingo@622: "input.encoding", ingo@622: "UTF-8"); ingo@622: ingo@624: engine.setProperty( ingo@624: VelocityEngine.RUNTIME_LOG, ingo@624: getVelocityLogfile()); ingo@622: ingo@624: engine.setProperty( ingo@624: "resource.loader", ingo@624: "file"); ingo@622: ingo@624: engine.setProperty( ingo@624: "file.resource.loader.path", ingo@624: getTemplateBaseDir()); ingo@624: ingo@624: engine.init(); ingo@624: } ingo@624: ingo@624: ingo@806: /** ingo@806: * Returns the path specifying the logfile for velocity. ingo@806: * ingo@806: * @return Velocity logfile path. ingo@806: */ ingo@624: protected String getVelocityLogfile() { ingo@624: if (velocityLogfile == null) ingo@624: velocityLogfile = Config.getStringXPath(VELOCITY_LOGFILE); ingo@624: ingo@624: return velocityLogfile; ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@806: * Returns the base directory storing the templates. ingo@806: * ingo@806: * @return the template base directory. ingo@806: */ ingo@622: protected String getTemplateBaseDir() { ingo@625: if (templatePath == null) { ingo@624: templatePath = Config.getStringXPath(TEMPLATE_PATH); ingo@625: templatePath = Config.replaceConfigDir(templatePath); ingo@625: } ingo@624: ingo@624: return templatePath; ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@840: * Returns the URL for calling the MapServer. ingo@840: * ingo@840: * @return the url for calling the MapServer. ingo@840: */ ingo@840: protected String getMapServerUrl() { ingo@840: if (mapServerUrl == null) { ingo@840: mapServerUrl = Config.getStringXPath(MAPSERVER_URL); ingo@840: mapServerUrl = Config.replaceConfigDir(mapServerUrl); ingo@840: } ingo@840: ingo@840: return mapServerUrl; ingo@840: } ingo@840: ingo@840: ingo@840: /** ingo@806: * Returns a template specified by model. ingo@806: * ingo@806: * @param model The name of the template. ingo@806: * @return a template. ingo@806: */ ingo@622: protected Template getTemplateByName(String model) { ingo@622: if (model.indexOf(".vm") < 0) { ingo@622: model = model.concat(".vm"); ingo@622: } ingo@622: ingo@622: try { ingo@622: VelocityEngine engine = getVelocityEngine(); ingo@622: if (engine == null) { ingo@622: logger.error("Error while fetching VelocityEngine."); ingo@622: return null; ingo@622: } ingo@622: ingo@624: return engine.getTemplate(model); ingo@622: } ingo@622: catch (Exception e) { ingo@622: logger.warn(e, e); ingo@622: } ingo@622: ingo@622: return null; ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@806: * Returns the mapfile template. ingo@806: * ingo@806: * @return the mapfile template. ingo@806: * @throws Exception if an error occured while reading the configuration. ingo@806: */ ingo@622: protected Template getMapfileTemplate() ingo@622: throws Exception ingo@622: { ingo@622: String mapfileName = Config.getStringXPath(TEMPLATE_MAPFILE); ingo@622: return getTemplateByName(mapfileName); ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@806: * Returns the base directory storing the shapefiles. ingo@806: * ingo@806: * @return the shapefile base directory. ingo@806: */ ingo@622: protected String getShapefileBaseDir() { ingo@625: if (shapefileDirectory == null) { ingo@625: shapefileDirectory = Config.getStringXPath(SHAPEFILE_BASE_DIR); ingo@625: shapefileDirectory = Config.replaceConfigDir(shapefileDirectory); ingo@625: } ingo@625: ingo@625: return shapefileDirectory; ingo@625: } ingo@625: ingo@625: ingo@806: /** ingo@806: * Returns the mapfile. sascha@807: * ingo@806: * @return the mapfile. ingo@806: */ ingo@625: protected File getMapfile() { ingo@625: if (mapfile == null) { ingo@625: String tmp = Config.getStringXPath(MAPFILE_PATH); ingo@625: tmp = Config.replaceConfigDir(tmp); ingo@625: mapfile = new File(tmp); ingo@625: } ingo@625: ingo@625: return mapfile; ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@806: * Search for meta information file in file and store the layer ingo@806: * information in store if a meta file was found. sascha@807: * ingo@806: * @param file The root directory to be searched for meta files. ingo@806: * @param store A list storing LayerInfo objects. ingo@806: */ ingo@622: protected void searchMetaInformation(File file, List store) { ingo@622: if (file.isDirectory()) { ingo@622: File[] files = file.listFiles(); ingo@622: ingo@622: if (files != null && files.length != 0) { ingo@622: int size = files.length; ingo@622: for (File tmp: files) { ingo@622: searchMetaInformation(tmp, store); ingo@622: } ingo@622: } ingo@622: } ingo@622: else if (file.getName().equals(META_FILE_NAME)) { ingo@622: LayerInfo[] info = parseMeta(file); ingo@622: ingo@622: int infoSize = info.length; ingo@622: for (int j = 0; j < infoSize; j++) { ingo@622: if (info[j] != null) { ingo@622: store.add(info[j]); ingo@622: } ingo@622: } ingo@622: } ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@806: * Parses a meta information file and returns necessary information as ingo@806: * LayerInfo array. ingo@806: * ingo@806: * @param file Meta information file. ingo@806: * @return Array of LayerInfo objects. ingo@806: */ ingo@622: protected LayerInfo[] parseMeta(File file) { ingo@622: Document meta = XMLUtils.parseDocument(file); ingo@622: List layers = new ArrayList(); ingo@622: ingo@622: NodeList layerset = (NodeList) XMLUtils.xpath( ingo@622: meta, ingo@622: XPATH_LAYER, ingo@622: XPathConstants.NODESET, ingo@622: ArtifactNamespaceContext.INSTANCE); ingo@622: ingo@622: int size = layerset.getLength(); ingo@622: for (int i = 0; i < size; i++) { ingo@622: LayerInfo info = parseLayer(layerset.item(i)); ingo@622: ingo@622: if (info != null && !info.isEmpty() && !info.isBroken()) { ingo@622: layers.add(info); ingo@622: } ingo@622: else { ingo@622: logger.warn("Found broken LayerInfo object."); ingo@622: } ingo@622: } ingo@622: ingo@622: return (LayerInfo[]) layers.toArray(new LayerInfo[layers.size()]); ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@806: * Parses a node storing layer information and returns them. ingo@806: * ingo@806: * @param layer Node storing information about a layer. ingo@806: * @return a LayerInfo object. ingo@806: */ ingo@622: protected LayerInfo parseLayer(Node layer) { ingo@622: LayerInfo info = new LayerInfo(); ingo@622: ingo@622: String name = parseLayerAttr(layer, XPATH_LAYER_NAME); ingo@622: if (name != null && !name.equals("")) { ingo@622: info.setName(name); ingo@622: } ingo@622: ingo@730: String title = parseLayerAttr(layer, XPATH_LAYER_TITLE); ingo@730: if (title != null && !title.equals("")) { ingo@730: info.setTitle(title); ingo@730: } ingo@730: else { ingo@730: info.setTitle(name); ingo@730: } ingo@730: ingo@622: String model = parseLayerAttr(layer, XPATH_LAYER_MODEL); ingo@622: if (model != null && !model.equals("")) { ingo@622: info.setModel(model); ingo@622: } ingo@622: ingo@622: String type = parseLayerAttr(layer, XPATH_LAYER_TYPE); ingo@622: if (type != null && !type.equals("")) { ingo@622: info.setType(type); ingo@622: } ingo@622: ingo@622: String data = parseLayerAttr(layer, XPATH_LAYER_DATA); ingo@622: if (data != null && !data.equals("")) { ingo@622: info.setData(data); ingo@622: } ingo@622: ingo@622: String status = parseLayerAttr(layer, XPATH_LAYER_STATUS); ingo@622: if (status != null && !status.equals("")) { ingo@622: info.setStatus(status); ingo@622: } ingo@622: ingo@622: return info; ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@806: * Parses attributes in layer nodes. ingo@806: * ingo@806: * @param node A node containing layer information. ingo@806: * @param xpath XPath specifying an attribute. ingo@806: * @return Attribute as string. ingo@806: */ ingo@622: protected String parseLayerAttr(Node node, String xpath) { ingo@622: return (String) XMLUtils.xpath( ingo@622: node, ingo@622: xpath, ingo@622: XPathConstants.STRING, ingo@622: ArtifactNamespaceContext.INSTANCE); ingo@622: } ingo@622: ingo@622: ingo@806: /** ingo@806: * Creates a mapfile with the layer information stored in layers. sascha@807: * ingo@806: * @param layers Layer information. ingo@806: */ ingo@622: protected void writeMapfile(List layers) { ingo@622: String tmpMapName = "mapfile" + new Date().getTime(); ingo@622: ingo@622: int layersize = layers.size(); ingo@622: StringBuilder sb = new StringBuilder(); ingo@622: StringWriter sw = null; ingo@622: LayerInfo info = null; ingo@622: ingo@622: for (int i = 0; i < layersize; i++) { ingo@622: sw = new StringWriter(); ingo@622: info = (LayerInfo) layers.get(i); ingo@622: ingo@622: Template layerTemplate = getTemplateByName(info.getModel()); ingo@622: VelocityContext context = new VelocityContext(); ingo@622: context.put("info", info); ingo@622: ingo@622: try { ingo@622: layerTemplate.merge(context, sw); ingo@622: sb.append(sw.toString()); ingo@622: } ingo@622: catch (IOException ioe) { ingo@622: logger.warn("Error while filling layer template."); ingo@622: logger.warn(ioe, ioe); ingo@622: } ingo@622: } ingo@622: ingo@625: File map = getMapfile(); ingo@622: Writer writer = null; ingo@622: File tmp = null; ingo@622: ingo@622: try { ingo@625: tmp = new File(map.getParent(), tmpMapName); ingo@622: ingo@622: tmp.createNewFile(); ingo@622: writer = new FileWriter(tmp); ingo@622: ingo@622: VelocityContext context = new VelocityContext(); ingo@840: context.put("MAPSERVERURL", getMapServerUrl()); ingo@622: context.put("LAYERS", sb.toString()); ingo@622: ingo@622: Template mapTemplate = getMapfileTemplate(); ingo@622: if (mapTemplate == null) { ingo@622: logger.warn("No mapfile template found."); ingo@622: return; ingo@622: } ingo@622: ingo@622: mapTemplate.merge(context, writer); ingo@622: ingo@622: // we need to create a temporary mapfile first und rename it into ingo@622: // real mapfile because we don't run into race conditions on this ingo@622: // way. (iw) ingo@622: tmp.renameTo(map); ingo@622: } ingo@622: catch (FileNotFoundException fnfe) { ingo@622: logger.error(fnfe, fnfe); ingo@622: } ingo@622: catch (IOException ioe) { ingo@622: logger.error(ioe, ioe); ingo@622: } ingo@622: catch (Exception e) { ingo@622: logger.error(e, e); ingo@622: } ingo@622: finally { ingo@622: try { ingo@622: // close file writer ingo@622: if (writer != null) { ingo@622: writer.close(); ingo@622: } ingo@622: ingo@622: // remove temporary mapfile if an error occured and it still ingo@622: // exists ingo@622: if (tmp.exists()) { ingo@622: tmp.delete(); ingo@622: } ingo@622: } ingo@622: catch (IOException ioe) { ingo@622: logger.debug(ioe, ioe); ingo@622: } ingo@622: } ingo@621: } ingo@621: } sascha@798: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :