comparison artifacts/src/main/java/org/dive4elements/river/collections/D4EArtifactCollection.java @ 5867:59ff03ff48f1

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

http://dive4elements.wald.intevation.org