Mercurial > dive4elements > gnv-client
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 : |