comparison artifacts/src/main/java/org/dive4elements/river/collections/FLYSArtifactCollection.java @ 5838:5aa05a7a34b7

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

http://dive4elements.wald.intevation.org