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 :