comparison gnv-artifacts/src/main/java/de/intevation/gnv/utils/MapfileGenerator.java @ 875:5e9efdda6894

merged gnv-artifacts/1.0
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:13:56 +0200
parents 47578d91c4f0
children f953c9a559d8
comparison
equal deleted inserted replaced
722:bb3ffe7d719e 875:5e9efdda6894
1 package de.intevation.gnv.utils;
2
3 import de.intevation.artifactdatabase.Config;
4 import de.intevation.artifactdatabase.XMLUtils;
5
6 import de.intevation.artifacts.ArtifactNamespaceContext;
7
8 import de.intevation.gnv.wms.LayerInfo;
9
10 import java.io.File;
11 import java.io.FileNotFoundException;
12 import java.io.FileWriter;
13 import java.io.IOException;
14 import java.io.StringWriter;
15 import java.io.Writer;
16
17 import java.util.ArrayList;
18 import java.util.Date;
19 import java.util.List;
20
21 import javax.xml.xpath.XPathConstants;
22
23 import org.apache.log4j.Logger;
24
25 import org.apache.velocity.Template;
26 import org.apache.velocity.VelocityContext;
27
28 import org.apache.velocity.app.VelocityEngine;
29
30 import org.w3c.dom.Document;
31 import org.w3c.dom.Node;
32 import org.w3c.dom.NodeList;
33
34 /**
35 * This class iterates over a bunch of directories, searches for meta
36 * information coresponding to shapefiles and creates a mapfile which is used by
37 * a <i>MapServer</i>.
38 *
39 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
40 */
41 public class MapfileGenerator
42 extends Thread
43 {
44 /**
45 * The configured template path.
46 */
47 public static final String TEMPLATE_PATH =
48 "/artifact-database/gnv/map-generator/templates/path/text()";
49
50 /**
51 * The configured template mapfile.
52 */
53 public static final String TEMPLATE_MAPFILE =
54 "/artifact-database/gnv/map-generator/templates/maptemplate/text()";
55
56 /**
57 * The configured mapfile path.
58 */
59 public static final String MAPFILE_PATH =
60 "/artifact-database/gnv/map-generator/mapfile/@path";
61
62 /**
63 * The configured shapefile base directory where the subdirectories for each
64 * artifact are stored.
65 */
66 public static final String SHAPEFILE_BASE_DIR =
67 "/artifact-database/gnv/shapefile-directory/@path";
68
69 /**
70 * The configured velocity logfile.
71 */
72 public static final String VELOCITY_LOGFILE =
73 "/artifact-database/velocity/logfile/@path";
74
75 /**
76 * The URL for calling the mapserver
77 */
78 public static final String MAPSERVER_URL =
79 "/artifact-database/mapserver/server/@path";
80
81 /**
82 * The name of the file storing meta information coresponding to shapefiles.
83 */
84 public static final String META_FILE_NAME = "meta.xml";
85
86 /**
87 * The XPath to layer nodes in the meta information xml file.
88 */
89 public static final String XPATH_LAYER = "/art:meta/art:layer";
90
91 public static final String XPATH_LAYER_NAME = "art:name";
92 public static final String XPATH_LAYER_TITLE = "art:title";
93 public static final String XPATH_LAYER_TYPE = "art:type";
94 public static final String XPATH_LAYER_DATA = "art:data";
95 public static final String XPATH_LAYER_STATUS = "art:status";
96 public static final String XPATH_LAYER_MODEL = "art:model";
97
98 protected static final long SLEEPTIME = 10 * 1000L; // 10 seconds
99
100 private static Logger logger = Logger.getLogger(MapfileGenerator.class);
101
102 private static MapfileGenerator instance;
103
104 private File mapfile;
105 private String mapServerUrl;
106 private String shapefileDirectory;
107 private String templatePath;
108 private String velocityLogfile;
109
110 private VelocityEngine velocityEngine;
111 private boolean lock[];
112
113
114
115 private MapfileGenerator() {
116 lock = new boolean[1];
117 }
118
119
120 /**
121 * A main method which can be used to create a mapfile without a running
122 * artifact server.<br>
123 * <b>NOTE:</b> This method is not implemented yet!
124 *
125 * @param args Arguments.
126 */
127 public static void main(String[] args) {
128 // TODO IMPLEMENT ME
129 }
130
131
132 /**
133 * Returns the instance of this generator.
134 *
135 * @return the instance.
136 */
137 public static synchronized MapfileGenerator getInstance() {
138 if (instance == null) {
139 instance = new MapfileGenerator();
140 instance.setDaemon(true);
141 instance.start();
142 }
143
144 return instance;
145 }
146
147
148 /**
149 * Triggers the mapfile generation process.
150 */
151 public void update() {
152 synchronized (lock) {
153 logger.debug("update");
154 lock[0] = true;
155 lock.notify();
156 }
157 }
158
159
160 /**
161 * Thread to generate a mapfile.
162 */
163 @Override
164 public void run() {
165 logger.debug("Start MapfileGenerator thread...");
166 try {
167 for (;;) {
168 synchronized (lock) {
169 while (!lock[0]) {
170 lock.wait(SLEEPTIME);
171 }
172 lock[0] = false;
173 }
174
175 logger.debug("Start sync process now...");
176 generate();
177 }
178 }
179 catch (InterruptedException ie) {
180 logger.debug("MapfileGenerator thread got an interrupt.");
181 }
182 finally {
183 logger.debug("THREAD END");
184 }
185 }
186
187 /**
188 * Method to check the existance of a template file.
189 *
190 * @param templateID The name of a template.
191 * @return true, of the template exists - otherwise false.
192 */
193 public boolean templateExists(String templateID){
194 Template template = getTemplateByName(templateID);
195 return template != null;
196 }
197
198
199 /**
200 * Method which starts searching for meta information file and mapfile
201 * generation.
202 */
203 protected void generate() {
204 File basedir = new File(getShapefileBaseDir());
205 List layers = new ArrayList();
206 searchMetaInformation(basedir, layers);
207 writeMapfile(layers);
208 }
209
210
211 /**
212 * Returns the VelocityEngine used for the template mechanism.
213 *
214 * @return the velocity engine.
215 */
216 protected VelocityEngine getVelocityEngine() {
217 if (velocityEngine == null) {
218 velocityEngine = new VelocityEngine();
219 try {
220 setupVelocity(velocityEngine);
221 }
222 catch (Exception e) {
223 logger.error(e, e);
224 return null;
225 }
226 }
227 return velocityEngine;
228 }
229
230
231 /**
232 * Initialize velocity.
233 *
234 * @param engine Velocity engine.
235 * @throws Exception if an error occured while initializing velocity.
236 */
237 protected void setupVelocity(VelocityEngine engine)
238 throws Exception
239 {
240 engine.setProperty(
241 "input.encoding",
242 "UTF-8");
243
244 engine.setProperty(
245 VelocityEngine.RUNTIME_LOG,
246 getVelocityLogfile());
247
248 engine.setProperty(
249 "resource.loader",
250 "file");
251
252 engine.setProperty(
253 "file.resource.loader.path",
254 getTemplateBaseDir());
255
256 engine.init();
257 }
258
259
260 /**
261 * Returns the path specifying the logfile for velocity.
262 *
263 * @return Velocity logfile path.
264 */
265 protected String getVelocityLogfile() {
266 if (velocityLogfile == null)
267 velocityLogfile = Config.getStringXPath(VELOCITY_LOGFILE);
268
269 return velocityLogfile;
270 }
271
272
273 /**
274 * Returns the base directory storing the templates.
275 *
276 * @return the template base directory.
277 */
278 protected String getTemplateBaseDir() {
279 if (templatePath == null) {
280 templatePath = Config.getStringXPath(TEMPLATE_PATH);
281 templatePath = Config.replaceConfigDir(templatePath);
282 }
283
284 return templatePath;
285 }
286
287
288 /**
289 * Returns the URL for calling the MapServer.
290 *
291 * @return the url for calling the MapServer.
292 */
293 protected String getMapServerUrl() {
294 if (mapServerUrl == null) {
295 mapServerUrl = Config.getStringXPath(MAPSERVER_URL);
296 mapServerUrl = Config.replaceConfigDir(mapServerUrl);
297 }
298
299 return mapServerUrl;
300 }
301
302
303 /**
304 * Returns a template specified by <i>model</i>.
305 *
306 * @param model The name of the template.
307 * @return a template.
308 */
309 protected Template getTemplateByName(String model) {
310 if (model.indexOf(".vm") < 0) {
311 model = model.concat(".vm");
312 }
313
314 try {
315 VelocityEngine engine = getVelocityEngine();
316 if (engine == null) {
317 logger.error("Error while fetching VelocityEngine.");
318 return null;
319 }
320
321 return engine.getTemplate(model);
322 }
323 catch (Exception e) {
324 logger.warn(e, e);
325 }
326
327 return null;
328 }
329
330
331 /**
332 * Returns the mapfile template.
333 *
334 * @return the mapfile template.
335 * @throws Exception if an error occured while reading the configuration.
336 */
337 protected Template getMapfileTemplate()
338 throws Exception
339 {
340 String mapfileName = Config.getStringXPath(TEMPLATE_MAPFILE);
341 return getTemplateByName(mapfileName);
342 }
343
344
345 /**
346 * Returns the base directory storing the shapefiles.
347 *
348 * @return the shapefile base directory.
349 */
350 protected String getShapefileBaseDir() {
351 if (shapefileDirectory == null) {
352 shapefileDirectory = Config.getStringXPath(SHAPEFILE_BASE_DIR);
353 shapefileDirectory = Config.replaceConfigDir(shapefileDirectory);
354 }
355
356 return shapefileDirectory;
357 }
358
359
360 /**
361 * Returns the mapfile.
362 *
363 * @return the mapfile.
364 */
365 protected File getMapfile() {
366 if (mapfile == null) {
367 String tmp = Config.getStringXPath(MAPFILE_PATH);
368 tmp = Config.replaceConfigDir(tmp);
369 mapfile = new File(tmp);
370 }
371
372 return mapfile;
373 }
374
375
376 /**
377 * Search for meta information file in <i>file</i> and store the layer
378 * information in <i>store</i> if a meta file was found.
379 *
380 * @param file The root directory to be searched for meta files.
381 * @param store A list storing <code>LayerInfo</code> objects.
382 */
383 protected void searchMetaInformation(File file, List store) {
384 if (file.isDirectory()) {
385 File[] files = file.listFiles();
386
387 if (files != null && files.length != 0) {
388 int size = files.length;
389 for (File tmp: files) {
390 searchMetaInformation(tmp, store);
391 }
392 }
393 }
394 else if (file.getName().equals(META_FILE_NAME)) {
395 LayerInfo[] info = parseMeta(file);
396
397 int infoSize = info.length;
398 for (int j = 0; j < infoSize; j++) {
399 if (info[j] != null) {
400 store.add(info[j]);
401 }
402 }
403 }
404 }
405
406
407 /**
408 * Parses a meta information file and returns necessary information as
409 * <code>LayerInfo</code> array.
410 *
411 * @param file Meta information file.
412 * @return Array of LayerInfo objects.
413 */
414 protected LayerInfo[] parseMeta(File file) {
415 Document meta = XMLUtils.parseDocument(file);
416 List layers = new ArrayList();
417
418 NodeList layerset = (NodeList) XMLUtils.xpath(
419 meta,
420 XPATH_LAYER,
421 XPathConstants.NODESET,
422 ArtifactNamespaceContext.INSTANCE);
423
424 int size = layerset.getLength();
425 for (int i = 0; i < size; i++) {
426 LayerInfo info = parseLayer(layerset.item(i));
427
428 if (info != null && !info.isEmpty() && !info.isBroken()) {
429 layers.add(info);
430 }
431 else {
432 logger.warn("Found broken LayerInfo object.");
433 }
434 }
435
436 return (LayerInfo[]) layers.toArray(new LayerInfo[layers.size()]);
437 }
438
439
440 /**
441 * Parses a node storing layer information and returns them.
442 *
443 * @param layer Node storing information about a layer.
444 * @return a LayerInfo object.
445 */
446 protected LayerInfo parseLayer(Node layer) {
447 LayerInfo info = new LayerInfo();
448
449 String name = parseLayerAttr(layer, XPATH_LAYER_NAME);
450 if (name != null && !name.equals("")) {
451 info.setName(name);
452 }
453
454 String title = parseLayerAttr(layer, XPATH_LAYER_TITLE);
455 if (title != null && !title.equals("")) {
456 info.setTitle(title);
457 }
458 else {
459 info.setTitle(name);
460 }
461
462 String model = parseLayerAttr(layer, XPATH_LAYER_MODEL);
463 if (model != null && !model.equals("")) {
464 info.setModel(model);
465 }
466
467 String type = parseLayerAttr(layer, XPATH_LAYER_TYPE);
468 if (type != null && !type.equals("")) {
469 info.setType(type);
470 }
471
472 String data = parseLayerAttr(layer, XPATH_LAYER_DATA);
473 if (data != null && !data.equals("")) {
474 info.setData(data);
475 }
476
477 String status = parseLayerAttr(layer, XPATH_LAYER_STATUS);
478 if (status != null && !status.equals("")) {
479 info.setStatus(status);
480 }
481
482 return info;
483 }
484
485
486 /**
487 * Parses attributes in layer nodes.
488 *
489 * @param node A node containing layer information.
490 * @param xpath XPath specifying an attribute.
491 * @return Attribute as string.
492 */
493 protected String parseLayerAttr(Node node, String xpath) {
494 return (String) XMLUtils.xpath(
495 node,
496 xpath,
497 XPathConstants.STRING,
498 ArtifactNamespaceContext.INSTANCE);
499 }
500
501
502 /**
503 * Creates a mapfile with the layer information stored in <i>layers</i>.
504 *
505 * @param layers Layer information.
506 */
507 protected void writeMapfile(List layers) {
508 String tmpMapName = "mapfile" + new Date().getTime();
509
510 int layersize = layers.size();
511 StringBuilder sb = new StringBuilder();
512 StringWriter sw = null;
513 LayerInfo info = null;
514
515 for (int i = 0; i < layersize; i++) {
516 sw = new StringWriter();
517 info = (LayerInfo) layers.get(i);
518
519 Template layerTemplate = getTemplateByName(info.getModel());
520 VelocityContext context = new VelocityContext();
521 context.put("info", info);
522
523 try {
524 layerTemplate.merge(context, sw);
525 sb.append(sw.toString());
526 }
527 catch (IOException ioe) {
528 logger.warn("Error while filling layer template.");
529 logger.warn(ioe, ioe);
530 }
531 }
532
533 File map = getMapfile();
534 Writer writer = null;
535 File tmp = null;
536
537 try {
538 tmp = new File(map.getParent(), tmpMapName);
539
540 tmp.createNewFile();
541 writer = new FileWriter(tmp);
542
543 VelocityContext context = new VelocityContext();
544 context.put("MAPSERVERURL", getMapServerUrl());
545 context.put("LAYERS", sb.toString());
546
547 Template mapTemplate = getMapfileTemplate();
548 if (mapTemplate == null) {
549 logger.warn("No mapfile template found.");
550 return;
551 }
552
553 mapTemplate.merge(context, writer);
554
555 // we need to create a temporary mapfile first und rename it into
556 // real mapfile because we don't run into race conditions on this
557 // way. (iw)
558 tmp.renameTo(map);
559 }
560 catch (FileNotFoundException fnfe) {
561 logger.error(fnfe, fnfe);
562 }
563 catch (IOException ioe) {
564 logger.error(ioe, ioe);
565 }
566 catch (Exception e) {
567 logger.error(e, e);
568 }
569 finally {
570 try {
571 // close file writer
572 if (writer != null) {
573 writer.close();
574 }
575
576 // remove temporary mapfile if an error occured and it still
577 // exists
578 if (tmp.exists()) {
579 tmp.delete();
580 }
581 }
582 catch (IOException ioe) {
583 logger.debug(ioe, ioe);
584 }
585 }
586 }
587 }
588 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org