ingo@621: package de.intevation.gnv.utils;
ingo@621: 
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;
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;
ingo@622: import org.apache.velocity.Template;
ingo@622: import org.apache.velocity.VelocityContext;
ingo@622: import org.apache.velocity.app.VelocityEngine;
ingo@622: import org.w3c.dom.Document;
ingo@622: import org.w3c.dom.Node;
ingo@622: import org.w3c.dom.NodeList;
ingo@621: 
tim@724: import de.intevation.artifactdatabase.Config;
tim@724: import de.intevation.artifactdatabase.XMLUtils;
tim@724: import de.intevation.artifacts.ArtifactNamespaceContext;
tim@724: import de.intevation.gnv.wms.LayerInfo;
tim@724: 
ingo@625: 
ingo@621: /**
ingo@621:  * @author Ingo Weinzierl (ingo.weinzierl@intevation.de)
ingo@621:  */
ingo@621: public class MapfileGenerator
ingo@621: extends Thread
ingo@621: {
ingo@622:     public static final String TEMPLATE_PATH =
ingo@625:         "/artifact-database/gnv/map-generator/templates/path/text()";
ingo@622: 
ingo@622:     public static final String TEMPLATE_MAPFILE =
ingo@625:         "/artifact-database/gnv/map-generator/templates/maptemplate/text()";
ingo@621: 
ingo@621:     public static final String MAPFILE_PATH =
ingo@625:         "/artifact-database/gnv/map-generator/mapfile/@path";
ingo@622: 
ingo@622:     public static final String SHAPEFILE_BASE_DIR =
ingo@625:         "/artifact-database/gnv/shapefile-directory/@path";
ingo@622: 
ingo@624:     public static final String VELOCITY_LOGFILE =
ingo@624:         "/artifact-database/velocity/logfile/@path";
ingo@624: 
ingo@622:     public static final String META_FILE_NAME = "meta.xml";
ingo@622: 
ingo@622:     public static final String XPATH_LAYER        = "/art:meta/art:layer";
ingo@622:     public static final String XPATH_LAYER_NAME   = "art:name";
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@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@621:     public static void main(String[] args) {
ingo@622:         // TODO IMPLEMENT ME
ingo@621:     }
ingo@621: 
ingo@621: 
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@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@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:     }
tim@724:     
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@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@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@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@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@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@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@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@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@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@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@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@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@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@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@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@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: }
ingo@621: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: