Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java @ 5818:a4ff4167be1e
Request feature info on all layers and show it as html if
the server does not return valid gml.
Non queryable layers produce an error message when the request
fails. This is good enough
author | Andre Heinecke <aheinecke@intevation.de> |
---|---|
date | Wed, 24 Apr 2013 17:33:27 +0200 |
parents | ace7a56d8047 |
children |
line wrap: on
line source
package de.intevation.flys.collections; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.xpath.XPathConstants; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import de.intevation.artifactdatabase.Backend; import de.intevation.artifactdatabase.Backend.PersistentArtifact; import de.intevation.artifactdatabase.DefaultArtifactCollection; import de.intevation.artifactdatabase.state.Facet; import de.intevation.artifactdatabase.state.Output; import de.intevation.artifactdatabase.state.Settings; import de.intevation.artifactdatabase.state.StateEngine; import de.intevation.artifacts.Artifact; import de.intevation.artifacts.ArtifactDatabase; import de.intevation.artifacts.ArtifactDatabaseException; import de.intevation.artifacts.ArtifactNamespaceContext; import de.intevation.artifacts.CallContext; import de.intevation.artifacts.CallMeta; import de.intevation.artifacts.common.utils.XMLUtils; import de.intevation.flys.artifacts.FLYSArtifact; import de.intevation.flys.artifacts.context.FLYSContext; import de.intevation.flys.exports.OutGenerator; import de.intevation.flys.exports.OutputHelper; import de.intevation.flys.utils.FLYSUtils; /** * Collection of artifacts, can do outs, describe. * Lots of stuff done in AttributeParser and AttributeWriter. * Critical out and facet merging. * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> */ public class FLYSArtifactCollection extends DefaultArtifactCollection { /** The logger used in this class. */ private static Logger log = Logger.getLogger(FLYSArtifactCollection.class); /** Constant XPath that points to the outputmodes of an artifact. */ public static final String XPATH_ARTIFACT_OUTPUTMODES = "/art:result/art:outputmodes"; public static final String XPATH_ARTIFACT_STATE_DATA = "/art:result/art:ui/art:static/art:state/art:data"; public static final String XPATH_COLLECTION_ITEMS = "/art:result/art:artifact-collection/art:collection-item"; public static final String XPATH_OUT_NAME = "/art:action/@art:name"; public static final String XPATH_OUT_TYPE = "/art:action/@art:type"; /** Xpath to master artifacts uuid. */ public static final String XPATH_MASTER_UUID = "/art:artifact-collection/art:artifact/@art:uuid"; public static final String XPATH_LOADED_RECOMMENDATIONS = "/art:attribute/art:loaded-recommendations"; /** * Create and return description Document for this collection. */ @Override public Document describe(CallContext context) { log.debug("FLYSArtifactCollection.describe: " + identifier); CollectionDescriptionHelper helper = new CollectionDescriptionHelper( getName(), identifier(), getCreationTime(), getTTL(), context); ArtifactDatabase db = context.getDatabase(); Document oldAttrs = getAttribute(); AttributeParser parser = new AttributeParser(oldAttrs); try { String[] aUUIDs = getArtifactUUIDs(context); oldAttrs = removeAttributes(oldAttrs, context); parser = new AttributeParser(oldAttrs); CollectionAttribute newAttr = mergeAttributes( db, context, parser, aUUIDs); if (checkOutputSettings(newAttr, context)) { saveCollectionAttribute(db, context, newAttr); } helper.setAttribute(newAttr); if (aUUIDs != null) { for (String uuid: aUUIDs) { helper.addArtifact(uuid); } } } catch (ArtifactDatabaseException ade) { log.error("Error while merging attribute documents.", ade); helper.setAttribute(parser.getCollectionAttribute()); } return helper.toXML(); } /** * Merge the current art:outputs nodes with the the outputs provided by the * artifacts in the Collection. * * @param uuids Artifact uuids. */ protected CollectionAttribute mergeAttributes( ArtifactDatabase db, CallContext context, AttributeParser oldParser, String[] uuids ) { CollectionAttribute cAttribute = buildOutAttributes(db, context, oldParser, uuids); if (cAttribute == null) { log.warn("mergeAttributes: cAttribute == null"); return null; } cAttribute.setLoadedRecommendations( getLoadedRecommendations(oldParser.getAttributeDocument())); saveCollectionAttribute(db, context, cAttribute); return cAttribute; } protected Document removeAttributes(Document attrs, CallContext context) { Node outs = (Node) XMLUtils.xpath( attrs, "/art:attribute/art:outputs", XPathConstants.NODE, ArtifactNamespaceContext.INSTANCE); NodeList nodes = (NodeList) XMLUtils.xpath( attrs, "/art:attribute/art:outputs/art:output", XPathConstants.NODESET, ArtifactNamespaceContext.INSTANCE); if (nodes != null) { for (int i = 0; i < nodes.getLength(); i++) { Element e = (Element)nodes.item(i); if(!outputExists(e.getAttribute("name"), context)) { outs.removeChild(e); } } } return attrs; } /** * True if current MasterArtifact has given output. * @param name Name of the output of interest. * @param context current context * @return true if current master artifact has given output. */ protected boolean outputExists(String name, CallContext context) { FLYSArtifact master = getMasterArtifact(context); List<Output> outList = master.getOutputs(context); for (Output o : outList) { if (name.equals(o.getName())) { return true; } } return false; } /** * @param db The ArtifactDatabase which is required to save the attribute * into. * @param attribute The CollectionAttribute that should be stored in the * database. * * @return true, if the transaction was successful, otherwise false. */ protected boolean saveCollectionAttribute( ArtifactDatabase db, CallContext context, CollectionAttribute attribute ) { log.info("Save new CollectionAttribute into database."); Document doc = attribute.toXML(); try { // Save the merged document into database. db.setCollectionAttribute(identifier(), context.getMeta(), doc); log.info("Saving CollectionAttribute was successful."); return true; } catch (ArtifactDatabaseException adb) { log.error(adb, adb); } return false; } /** * Merge the recommendations which have already been loaded from the old * attribute document into the new attribute document. This is necessary, * because mergeAttributes() only merges the art:outputs nodes - all * other nodes are skipped. */ protected Node getLoadedRecommendations(Document oldAttrs) { Element loadedRecoms = (Element) XMLUtils.xpath( oldAttrs, XPATH_LOADED_RECOMMENDATIONS, XPathConstants.NODE, ArtifactNamespaceContext.INSTANCE); return loadedRecoms; } /** * Evaluates the Output settings. If an Output has no Settings set, the * relevant OutGenerator is used to initialize a default Settings object. * * @param attribute The CollectionAttribute. * @param cc The CallContext. * * @return true, if the CollectionAttribute was modified, otherwise false. */ protected boolean checkOutputSettings( CollectionAttribute attribute, CallContext cc ) { boolean modified = false; Map<String, Output> outputMap = attribute != null ? attribute.getOutputs() : null; if (outputMap == null || outputMap.isEmpty()) { log.debug("No Output Settings check necessary."); return modified; } for (Map.Entry<String, Output> entry: outputMap.entrySet()) { String outName = entry.getKey(); Output output = entry.getValue(); if (outName.equals("sq_overview")) { continue; } Settings settings = output.getSettings(); if (settings == null) { log.debug("No Settings set for Output '" + outName + "'."); output.setSettings( createInitialOutputSettings(cc, attribute, outName)); modified = true; } } return modified; } /** * This method uses the the OutGenerator for the specified Output * <i>out</i> to create an initial Settings object. * * @param cc The CallContext object. * @param attr The CollectionAttribute. * @param out The name of the output. * * @return a default Settings object for the specified Output. */ protected Settings createInitialOutputSettings( CallContext cc, CollectionAttribute attr, String out ) { OutGenerator outGen = FLYSContext.getOutGenerator(cc, out, null); if (outGen == null) { return null; } // XXX NOTE: the outGen is not able to process its generate() operation, // because it has no OutputStream set! outGen.init(XMLUtils.newDocument(), null, cc); prepareMasterArtifact(outGen, cc); try { Document outAttr = getAttribute(cc, attr, out); OutputHelper helper = new OutputHelper(identifier()); helper.doOut(outGen, out, out, outAttr, cc); } catch (ArtifactDatabaseException adbe) { log.error(adbe, adbe); } catch (IOException ioe) { log.error(ioe, ioe); } return outGen.getSettings(); } @Override public void out( String type, Document format, OutputStream out, CallContext context) throws IOException { boolean debug = log.isDebugEnabled(); long reqBegin = System.currentTimeMillis(); if (debug) { log.debug(XMLUtils.toString(format)); log.debug("FLYSArtifactCollection.out"); } String name = XMLUtils.xpathString( format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE); String subtype = XMLUtils.xpathString( format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE); if (debug) { log.debug("-> Output name = " + name); log.debug("-> Output type = " + type); log.debug("-> Output subtype = " + subtype); } OutGenerator generator = null; if (type != null && type.length() > 0 && type.indexOf("chartinfo") > 0) { generator = FLYSContext.getOutGenerator(context, type, subtype); } else { generator = FLYSContext.getOutGenerator(context, name, subtype); } if (generator == null) { log.error("There is no generator specified for output: " + name); // TODO Throw an exception. return; } Document oldAttrs = getAttribute(); AttributeParser parser = new AttributeParser(oldAttrs); CollectionAttribute cAttr = parser.getCollectionAttribute(); Output output = cAttr.getOutput(name); Settings settings = null; if (output != null) { settings = output.getSettings(); if (debug) { List<Facet> facets = output.getFacets(); for(Facet facet: facets) { log.debug(" -- Facet " + facet.getName()); } } } generator.init(format, out, context); generator.setSettings(settings); generator.setCollection(this); prepareMasterArtifact(generator, context); try { Document attr = getAttribute(context, cAttr, name); OutputHelper helper = new OutputHelper(identifier()); if (name.equals("sq_overview")) { helper.doOut(generator, name, subtype, format, context); } helper.doOut(generator, name, subtype, attr, context); generator.generate(); } catch (ArtifactDatabaseException adbe) { log.error(adbe, adbe); } if (debug) { long duration = System.currentTimeMillis() -reqBegin; log.info("Processing out(" + name + ") took " + duration + " ms."); } } /** * Sets the master Artifact at the given <i>generator</i>. * * @param generator The generator that gets a master Artifact. * @param cc The CallContext. */ protected void prepareMasterArtifact(OutGenerator generator, CallContext cc ) { // Get master artifact. FLYSArtifact master = getMasterArtifact(cc); if (master != null) { log.debug("Set master Artifact to uuid: " + master.identifier()); generator.setMasterArtifact(master); } else { log.warn("Could not set master artifact."); } } /** * @return masterartifact or null if exception/not found. */ protected FLYSArtifact getMasterArtifact(CallContext context) { try { ArtifactDatabase db = context.getDatabase(); CallMeta callMeta = context.getMeta(); Document document = db.getCollectionsMasterArtifact( identifier(), callMeta); String masterUUID = XMLUtils.xpathString( document, XPATH_MASTER_UUID, ArtifactNamespaceContext.INSTANCE); FLYSArtifact masterArtifact = (FLYSArtifact) getArtifact(masterUUID, context); return masterArtifact; } catch (ArtifactDatabaseException ade) { log.error(ade, ade); } return null; } /** * Return merged output document. * @param uuids List of artifact uuids. */ protected CollectionAttribute buildOutAttributes( ArtifactDatabase db, CallContext context, AttributeParser aParser, String[] uuids) { FLYSContext flysContext = FLYSUtils.getFlysContext(context); StateEngine engine = (StateEngine) flysContext.get( FLYSContext.STATE_ENGINE_KEY); if (engine == null) { log.error("buildOutAttributes: engine == null"); return null; } FLYSArtifact masterArtifact = getMasterArtifact(context); if (masterArtifact == null) { log.debug("buildOutAttributes: masterArtifact == null"); return null; } OutputParser oParser = new OutputParser(db, context); if (uuids != null) { for (String uuid: uuids) { try { oParser.parse(uuid); } catch (ArtifactDatabaseException ade) { log.warn(ade, ade); } } } aParser.parse(); AttributeWriter aWriter = new AttributeWriter( db, aParser.getCollectionAttribute(), aParser.getOuts(), aParser.getFacets(), oParser.getOuts(), oParser.getFacets(), engine.getCompatibleFacets(masterArtifact.getStateHistoryIds()) ); return aWriter.write(); } /** * Returns the "attribute" (part of description document) for a specific * output type. * * @param context The CallContext object. * @param cAttr The CollectionAttribute. * @param output The name of the desired output type. * * @return the attribute for the desired output type. */ protected Document getAttribute( CallContext context, CollectionAttribute cAttr, String output) throws ArtifactDatabaseException { Document attr = cAttr.toXML(); Map<String, String> vars = new HashMap<String, String>(); vars.put("output", output); Node out = (Node) XMLUtils.xpath( attr, "art:attribute/art:outputs/art:output[@name=$output]", XPathConstants.NODE, ArtifactNamespaceContext.INSTANCE, vars); if (out != null) { Document o = XMLUtils.newDocument(); o.appendChild(o.importNode(out, true)); return o; } return null; } /** * This method returns the list of artifact UUIDs that this collections * contains. * * @param context The CallContext that is necessary to get information about * the ArtifactDatabase. * * @return a list of uuids. */ protected String[] getArtifactUUIDs(CallContext context) throws ArtifactDatabaseException { log.debug("FLYSArtifactCollection.getArtifactUUIDs"); ArtifactDatabase db = context.getDatabase(); CallMeta meta = context.getMeta(); Document itemList = db.listCollectionArtifacts(identifier(), meta); NodeList items = (NodeList) XMLUtils.xpath( itemList, XPATH_COLLECTION_ITEMS, XPathConstants.NODESET, ArtifactNamespaceContext.INSTANCE); if (items == null || items.getLength() == 0) { log.debug("No artifacts found in this collection."); return null; } int num = items.getLength(); List<String> uuids = new ArrayList<String>(num); for (int i = 0; i < num; i++) { String uuid = XMLUtils.xpathString( items.item(i), "@art:uuid", ArtifactNamespaceContext.INSTANCE); if (uuid != null && uuid.trim().length() != 0) { uuids.add(uuid); } } return uuids.toArray(new String[uuids.size()]); } /** * Returns a concrete Artifact of this collection specified by its uuid. * * @param uuid The Artifact's uuid. * @param context The CallContext. * * @return an Artifact. */ protected Artifact getArtifact(String uuid, CallContext context) throws ArtifactDatabaseException { log.debug("FLYSArtifactCollection.getArtifact"); Backend backend = Backend.getInstance(); PersistentArtifact persistent = backend.getArtifact(uuid); return persistent != null ? persistent.getArtifact() : null; } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :