Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java @ 3468:f37e7e8907cb
merged flys-artifacts/2.8.1
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:39 +0200 |
parents | 200e70f31f6f |
children | b1912514e0f5 |
comparison
equal
deleted
inserted
replaced
3387:5ffad8bde8ad | 3468:f37e7e8907cb |
---|---|
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 | |
15 import org.w3c.dom.Document; | |
16 import org.w3c.dom.Element; | |
17 import org.w3c.dom.Node; | |
18 import org.w3c.dom.NodeList; | |
19 | |
20 import de.intevation.artifacts.Artifact; | |
21 import de.intevation.artifacts.ArtifactDatabase; | |
22 import de.intevation.artifacts.ArtifactDatabaseException; | |
23 import de.intevation.artifacts.ArtifactNamespaceContext; | |
24 import de.intevation.artifacts.CallContext; | |
25 import de.intevation.artifacts.CallMeta; | |
26 | |
27 import de.intevation.artifacts.common.utils.XMLUtils; | |
28 | |
29 import de.intevation.artifactdatabase.Backend; | |
30 import de.intevation.artifactdatabase.Backend.PersistentArtifact; | |
31 import de.intevation.artifactdatabase.DefaultArtifactCollection; | |
32 import de.intevation.artifactdatabase.state.Output; | |
33 import de.intevation.artifactdatabase.state.Settings; | |
34 import de.intevation.artifactdatabase.state.StateEngine; | |
35 | |
36 import de.intevation.flys.artifacts.context.FLYSContext; | |
37 import de.intevation.flys.artifacts.FLYSArtifact; | |
38 import de.intevation.flys.exports.OutGenerator; | |
39 import de.intevation.flys.exports.OutputHelper; | |
40 | |
41 import de.intevation.flys.utils.FLYSUtils; | |
42 | |
43 /** | |
44 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> | |
45 */ | |
46 public class FLYSArtifactCollection extends DefaultArtifactCollection { | |
47 /** The logger used in this class. */ | |
48 private static Logger log = Logger.getLogger(FLYSArtifactCollection.class); | |
49 | |
50 /** Constant XPath that points to the outputmodes of an artifact. */ | |
51 public static final String XPATH_ARTIFACT_OUTPUTMODES = | |
52 "/art:result/art:outputmodes"; | |
53 | |
54 public static final String XPATH_ARTIFACT_STATE_DATA = | |
55 "/art:result/art:ui/art:static/art:state/art:data"; | |
56 | |
57 public static final String XPATH_COLLECTION_ITEMS = | |
58 "/art:result/art:artifact-collection/art:collection-item"; | |
59 | |
60 public static final String XPATH_OUT_NAME = "/art:action/@art:name"; | |
61 | |
62 public static final String XPATH_OUT_TYPE = "/art:action/@art:type"; | |
63 | |
64 /** Xpath to master artifacts uuid. */ | |
65 public static final String XPATH_MASTER_UUID = | |
66 "/art:artifact-collection/art:artifact/@art:uuid"; | |
67 | |
68 public static final String XPATH_LOADED_RECOMMENDATIONS = | |
69 "/art:attribute/art:loaded-recommendations"; | |
70 | |
71 | |
72 /** | |
73 * Return description Document for this collection. | |
74 */ | |
75 @Override | |
76 public Document describe(CallContext context) { | |
77 log.debug("FLYSArtifactCollection.describe: " + identifier); | |
78 | |
79 CollectionDescriptionHelper helper = new CollectionDescriptionHelper( | |
80 getName(), identifier(), getCreationTime(), getTTL(), | |
81 context); | |
82 | |
83 ArtifactDatabase db = context.getDatabase(); | |
84 | |
85 Document oldAttrs = getAttribute(); | |
86 AttributeParser parser = new AttributeParser(oldAttrs); | |
87 | |
88 try { | |
89 String[] aUUIDs = getArtifactUUIDs(context); | |
90 | |
91 oldAttrs = removeAttributes(oldAttrs, context); | |
92 parser = new AttributeParser(oldAttrs); | |
93 | |
94 CollectionAttribute newAttr = mergeAttributes( | |
95 db, context, parser, aUUIDs); | |
96 | |
97 if (checkOutputSettings(newAttr, context)) { | |
98 saveCollectionAttribute(db, context, newAttr); | |
99 } | |
100 | |
101 helper.setAttribute(newAttr); | |
102 | |
103 if (aUUIDs != null) { | |
104 for (String uuid: aUUIDs) { | |
105 helper.addArtifact(uuid); | |
106 } | |
107 } | |
108 } | |
109 catch (ArtifactDatabaseException ade) { | |
110 log.error("Error while merging attribute documents.", ade); | |
111 | |
112 helper.setAttribute(parser.getCollectionAttribute()); | |
113 } | |
114 | |
115 return helper.toXML(); | |
116 } | |
117 | |
118 | |
119 /** | |
120 * Merge the current art:outputs nodes with the the outputs provided by the | |
121 * artifacts in the Collection. | |
122 * | |
123 * @param uuids Artifact uuids. | |
124 */ | |
125 protected CollectionAttribute mergeAttributes( | |
126 ArtifactDatabase db, | |
127 CallContext context, | |
128 AttributeParser oldParser, | |
129 String[] uuids | |
130 ) { | |
131 CollectionAttribute cAttribute = | |
132 buildOutAttributes(db, context, oldParser, uuids); | |
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.size() == 0) { | |
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 Document doc = XMLUtils.newDocument(); | |
459 | |
460 FLYSContext flysContext = FLYSUtils.getFlysContext(context); | |
461 StateEngine engine = (StateEngine) flysContext.get( | |
462 FLYSContext.STATE_ENGINE_KEY); | |
463 | |
464 FLYSArtifact masterArtifact = getMasterArtifact(context); | |
465 | |
466 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
467 doc, | |
468 ArtifactNamespaceContext.NAMESPACE_URI, | |
469 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
470 | |
471 OutputParser oParser = new OutputParser(db, context); | |
472 | |
473 if (uuids != null) { | |
474 for (String uuid: uuids) { | |
475 try { | |
476 oParser.parse(uuid); | |
477 } | |
478 catch (ArtifactDatabaseException ade) { | |
479 log.warn(ade, ade); | |
480 } | |
481 } | |
482 } | |
483 | |
484 aParser.parse(); | |
485 | |
486 return new AttributeWriter( | |
487 db, | |
488 aParser.getCollectionAttribute(), | |
489 aParser.getOuts(), | |
490 aParser.getFacets(), | |
491 oParser.getOuts(), | |
492 oParser.getFacets(), | |
493 engine.getCompatibleFacets(masterArtifact.getStateHistoryIds()) | |
494 ).write(); | |
495 } | |
496 | |
497 | |
498 /** | |
499 * Returns the "attribute" (part of description document) for a specific | |
500 * output type. | |
501 * | |
502 * @param context The CallContext object. | |
503 * @param cAttr The CollectionAttribute. | |
504 * @param output The name of the desired output type. | |
505 * | |
506 * @return the attribute for the desired output type. | |
507 */ | |
508 protected Document getAttribute( | |
509 CallContext context, | |
510 CollectionAttribute cAttr, | |
511 String output) | |
512 throws ArtifactDatabaseException | |
513 { | |
514 Document attr = cAttr.toXML(); | |
515 | |
516 Map<String, String> vars = new HashMap<String, String>(); | |
517 vars.put("output", output); | |
518 | |
519 Node out = (Node) XMLUtils.xpath( | |
520 attr, | |
521 "art:attribute/art:outputs/art:output[@name=$output]", | |
522 XPathConstants.NODE, | |
523 ArtifactNamespaceContext.INSTANCE, | |
524 vars); | |
525 | |
526 | |
527 if (out != null) { | |
528 Document o = XMLUtils.newDocument(); | |
529 | |
530 o.appendChild(o.importNode(out, true)); | |
531 | |
532 return o; | |
533 } | |
534 | |
535 return null; | |
536 } | |
537 | |
538 | |
539 /** | |
540 * This method returns the list of artifact UUIDs that this collections | |
541 * contains. | |
542 * | |
543 * @param context The CallContext that is necessary to get information about | |
544 * the ArtifactDatabase. | |
545 * | |
546 * @return a list of uuids. | |
547 */ | |
548 protected String[] getArtifactUUIDs(CallContext context) | |
549 throws ArtifactDatabaseException | |
550 { | |
551 log.debug("FLYSArtifactCollection.getArtifactUUIDs"); | |
552 | |
553 ArtifactDatabase db = context.getDatabase(); | |
554 CallMeta meta = context.getMeta(); | |
555 | |
556 Document itemList = db.listCollectionArtifacts(identifier(), meta); | |
557 NodeList items = (NodeList) XMLUtils.xpath( | |
558 itemList, | |
559 XPATH_COLLECTION_ITEMS, | |
560 XPathConstants.NODESET, | |
561 ArtifactNamespaceContext.INSTANCE); | |
562 | |
563 if (items == null || items.getLength() == 0) { | |
564 log.debug("No artifacts found in this collection."); | |
565 return null; | |
566 } | |
567 | |
568 int num = items.getLength(); | |
569 | |
570 List<String> uuids = new ArrayList<String>(num); | |
571 | |
572 for (int i = 0; i < num; i++) { | |
573 String uuid = XMLUtils.xpathString( | |
574 items.item(i), | |
575 "@art:uuid", | |
576 ArtifactNamespaceContext.INSTANCE); | |
577 | |
578 if (uuid != null && uuid.trim().length() != 0) { | |
579 uuids.add(uuid); | |
580 } | |
581 } | |
582 | |
583 return uuids.toArray(new String[uuids.size()]); | |
584 } | |
585 | |
586 | |
587 /** | |
588 * Returns a concrete Artifact of this collection specified by its uuid. | |
589 * | |
590 * @param uuid The Artifact's uuid. | |
591 * @param context The CallContext. | |
592 * | |
593 * @return an Artifact. | |
594 */ | |
595 protected Artifact getArtifact(String uuid, CallContext context) | |
596 throws ArtifactDatabaseException | |
597 { | |
598 log.debug("FLYSArtifactCollection.getArtifact"); | |
599 | |
600 Backend backend = Backend.getInstance(); | |
601 PersistentArtifact persistent = backend.getArtifact(uuid); | |
602 | |
603 return persistent != null ? persistent.getArtifact() : null; | |
604 } | |
605 | |
606 | |
607 } | |
608 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |