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 :

http://dive4elements.wald.intevation.org