Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java @ 3786:4adc35aa655c
merged flys-artifacts/2.9.1
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:47 +0200 |
parents | a5f65e8983be |
children | 3aec5a42696a |
comparison
equal
deleted
inserted
replaced
3719:e82acd5c86f7 | 3786:4adc35aa655c |
---|---|
1 package de.intevation.flys.collections; | |
2 | |
3 import java.io.IOException; | |
4 import java.io.OutputStream; | |
5 import java.util.ArrayList; | |
6 import java.util.HashMap; | |
7 import java.util.List; | |
8 import java.util.Map; | |
9 import java.util.Set; | |
10 | |
11 import javax.xml.xpath.XPathConstants; | |
12 | |
13 import org.apache.log4j.Logger; | |
14 import org.w3c.dom.Document; | |
15 import org.w3c.dom.Element; | |
16 import org.w3c.dom.Node; | |
17 import org.w3c.dom.NodeList; | |
18 | |
19 import de.intevation.artifactdatabase.Backend; | |
20 import de.intevation.artifactdatabase.Backend.PersistentArtifact; | |
21 import de.intevation.artifactdatabase.DefaultArtifactCollection; | |
22 import de.intevation.artifactdatabase.state.Output; | |
23 import de.intevation.artifactdatabase.state.Settings; | |
24 import de.intevation.artifactdatabase.state.StateEngine; | |
25 import de.intevation.artifacts.Artifact; | |
26 import de.intevation.artifacts.ArtifactDatabase; | |
27 import de.intevation.artifacts.ArtifactDatabaseException; | |
28 import de.intevation.artifacts.ArtifactNamespaceContext; | |
29 import de.intevation.artifacts.CallContext; | |
30 import de.intevation.artifacts.CallMeta; | |
31 import de.intevation.artifacts.common.utils.XMLUtils; | |
32 import de.intevation.flys.artifacts.FLYSArtifact; | |
33 import de.intevation.flys.artifacts.context.FLYSContext; | |
34 import de.intevation.flys.exports.OutGenerator; | |
35 import de.intevation.flys.exports.OutputHelper; | |
36 import de.intevation.flys.utils.FLYSUtils; | |
37 | |
38 /** | |
39 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> | |
40 */ | |
41 public class FLYSArtifactCollection extends DefaultArtifactCollection { | |
42 /** The logger used in this class. */ | |
43 private static Logger log = Logger.getLogger(FLYSArtifactCollection.class); | |
44 | |
45 /** Constant XPath that points to the outputmodes of an artifact. */ | |
46 public static final String XPATH_ARTIFACT_OUTPUTMODES = | |
47 "/art:result/art:outputmodes"; | |
48 | |
49 public static final String XPATH_ARTIFACT_STATE_DATA = | |
50 "/art:result/art:ui/art:static/art:state/art:data"; | |
51 | |
52 public static final String XPATH_COLLECTION_ITEMS = | |
53 "/art:result/art:artifact-collection/art:collection-item"; | |
54 | |
55 public static final String XPATH_OUT_NAME = "/art:action/@art:name"; | |
56 | |
57 public static final String XPATH_OUT_TYPE = "/art:action/@art:type"; | |
58 | |
59 /** Xpath to master artifacts uuid. */ | |
60 public static final String XPATH_MASTER_UUID = | |
61 "/art:artifact-collection/art:artifact/@art:uuid"; | |
62 | |
63 public static final String XPATH_LOADED_RECOMMENDATIONS = | |
64 "/art:attribute/art:loaded-recommendations"; | |
65 | |
66 | |
67 /** | |
68 * Return description Document for this collection. | |
69 */ | |
70 @Override | |
71 public Document describe(CallContext context) { | |
72 log.debug("FLYSArtifactCollection.describe: " + identifier); | |
73 | |
74 CollectionDescriptionHelper helper = new CollectionDescriptionHelper( | |
75 getName(), identifier(), getCreationTime(), getTTL(), | |
76 context); | |
77 | |
78 ArtifactDatabase db = context.getDatabase(); | |
79 | |
80 Document oldAttrs = getAttribute(); | |
81 AttributeParser parser = new AttributeParser(oldAttrs); | |
82 | |
83 try { | |
84 String[] aUUIDs = getArtifactUUIDs(context); | |
85 | |
86 oldAttrs = removeAttributes(oldAttrs, context); | |
87 parser = new AttributeParser(oldAttrs); | |
88 | |
89 CollectionAttribute newAttr = mergeAttributes( | |
90 db, context, parser, aUUIDs); | |
91 | |
92 if (checkOutputSettings(newAttr, context)) { | |
93 saveCollectionAttribute(db, context, newAttr); | |
94 } | |
95 | |
96 helper.setAttribute(newAttr); | |
97 | |
98 if (aUUIDs != null) { | |
99 for (String uuid: aUUIDs) { | |
100 helper.addArtifact(uuid); | |
101 } | |
102 } | |
103 } | |
104 catch (ArtifactDatabaseException ade) { | |
105 log.error("Error while merging attribute documents.", ade); | |
106 | |
107 helper.setAttribute(parser.getCollectionAttribute()); | |
108 } | |
109 | |
110 return helper.toXML(); | |
111 } | |
112 | |
113 | |
114 /** | |
115 * Merge the current art:outputs nodes with the the outputs provided by the | |
116 * artifacts in the Collection. | |
117 * | |
118 * @param uuids Artifact uuids. | |
119 */ | |
120 protected CollectionAttribute mergeAttributes( | |
121 ArtifactDatabase db, | |
122 CallContext context, | |
123 AttributeParser oldParser, | |
124 String[] uuids | |
125 ) { | |
126 CollectionAttribute cAttribute = | |
127 buildOutAttributes(db, context, oldParser, uuids); | |
128 | |
129 if (cAttribute == null) { | |
130 log.warn("mergeAttributes: cAttribute == null"); | |
131 return null; | |
132 } | |
133 | |
134 cAttribute.setLoadedRecommendations( | |
135 getLoadedRecommendations(oldParser.getAttributeDocument())); | |
136 | |
137 saveCollectionAttribute(db, context, cAttribute); | |
138 | |
139 return cAttribute; | |
140 } | |
141 | |
142 | |
143 protected Document removeAttributes(Document attrs, CallContext context) { | |
144 Node outs = (Node) XMLUtils.xpath( | |
145 attrs, | |
146 "/art:attribute/art:outputs", | |
147 XPathConstants.NODE, | |
148 ArtifactNamespaceContext.INSTANCE); | |
149 | |
150 NodeList nodes = (NodeList) XMLUtils.xpath( | |
151 attrs, | |
152 "/art:attribute/art:outputs/art:output", | |
153 XPathConstants.NODESET, | |
154 ArtifactNamespaceContext.INSTANCE); | |
155 | |
156 if (nodes != null) { | |
157 for (int i = 0; i < nodes.getLength(); i++) { | |
158 Element e = (Element)nodes.item(i); | |
159 if(!outputExists(e.getAttribute("name"), context)) { | |
160 outs.removeChild(e); | |
161 } | |
162 } | |
163 } | |
164 return attrs; | |
165 } | |
166 | |
167 | |
168 /** | |
169 * True if current MasterArtifact has given output. | |
170 * @param name Name of the output of interest. | |
171 * @param context current context | |
172 * @return true if current master artifact has given output. | |
173 */ | |
174 protected boolean outputExists(String name, CallContext context) { | |
175 FLYSArtifact master = getMasterArtifact(context); | |
176 List<Output> outList = master.getOutputs(context); | |
177 | |
178 for (Output o : outList) { | |
179 if (name.equals(o.getName())) { | |
180 return true; | |
181 } | |
182 } | |
183 return false; | |
184 } | |
185 | |
186 /** | |
187 * @param db The ArtifactDatabase which is required to save the attribute | |
188 * into. | |
189 * @param attribute The CollectionAttribute that should be stored in the | |
190 * database. | |
191 * | |
192 * @return true, if the transaction was successful, otherwise false. | |
193 */ | |
194 protected boolean saveCollectionAttribute( | |
195 ArtifactDatabase db, | |
196 CallContext context, | |
197 CollectionAttribute attribute | |
198 ) { | |
199 log.info("Save new CollectionAttribute into database."); | |
200 | |
201 Document doc = attribute.toXML(); | |
202 | |
203 try { | |
204 // Save the merged document into database. | |
205 db.setCollectionAttribute(identifier(), context.getMeta(), doc); | |
206 | |
207 log.info("Saving CollectionAttribute was successful."); | |
208 | |
209 return true; | |
210 } | |
211 catch (ArtifactDatabaseException adb) { | |
212 log.error(adb, adb); | |
213 } | |
214 | |
215 return false; | |
216 } | |
217 | |
218 | |
219 /** | |
220 * Merge the recommendations which have already been loaded from the old | |
221 * attribute document into the new attribute document. This is necessary, | |
222 * because mergeAttributes() only merges the art:outputs nodes - all | |
223 * other nodes are skipped. | |
224 */ | |
225 protected Node getLoadedRecommendations(Document oldAttrs) { | |
226 Element loadedRecoms = (Element) XMLUtils.xpath( | |
227 oldAttrs, | |
228 XPATH_LOADED_RECOMMENDATIONS, | |
229 XPathConstants.NODE, | |
230 ArtifactNamespaceContext.INSTANCE); | |
231 | |
232 return loadedRecoms; | |
233 } | |
234 | |
235 | |
236 /** | |
237 * Evaluates the Output settings. If an Output has no Settings set, the | |
238 * relevant OutGenerator is used to initialize a default Settings object. | |
239 * | |
240 * @param attribute The CollectionAttribute. | |
241 * @param cc The CallContext. | |
242 * | |
243 * @return true, if the CollectionAttribute was modified, otherwise false. | |
244 */ | |
245 protected boolean checkOutputSettings( | |
246 CollectionAttribute attribute, | |
247 CallContext cc | |
248 ) { | |
249 boolean modified = false; | |
250 | |
251 Map<String, Output> outputMap = attribute != null | |
252 ? attribute.getOutputs() | |
253 : null; | |
254 | |
255 if (outputMap == null || outputMap.isEmpty()) { | |
256 log.debug("No Output Settings check necessary."); | |
257 return modified; | |
258 } | |
259 | |
260 Set<Map.Entry<String, Output>> entries = outputMap.entrySet(); | |
261 | |
262 for (Map.Entry<String, Output> entry: entries) { | |
263 String outName = entry.getKey(); | |
264 Output output = entry.getValue(); | |
265 | |
266 if (outName.equals("sq_overview")) { | |
267 continue; | |
268 } | |
269 Settings settings = output.getSettings(); | |
270 | |
271 if (settings == null) { | |
272 log.debug("No Settings set for Output '" + outName + "'."); | |
273 output.setSettings( | |
274 createInitialOutputSettings(cc, attribute, outName)); | |
275 | |
276 modified = true; | |
277 } | |
278 } | |
279 | |
280 return modified; | |
281 } | |
282 | |
283 | |
284 /** | |
285 * This method uses the the OutGenerator for the specified Output | |
286 * <i>out</i> to create an initial Settings object. | |
287 * | |
288 * @param cc The CallContext object. | |
289 * @param attr The CollectionAttribute. | |
290 * @param out The name of the output. | |
291 * | |
292 * @return a default Settings object for the specified Output. | |
293 */ | |
294 protected Settings createInitialOutputSettings( | |
295 CallContext cc, | |
296 CollectionAttribute attr, | |
297 String out | |
298 ) { | |
299 OutGenerator outGen = FLYSContext.getOutGenerator(cc, out, null); | |
300 | |
301 if (outGen == null) { | |
302 return null; | |
303 } | |
304 | |
305 // XXX NOTE: the outGen is not able to process its generate() operation, | |
306 // because it has no OutputStream set! | |
307 outGen.init(XMLUtils.newDocument(), null, cc); | |
308 prepareMasterArtifact(outGen, cc); | |
309 | |
310 try { | |
311 Document outAttr = getAttribute(cc, attr, out); | |
312 OutputHelper helper = new OutputHelper(identifier()); | |
313 helper.doOut(outGen, out, out, outAttr, cc); | |
314 } | |
315 catch (ArtifactDatabaseException adbe) { | |
316 log.error(adbe, adbe); | |
317 } | |
318 catch (IOException ioe) { | |
319 log.error(ioe, ioe); | |
320 } | |
321 | |
322 return outGen.getSettings(); | |
323 } | |
324 | |
325 | |
326 @Override | |
327 public void out( | |
328 String type, | |
329 Document format, | |
330 OutputStream out, | |
331 CallContext context) | |
332 throws IOException | |
333 { | |
334 boolean debug = log.isDebugEnabled(); | |
335 | |
336 long reqBegin = System.currentTimeMillis(); | |
337 | |
338 if (debug) { | |
339 log.debug("FLYSArtifactCollection.out"); | |
340 } | |
341 | |
342 String name = XMLUtils.xpathString( | |
343 format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE); | |
344 | |
345 String subtype = XMLUtils.xpathString( | |
346 format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE); | |
347 | |
348 if (debug) { | |
349 log.debug("-> Output name = " + name); | |
350 log.debug("-> Output type = " + type); | |
351 log.debug("-> Output subtype = " + subtype); | |
352 } | |
353 | |
354 OutGenerator generator = null; | |
355 if (type != null | |
356 && type.length() > 0 | |
357 && type.indexOf("chartinfo") > 0) | |
358 { | |
359 generator = FLYSContext.getOutGenerator(context, type, subtype); | |
360 } | |
361 else { | |
362 generator = FLYSContext.getOutGenerator(context, name, subtype); | |
363 } | |
364 | |
365 if (generator == null) { | |
366 log.error("There is no generator specified for output: " + name); | |
367 // TODO Throw an exception. | |
368 | |
369 return; | |
370 } | |
371 | |
372 Document oldAttrs = getAttribute(); | |
373 AttributeParser parser = new AttributeParser(oldAttrs); | |
374 CollectionAttribute cAttr = parser.getCollectionAttribute(); | |
375 | |
376 Output output = cAttr.getOutput(name); | |
377 Settings settings = output.getSettings(); | |
378 | |
379 generator.init(format, out, context); | |
380 generator.setSettings(settings); | |
381 generator.setCollection(this); | |
382 prepareMasterArtifact(generator, context); | |
383 | |
384 try { | |
385 Document attr = getAttribute(context, cAttr, name); | |
386 OutputHelper helper = new OutputHelper(identifier()); | |
387 if (name.equals("sq_overview")) { | |
388 helper.doOut(generator, name, subtype, format, context); | |
389 } | |
390 helper.doOut(generator, name, subtype, attr, context); | |
391 generator.generate(); | |
392 } | |
393 catch (ArtifactDatabaseException adbe) { | |
394 log.error(adbe, adbe); | |
395 } | |
396 | |
397 if (debug) { | |
398 long duration = System.currentTimeMillis() -reqBegin; | |
399 log.info("Processing out(" + name + ") took " + duration + " ms."); | |
400 } | |
401 } | |
402 | |
403 | |
404 /** | |
405 * Sets the master Artifact at the given <i>generator</i>. | |
406 * | |
407 * @param generator The generator that gets a master Artifact. | |
408 * @param cc The CallContext. | |
409 */ | |
410 protected void prepareMasterArtifact(OutGenerator generator, CallContext cc | |
411 ) { | |
412 // Get master artifact. | |
413 FLYSArtifact master = getMasterArtifact(cc); | |
414 if (master != null) { | |
415 log.debug("Set master Artifact to uuid: " + master.identifier()); | |
416 generator.setMasterArtifact(master); | |
417 } | |
418 else { | |
419 log.warn("Could not set master artifact."); | |
420 } | |
421 } | |
422 | |
423 | |
424 /** | |
425 * @return masterartifact or null if exception/not found. | |
426 */ | |
427 protected FLYSArtifact getMasterArtifact(CallContext context) | |
428 { | |
429 try { | |
430 ArtifactDatabase db = context.getDatabase(); | |
431 CallMeta callMeta = context.getMeta(); | |
432 Document document = db.getCollectionsMasterArtifact( | |
433 identifier(), callMeta); | |
434 | |
435 String masterUUID = XMLUtils.xpathString( | |
436 document, XPATH_MASTER_UUID, ArtifactNamespaceContext.INSTANCE); | |
437 FLYSArtifact masterArtifact = | |
438 (FLYSArtifact) getArtifact(masterUUID, context); | |
439 return masterArtifact; | |
440 } | |
441 catch (ArtifactDatabaseException ade) { | |
442 log.error(ade, ade); | |
443 } | |
444 return null; | |
445 } | |
446 | |
447 | |
448 /** | |
449 * Return merged output document. | |
450 * @param uuids List of artifact uuids. | |
451 */ | |
452 protected CollectionAttribute buildOutAttributes( | |
453 ArtifactDatabase db, | |
454 CallContext context, | |
455 AttributeParser aParser, | |
456 String[] uuids) | |
457 { | |
458 FLYSContext flysContext = FLYSUtils.getFlysContext(context); | |
459 StateEngine engine = (StateEngine) flysContext.get( | |
460 FLYSContext.STATE_ENGINE_KEY); | |
461 | |
462 if (engine == null) { | |
463 log.error("buildOutAttributes: engine == null"); | |
464 return null; | |
465 } | |
466 | |
467 FLYSArtifact masterArtifact = getMasterArtifact(context); | |
468 | |
469 if (masterArtifact == null) { | |
470 log.debug("buildOutAttributes: masterArtifact == null"); | |
471 return null; | |
472 } | |
473 | |
474 OutputParser oParser = new OutputParser(db, context); | |
475 | |
476 if (uuids != null) { | |
477 for (String uuid: uuids) { | |
478 try { | |
479 oParser.parse(uuid); | |
480 } | |
481 catch (ArtifactDatabaseException ade) { | |
482 log.warn(ade, ade); | |
483 } | |
484 } | |
485 } | |
486 | |
487 aParser.parse(); | |
488 | |
489 AttributeWriter aWriter = new AttributeWriter( | |
490 db, | |
491 aParser.getCollectionAttribute(), | |
492 aParser.getOuts(), | |
493 aParser.getFacets(), | |
494 oParser.getOuts(), | |
495 oParser.getFacets(), | |
496 engine.getCompatibleFacets(masterArtifact.getStateHistoryIds()) | |
497 ); | |
498 return aWriter.write(); | |
499 } | |
500 | |
501 | |
502 /** | |
503 * Returns the "attribute" (part of description document) for a specific | |
504 * output type. | |
505 * | |
506 * @param context The CallContext object. | |
507 * @param cAttr The CollectionAttribute. | |
508 * @param output The name of the desired output type. | |
509 * | |
510 * @return the attribute for the desired output type. | |
511 */ | |
512 protected Document getAttribute( | |
513 CallContext context, | |
514 CollectionAttribute cAttr, | |
515 String output) | |
516 throws ArtifactDatabaseException | |
517 { | |
518 Document attr = cAttr.toXML(); | |
519 | |
520 Map<String, String> vars = new HashMap<String, String>(); | |
521 vars.put("output", output); | |
522 | |
523 Node out = (Node) XMLUtils.xpath( | |
524 attr, | |
525 "art:attribute/art:outputs/art:output[@name=$output]", | |
526 XPathConstants.NODE, | |
527 ArtifactNamespaceContext.INSTANCE, | |
528 vars); | |
529 | |
530 | |
531 if (out != null) { | |
532 Document o = XMLUtils.newDocument(); | |
533 | |
534 o.appendChild(o.importNode(out, true)); | |
535 | |
536 return o; | |
537 } | |
538 | |
539 return null; | |
540 } | |
541 | |
542 | |
543 /** | |
544 * This method returns the list of artifact UUIDs that this collections | |
545 * contains. | |
546 * | |
547 * @param context The CallContext that is necessary to get information about | |
548 * the ArtifactDatabase. | |
549 * | |
550 * @return a list of uuids. | |
551 */ | |
552 protected String[] getArtifactUUIDs(CallContext context) | |
553 throws ArtifactDatabaseException | |
554 { | |
555 log.debug("FLYSArtifactCollection.getArtifactUUIDs"); | |
556 | |
557 ArtifactDatabase db = context.getDatabase(); | |
558 CallMeta meta = context.getMeta(); | |
559 | |
560 Document itemList = db.listCollectionArtifacts(identifier(), meta); | |
561 NodeList items = (NodeList) XMLUtils.xpath( | |
562 itemList, | |
563 XPATH_COLLECTION_ITEMS, | |
564 XPathConstants.NODESET, | |
565 ArtifactNamespaceContext.INSTANCE); | |
566 | |
567 if (items == null || items.getLength() == 0) { | |
568 log.debug("No artifacts found in this collection."); | |
569 return null; | |
570 } | |
571 | |
572 int num = items.getLength(); | |
573 | |
574 List<String> uuids = new ArrayList<String>(num); | |
575 | |
576 for (int i = 0; i < num; i++) { | |
577 String uuid = XMLUtils.xpathString( | |
578 items.item(i), | |
579 "@art:uuid", | |
580 ArtifactNamespaceContext.INSTANCE); | |
581 | |
582 if (uuid != null && uuid.trim().length() != 0) { | |
583 uuids.add(uuid); | |
584 } | |
585 } | |
586 | |
587 return uuids.toArray(new String[uuids.size()]); | |
588 } | |
589 | |
590 | |
591 /** | |
592 * Returns a concrete Artifact of this collection specified by its uuid. | |
593 * | |
594 * @param uuid The Artifact's uuid. | |
595 * @param context The CallContext. | |
596 * | |
597 * @return an Artifact. | |
598 */ | |
599 protected Artifact getArtifact(String uuid, CallContext context) | |
600 throws ArtifactDatabaseException | |
601 { | |
602 log.debug("FLYSArtifactCollection.getArtifact"); | |
603 | |
604 Backend backend = Backend.getInstance(); | |
605 PersistentArtifact persistent = backend.getArtifact(uuid); | |
606 | |
607 return persistent != null ? persistent.getArtifact() : null; | |
608 } | |
609 | |
610 | |
611 } | |
612 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |