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 :

http://dive4elements.wald.intevation.org