teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.artifacts; ingo@119: christian@3883: import java.util.ArrayList; christian@3883: import java.util.Collection; christian@3883: import java.util.HashMap; christian@3883: import java.util.HashSet; christian@3883: import java.util.LinkedList; christian@3883: import java.util.List; christian@3883: import java.util.Map; christian@3883: import java.util.Set; christian@3883: import java.util.TreeMap; christian@3883: christian@3883: import javax.xml.xpath.XPathConstants; christian@3883: christian@3883: import net.sf.ehcache.Cache; christian@3883: christian@3883: import org.apache.log4j.Logger; christian@3883: import org.w3c.dom.Document; christian@3883: import org.w3c.dom.Element; christian@3883: import org.w3c.dom.Node; christian@3883: import org.w3c.dom.NodeList; christian@3883: teichmann@5831: import org.dive4elements.artifactdatabase.ArtifactDatabaseImpl; teichmann@5831: import org.dive4elements.artifactdatabase.DefaultArtifact; teichmann@5831: import org.dive4elements.artifactdatabase.ProtocolUtils; teichmann@5831: import org.dive4elements.artifactdatabase.data.DefaultStateData; teichmann@5831: import org.dive4elements.artifactdatabase.data.StateData; teichmann@5831: import org.dive4elements.artifactdatabase.state.DefaultFacet; teichmann@5831: import org.dive4elements.artifactdatabase.state.DefaultOutput; teichmann@5831: import org.dive4elements.artifactdatabase.state.Facet; teichmann@5831: import org.dive4elements.artifactdatabase.state.Output; teichmann@5831: import org.dive4elements.artifactdatabase.state.State; teichmann@5831: import org.dive4elements.artifactdatabase.state.StateEngine; rrenkert@7845: import org.dive4elements.artifactdatabase.state.StaticFacet; teichmann@5831: import org.dive4elements.artifactdatabase.transition.TransitionEngine; teichmann@5831: import org.dive4elements.artifacts.Artifact; teichmann@5831: import org.dive4elements.artifacts.ArtifactDatabase; teichmann@5831: import org.dive4elements.artifacts.ArtifactDatabaseException; teichmann@5831: import org.dive4elements.artifacts.ArtifactFactory; teichmann@5831: import org.dive4elements.artifacts.CallContext; teichmann@5831: import org.dive4elements.artifacts.CallMeta; teichmann@5831: import org.dive4elements.artifacts.Message; teichmann@5831: import org.dive4elements.artifacts.common.ArtifactNamespaceContext; teichmann@5831: import org.dive4elements.artifacts.common.utils.XMLUtils; teichmann@5831: import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator; teichmann@5831: import org.dive4elements.river.artifacts.cache.CacheFactory; teichmann@5866: import org.dive4elements.river.artifacts.context.RiverContext; teichmann@7227: import org.dive4elements.river.artifacts.context.RiverContextFactory; teichmann@5831: import org.dive4elements.river.artifacts.model.CalculationMessage; teichmann@5831: import org.dive4elements.river.artifacts.states.DefaultState; teichmann@5831: import org.dive4elements.river.artifacts.states.DefaultState.ComputeType; teichmann@5865: import org.dive4elements.river.utils.RiverUtils; ingo@119: ingo@119: /** felix@2169: * The default FLYS artifact with convenience added. felix@2169: * (Subclass to get fully functional artifacts). ingo@119: * ingo@119: * @author Ingo Weinzierl ingo@119: */ rrenkert@7843: public class D4EArtifact extends DefaultArtifact { ingo@119: teichmann@8202: /** The log that is used in this artifact. */ teichmann@5867: private static Logger log = Logger.getLogger(D4EArtifact.class); ingo@119: ingo@686: public static final String COMPUTING_CACHE = "computed.values"; ingo@686: felix@1704: /** The XPath that points to the input data elements of the FEED document. */ ingo@121: public static final String XPATH_FEED_INPUT = ingo@121: "/art:action/art:data/art:input"; ingo@119: felix@1704: /** The XPath that points to the name of the target state of ADVANCE. */ ingo@122: public static final String XPATH_ADVANCE_TARGET = ingo@122: "/art:action/art:target/@art:name"; ingo@122: ingo@940: public static final String XPATH_MODEL_ARTIFACT = ingo@941: "/art:action/art:template/@uuid"; ingo@940: sascha@1057: public static final String XPATH_FILTER = sascha@1057: "/art:action/art:filter/art:out"; sascha@1057: aheinecke@5975: /** Path to 'ids' (data) in doc that comes from datacage. */ aheinecke@5975: public static final String XPATH_IDS = "/art:action/art:ids/@value"; aheinecke@5975: aheinecke@6140: /** Path to 'target_out' (data) in doc that comes from datacage. */ aheinecke@6140: public static final String XPATH_TARGET_OUT = "/art:action/art:target_out/@value"; aheinecke@5975: felix@1704: /** The constant string that shows that an operation was successful. */ ingo@122: public static final String OPERATION_SUCCESSFUL = "SUCCESS"; ingo@122: felix@1704: /** The constant string that shows that an operation failed. */ ingo@122: public static final String OPERATION_FAILED = "FAILURE"; ingo@122: ingo@119: /** The identifier of the current state. */ ingo@119: protected String currentStateId; ingo@119: felix@1704: /** The identifiers of previous states on a stack. */ sascha@661: protected List previousStateIds; ingo@122: felix@1704: /** The data that have been inserted into this artifact. */ bjoern@3920: private Map data; ingo@121: felix@1771: /** Mapping of state names to created facets. */ bjoern@4497: private Map> facets; ingo@687: felix@1704: /** felix@1704: * Used to generates "view" on the facets (hides facets not matching the felix@1704: * filter in output of collection); out -> facets. felix@1704: */ sascha@1056: protected Map> filterFacets; sascha@1056: felix@7676: /** To which out this artifacts facets are bound by default. */ aheinecke@6140: private String boundToOut; aheinecke@5975: felix@7676: teichmann@7227: /** teichmann@7227: * Interface to a global facet filter. teichmann@7227: */ teichmann@7227: public interface FacetFilter { teichmann@7227: boolean accept(String outName, String facetName); teichmann@7227: } // interface FacetFilter teichmann@7227: ingo@121: ingo@121: /** teichmann@5867: * The default constructor that creates an empty D4EArtifact. ingo@121: */ teichmann@5867: public D4EArtifact() { ingo@686: data = new TreeMap(); sascha@661: previousStateIds = new ArrayList(); felix@1771: facets = new HashMap>(); ingo@121: } ingo@121: felix@4147: sascha@3193: /** sascha@3193: * This method appends the static data - that has already been inserted by sascha@3193: * the user - to the static node of the DESCRIBE document. sascha@3193: * sascha@3193: * @param doc The document. sascha@3193: * @param ui The root node. sascha@3193: * @param context The CallContext. sascha@3193: * @param uuid The identifier of the artifact. sascha@3193: */ sascha@3193: protected void appendStaticUI( sascha@3193: Document doc, sascha@3193: Node ui, sascha@3193: CallContext context, sascha@3193: String uuid) sascha@3193: { sascha@3193: List stateIds = getPreviousStateIds(); sascha@3193: teichmann@5866: RiverContext flysContext = RiverUtils.getFlysContext(context); sascha@3193: StateEngine engine = (StateEngine) flysContext.get( teichmann@5866: RiverContext.STATE_ENGINE_KEY); sascha@3193: sascha@3554: boolean debug = log.isDebugEnabled(); sascha@3554: sascha@3193: for (String stateId: stateIds) { sascha@3554: if (debug) { sascha@3554: log.debug("Append static data for state: " + stateId); sascha@3554: } sascha@3193: DefaultState state = (DefaultState) engine.getState(stateId); sascha@3193: sascha@3193: ui.appendChild(state.describeStatic(this, doc, ui, context, uuid)); sascha@3193: } sascha@3193: } sascha@3193: ingo@121: ingo@121: /** ingo@119: * Initialize the artifact and insert new data if data contains ingo@119: * information necessary for this artifact. ingo@119: * ingo@119: * @param identifier The UUID. ingo@119: * @param factory The factory that is used to create this artifact. ingo@119: * @param context The CallContext. ingo@119: * @param data Some optional data. ingo@119: */ ingo@119: @Override ingo@119: public void setup( ingo@119: String identifier, ingo@119: ArtifactFactory factory, ingo@119: Object context, ingo@952: CallMeta callMeta, rrenkert@7842: Document data, rrenkert@7842: List facets) ingo@119: { aheinecke@6140: log.debug("Setup this artifact with the uuid: " + identifier); ingo@119: rrenkert@7842: super.setup(identifier, factory, context, callMeta, data, facets); ingo@119: teichmann@5866: RiverContext flysContext = RiverUtils.getFlysContext(context); felix@1067: felix@1067: List states = getStates(context); ingo@119: ingo@121: String name = getName(); sascha@3554: aheinecke@6140: log.debug("setup(): Set initial state for artifact '" + name + "'"); ingo@119: felix@2169: if (states == null) { sascha@3554: log.error("No states found from which an initial " felix@2766: + "state could be picked."); felix@2169: } ingo@119: setCurrentState(states.get(0)); ingo@940: felix@7746: handleInitModel(data, context, callMeta); felix@7746: rrenkert@7888: if (!facets.isEmpty()) { rrenkert@7888: buildStaticFacets(data, facets, callMeta); rrenkert@7888: } rrenkert@7845: felix@7746: filterFacets = buildFilterFacets(data); felix@7746: felix@7746: extractOut(data); felix@7746: } felix@7746: rrenkert@7845: protected void buildStaticFacets( rrenkert@7845: Document data, rrenkert@7845: List facets, rrenkert@7845: CallMeta callMeta) rrenkert@7845: { rrenkert@7845: List staticFacets = new ArrayList(); rrenkert@7845: String currentState = getCurrentStateId(); rrenkert@7845: for (int i = 0; i < facets.size(); i++) { rrenkert@7845: try { rrenkert@7845: StaticFacet facet = (StaticFacet)facets.get(i).newInstance(); rrenkert@7845: facet.setup(this, data, callMeta); rrenkert@7845: staticFacets.add(facet); rrenkert@7845: } rrenkert@7845: catch (InstantiationException ie) { rrenkert@7845: log.error(ie.getLocalizedMessage(), ie); rrenkert@7845: } rrenkert@7845: catch (IllegalAccessException iae) { rrenkert@7845: log.error(iae.getLocalizedMessage(), iae); rrenkert@7845: } rrenkert@7845: } rrenkert@7845: this.facets.put(currentState, staticFacets); rrenkert@7845: } rrenkert@7845: felix@7746: protected void handleInitModel(Document data, Object context, CallMeta callMeta) { felix@7746: RiverContext flysContext = RiverUtils.getFlysContext(context); felix@7746: ingo@940: String model = XMLUtils.xpathString( ingo@940: data, ingo@940: XPATH_MODEL_ARTIFACT, ingo@940: ArtifactNamespaceContext.INSTANCE); ingo@940: ingo@940: if (model != null && model.length() > 0) { ingo@940: ArtifactDatabase db = (ArtifactDatabase) flysContext.get( ingo@940: ArtifactDatabaseImpl.GLOBAL_CONTEXT_KEY); ingo@940: ingo@940: try { ingo@952: initialize(db.getRawArtifact(model), context, callMeta); ingo@940: } ingo@940: catch (ArtifactDatabaseException adbe) { sascha@3554: log.error(adbe, adbe); ingo@940: } ingo@940: } ingo@940: } ingo@940: aheinecke@5975: protected void extractOut(Document data) { aheinecke@6140: String targetOut = XMLUtils.xpathString(data, XPATH_TARGET_OUT, aheinecke@5975: ArtifactNamespaceContext.INSTANCE); aheinecke@6140: if (targetOut.isEmpty()) { aheinecke@6140: targetOut = null; aheinecke@6140: } aheinecke@5975: aheinecke@6140: setBoundToOut(targetOut); aheinecke@5975: } aheinecke@5975: aheinecke@5975: /** aheinecke@5975: * Return the value of id element in Datacage data document. aheinecke@5975: * @param data Document as passed by datacage. aheinecke@5975: * @return the id element value of data document. aheinecke@5975: */ aheinecke@5975: public static String getDatacageIDValue(Document data) { aheinecke@5975: String ids = XMLUtils.xpathString(data, XPATH_IDS, aheinecke@5975: ArtifactNamespaceContext.INSTANCE); aheinecke@5975: aheinecke@5975: return ids; aheinecke@5975: } aheinecke@5975: aheinecke@5975: felix@1763: felix@2141: /** Get copy of previous state ids as Strings in list. */ sascha@1059: protected List clonePreviousStateIds() { sascha@1059: return new ArrayList(previousStateIds); sascha@1059: } sascha@1059: felix@2141: felix@1895: /** felix@1895: * Copies data item from other artifact to this artifact. felix@2111: * felix@1895: * @param other Artifact from which to get data. felix@1895: * @param name Name of data. felix@1895: */ teichmann@5867: protected void importData(D4EArtifact other, final String name) { felix@1895: if (other == null) { sascha@3554: log.error("No other art. to import data " + name + " from."); felix@2111: return; felix@1895: } felix@1895: felix@1895: StateData sd = other.getData(name); felix@1895: felix@1895: if (sd == null) { sascha@3554: log.warn("Other artifact has no data " + name + "."); felix@1895: return; felix@1895: } felix@1895: felix@1895: this.addData(name, sd); felix@1895: } felix@1763: felix@2169: felix@4206: /** Clone the internal map of map of state-name to state-data. */ sascha@1059: protected Map cloneData() { sascha@1059: Map copy = new TreeMap(); sascha@1059: sascha@1059: for (Map.Entry entry: data.entrySet()) { sascha@1059: copy.put(entry.getKey(), entry.getValue().deepCopy()); sascha@1059: } sascha@1059: sascha@1059: return copy; sascha@1059: } sascha@1059: felix@4147: felix@1763: /** felix@1763: * Return a copy of the facet mapping. felix@1771: * @return Mapping of state-ids to facets. felix@1763: */ felix@1771: protected Map> cloneFacets() { christian@3306: Map> copy = new HashMap>(); sascha@1059: felix@1771: for (Map.Entry> entry: facets.entrySet()) { felix@1771: List facets = entry.getValue(); felix@1771: List facetCopies = new ArrayList(facets.size()); felix@1771: for (Facet facet: facets) { felix@1771: facetCopies.add(facet.deepCopy()); felix@1771: } felix@1771: copy.put(entry.getKey(), facetCopies); sascha@1059: } sascha@1059: sascha@1059: return copy; sascha@1059: } ingo@940: felix@1763: felix@1765: /** felix@1765: * (called from setup). felix@1771: * @param artifact master-artifact (if any, otherwise initialize is not called). felix@1765: */ ingo@952: protected void initialize( ingo@952: Artifact artifact, ingo@952: Object context, ingo@952: CallMeta callMeta) ingo@952: { teichmann@5867: if (!(artifact instanceof D4EArtifact)) { sascha@1059: return; sascha@1059: } sascha@1059: teichmann@5867: D4EArtifact flys = (D4EArtifact)artifact; sascha@1059: sascha@1059: currentStateId = flys.currentStateId; sascha@1059: previousStateIds = flys.clonePreviousStateIds(); sascha@1059: name = flys.name; sascha@1059: data = flys.cloneData(); sascha@1059: facets = flys.cloneFacets(); sascha@1059: // Do not clone filter facets! ingo@2093: ingo@2606: ArrayList stateIds = (ArrayList) getPreviousStateIds(); ingo@2606: ArrayList toInitialize = (ArrayList) stateIds.clone(); ingo@2093: ingo@2606: toInitialize.add(getCurrentStateId()); ingo@2606: ingo@2606: for (String stateId: toInitialize) { ingo@2093: State state = getState(context, stateId); ingo@2093: ingo@2093: if (state != null) { ingo@2095: state.initialize(artifact, this, context, callMeta); ingo@2093: } ingo@2093: } ingo@119: } ingo@119: felix@1704: felix@1704: /** felix@1704: * Builds filter facets from document. felix@1704: * @see filterFacets felix@1704: */ sascha@1057: protected Map> buildFilterFacets(Document document) { bjoern@4403: if (log.isDebugEnabled()) { bjoern@4403: log.debug("Building filter factes for artifact " + this.getName()); bjoern@4403: } sascha@1057: sascha@1057: NodeList nodes = (NodeList)XMLUtils.xpath( sascha@1057: document, sascha@1057: XPATH_FILTER, sascha@1057: XPathConstants.NODESET, sascha@1057: ArtifactNamespaceContext.INSTANCE); sascha@1057: sascha@1057: if (nodes == null || nodes.getLength() == 0) { sascha@1057: return null; sascha@1057: } sascha@1057: sascha@1057: Map> result = new HashMap>(); sascha@1057: sascha@1057: for (int i = 0, N = nodes.getLength(); i < N; ++i) { sascha@1057: Element element = (Element)nodes.item(i); sascha@1057: String oName = element.getAttribute("name"); bjoern@4403: if (oName == null || oName.isEmpty()) { sascha@1057: continue; sascha@1057: } sascha@1057: sascha@1057: List facets = new ArrayList(); sascha@1057: sascha@1057: NodeList facetNodes = element.getElementsByTagNameNS( sascha@1057: ArtifactNamespaceContext.NAMESPACE_URI, sascha@1057: "facet"); sascha@1057: sascha@1057: for (int j = 0, M = facetNodes.getLength(); j < M; ++j) { sascha@1057: Element facetElement = (Element)facetNodes.item(j); sascha@1057: sascha@1057: String fName = facetElement.getAttribute("name"); sascha@1057: sascha@1057: int index; sascha@1057: try { sascha@1057: index = Integer.parseInt(facetElement.getAttribute("index")); sascha@1057: } sascha@1057: catch (NumberFormatException nfe) { sascha@3554: log.warn(nfe); sascha@1057: index = 0; sascha@1057: } bjoern@4403: if (log.isDebugEnabled()) { bjoern@4403: log.debug("Creating filter facet " + fName + " with index " + index + bjoern@4403: " for out " + oName); bjoern@4403: } sascha@1057: facets.add(new DefaultFacet(index, fName, "")); sascha@1057: } sascha@1057: sascha@1057: if (!facets.isEmpty()) { sascha@1057: result.put(oName, facets); sascha@1057: } sascha@1057: } sascha@1057: sascha@1057: return result; sascha@1057: } sascha@1057: ingo@119: ingo@119: /** ingo@119: * Insert new data included in input into the current state. ingo@119: * ingo@119: * @param target XML document that contains new data. ingo@119: * @param context The CallContext. ingo@119: * ingo@119: * @return a document that contains a SUCCESS or FAILURE message. ingo@119: */ ingo@119: @Override ingo@119: public Document feed(Document target, CallContext context) { teichmann@5867: log.debug("D4EArtifact.feed()"); ingo@123: ingo@121: Document doc = XMLUtils.newDocument(); ingo@119: ingo@121: XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( ingo@121: doc, ingo@121: ArtifactNamespaceContext.NAMESPACE_URI, ingo@121: ArtifactNamespaceContext.NAMESPACE_PREFIX); ingo@119: ingo@121: Element result = creator.create("result"); ingo@121: doc.appendChild(result); ingo@119: ingo@121: try { sascha@3553: saveData(target, context); ingo@689: sascha@705: compute(context, ComputeType.FEED, true); ingo@689: ingo@123: return describe(target, context); ingo@121: } ingo@121: catch (IllegalArgumentException iae) { sascha@658: // do not store state if validation fails. sascha@658: context.afterCall(CallContext.NOTHING); ingo@122: creator.addAttr(result, "type", OPERATION_FAILED, true); ingo@121: ingo@121: result.setTextContent(iae.getMessage()); ingo@121: } ingo@121: ingo@121: return doc; ingo@119: } ingo@119: felix@4147: sascha@3193: /** sascha@3193: * This method returns a description of this artifact. sascha@3193: * sascha@3193: * @param data Some data. sascha@3193: * @param context The CallContext. sascha@3193: * sascha@3193: * @return the description of this artifact. sascha@3193: */ christian@3306: @Override sascha@3193: public Document describe(Document data, CallContext context) { sascha@3193: sascha@3554: if (log.isDebugEnabled()) { sascha@3554: log.debug( sascha@3554: "Describe: the current state is: " + getCurrentStateId()); sascha@3193: dumpArtifact(); sascha@3193: } sascha@3193: teichmann@5866: RiverContext flysContext = RiverUtils.getFlysContext(context); sascha@3193: sascha@3193: StateEngine stateEngine = (StateEngine) flysContext.get( teichmann@5866: RiverContext.STATE_ENGINE_KEY); sascha@3193: sascha@3193: TransitionEngine transitionEngine = (TransitionEngine) flysContext.get( teichmann@5866: RiverContext.TRANSITION_ENGINE_KEY); sascha@3193: sascha@3193: List reachable = transitionEngine.getReachableStates( sascha@3193: this, getCurrentState(context), stateEngine); sascha@3193: sascha@3193: Document description = XMLUtils.newDocument(); sascha@3193: XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( sascha@3193: description, sascha@3193: ArtifactNamespaceContext.NAMESPACE_URI, sascha@3193: ArtifactNamespaceContext.NAMESPACE_PREFIX); sascha@3193: sascha@3193: Element root = ProtocolUtils.createRootNode(creator); sascha@3193: description.appendChild(root); sascha@3193: sascha@3193: State current = getCurrentState(context); sascha@3193: sascha@3193: ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash()); sascha@3193: ProtocolUtils.appendState(creator, root, current); sascha@3193: ProtocolUtils.appendReachableStates(creator, root, reachable); sascha@3193: sascha@3193: appendBackgroundActivity(creator, root, context); sascha@3193: sascha@3193: Element ui = ProtocolUtils.createArtNode( sascha@3193: creator, "ui", null, null); sascha@3193: sascha@3193: Element staticUI = ProtocolUtils.createArtNode( sascha@3193: creator, "static", null, null); sascha@3193: sascha@3193: Element outs = ProtocolUtils.createArtNode( sascha@3193: creator, "outputmodes", null, null); sascha@3193: appendOutputModes(description, outs, context, identifier()); sascha@3193: sascha@3193: appendStaticUI(description, staticUI, context, identifier()); sascha@3193: sascha@3193: Element name = ProtocolUtils.createArtNode( sascha@3193: creator, "name", sascha@3193: new String[] { "value" }, sascha@3193: new String[] { getName() }); sascha@3193: sascha@3193: Element dynamic = current.describe( sascha@3193: this, sascha@3193: description, sascha@3193: root, sascha@3193: context, sascha@3193: identifier()); sascha@3193: sascha@3193: if (dynamic != null) { sascha@3193: ui.appendChild(dynamic); sascha@3193: } sascha@3193: sascha@3193: ui.appendChild(staticUI); sascha@3193: sascha@3193: root.appendChild(name); sascha@3193: root.appendChild(ui); sascha@3193: root.appendChild(outs); sascha@3193: sascha@3193: return description; sascha@3193: } sascha@3193: sascha@3193: /** Override me! */ sascha@3193: sascha@3193: protected void appendBackgroundActivity( sascha@3193: ElementCreator cr, sascha@3193: Element root, sascha@3193: CallContext context sascha@3193: ) { sascha@3193: LinkedList messages = context.getBackgroundMessages(); sascha@3193: sascha@3193: if (messages == null) { sascha@3193: return; sascha@3193: } sascha@3193: sascha@3193: Element inBackground = cr.create("background-processing"); sascha@3193: root.appendChild(inBackground); sascha@3193: sascha@3193: cr.addAttr( sascha@3193: inBackground, sascha@3193: "value", sascha@3193: String.valueOf(context.isInBackground()), sascha@3193: true); sascha@3193: felix@4206: CalculationMessage message = (CalculationMessage) messages.getLast(); sascha@3193: cr.addAttr( sascha@3193: inBackground, sascha@3193: "steps", sascha@3193: String.valueOf(message.getSteps()), sascha@3193: true); sascha@3193: sascha@3193: cr.addAttr( sascha@3193: inBackground, sascha@3193: "currentStep", sascha@3193: String.valueOf(message.getCurrentStep()), sascha@3193: true); sascha@3193: sascha@3193: inBackground.setTextContent(message.getMessage()); sascha@3193: } sascha@3193: sascha@3193: /** sascha@3193: * Append output mode nodes to a document. sascha@3193: */ sascha@3193: protected void appendOutputModes( sascha@3193: Document doc, sascha@3193: Element outs, sascha@3193: CallContext context, sascha@3193: String uuid) sascha@3193: { sascha@3193: List generated = getOutputs(context); sascha@3554: sascha@3554: if (log.isDebugEnabled()) { sascha@3554: log.debug("This Artifact has " + generated.size() + " Outputs."); sascha@3554: } sascha@3193: sascha@3193: ProtocolUtils.appendOutputModes(doc, outs, generated); sascha@3193: } sascha@3193: ingo@119: ingo@119: /** ingo@122: * This method handles request for changing the current state of an ingo@122: * artifact. It is possible to step forward or backward. ingo@122: * ingo@122: * @param target The incoming ADVANCE document. ingo@122: * @param context The CallContext. ingo@122: * ingo@122: * @return a document that contains a SUCCESS or FAILURE message. ingo@122: */ christian@3306: @Override ingo@122: public Document advance(Document target, CallContext context) { sascha@3554: sascha@3554: boolean debug = log.isDebugEnabled(); sascha@3554: ingo@122: Document doc = XMLUtils.newDocument(); ingo@122: ingo@122: XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( ingo@122: doc, ingo@122: ArtifactNamespaceContext.NAMESPACE_URI, ingo@122: ArtifactNamespaceContext.NAMESPACE_PREFIX); ingo@122: ingo@122: Element result = ec.create("result"); ingo@122: ingo@2606: String currentStateId = getCurrentStateId(); ingo@2606: String targetState = XMLUtils.xpathString( ingo@122: target, XPATH_ADVANCE_TARGET, ArtifactNamespaceContext.INSTANCE); ingo@122: sascha@3554: if (debug) { teichmann@5867: log.debug("D4EArtifact.advance() to '" + targetState + "'"); sascha@3554: } ingo@140: ingo@2606: if (!currentStateId.equals(targetState) ingo@2606: && isStateReachable(targetState, context)) ingo@2606: { sascha@3554: if (debug) { sascha@3554: log.debug("Advance: Step forward"); sascha@3554: } ingo@122: sascha@661: List prev = getPreviousStateIds(); ingo@2606: prev.add(currentStateId); ingo@122: ingo@122: setCurrentStateId(targetState); ingo@122: sascha@3554: if (debug) { sascha@3554: log.debug("Compute data for state: " + targetState); sascha@3554: } sascha@705: compute(context, ComputeType.ADVANCE, true); ingo@693: ingo@123: return describe(target, context); ingo@122: } ingo@140: else if (isPreviousState(targetState, context)) { sascha@3554: if (debug) { sascha@3554: log.debug("Advance: Step back to"); sascha@3554: } ingo@122: ingo@1086: List prevs = getPreviousStateIds(); ingo@140: int targetIdx = prevs.indexOf(targetState); ingo@140: int start = prevs.size() - 1; ingo@140: ingo@1086: destroyStates(prevs, context); ingo@1084: ingo@140: for (int i = start; i >= targetIdx; i--) { ingo@140: String prev = prevs.get(i); sascha@3554: if (debug) { sascha@3554: log.debug("Remove state id '" + prev + "'"); sascha@3554: } ingo@1084: ingo@140: prevs.remove(prev); ingo@691: facets.remove(prev); ingo@140: } ingo@140: ingo@1094: destroyState(getCurrentStateId(), context); ingo@140: setCurrentStateId(targetState); ingo@140: ingo@140: return describe(target, context); ingo@140: } ingo@122: sascha@3554: log.warn("Advance: Cannot advance to '" + targetState + "'"); ingo@123: ec.addAttr(result, "type", OPERATION_FAILED, true); ingo@122: ingo@123: doc.appendChild(result); ingo@122: ingo@122: return doc; ingo@122: } ingo@122: ingo@122: ingo@122: /** ingo@119: * Returns the identifier of the current state. ingo@119: * ingo@119: * @return the identifier of the current state. ingo@119: */ sascha@722: public String getCurrentStateId() { ingo@119: return currentStateId; ingo@119: } ingo@119: ingo@119: ingo@119: /** ingo@119: * Sets the identifier of the current state. ingo@119: * ingo@119: * @param id the identifier of a state. ingo@119: */ ingo@119: protected void setCurrentStateId(String id) { ingo@119: currentStateId = id; ingo@119: } ingo@119: ingo@119: ingo@119: /** ingo@119: * Set the current state of this artifact. NOTEWe don't store the ingo@119: * State object itself - which is not necessary - but its identifier. So ingo@119: * this method will just call the setCurrentStateId() method with the ingo@119: * identifier of state. ingo@119: * ingo@119: * @param state The new current state. ingo@119: */ ingo@119: protected void setCurrentState(State state) { ingo@119: setCurrentStateId(state.getID()); ingo@119: } ingo@119: ingo@119: ingo@119: /** ingo@119: * Returns the current state of the artifact. ingo@119: * ingo@119: * @return the current State of the artifact. ingo@119: */ ingo@1654: public State getCurrentState(Object context) { felix@1067: return getState(context, getCurrentStateId()); felix@1067: } felix@1067: felix@1067: felix@1067: /** felix@1067: * Get list of existant states for this Artifact. felix@1067: * @param context Contex to get StateEngine from. felix@1067: * @return list of states. felix@1067: */ felix@1067: protected List getStates(Object context) { teichmann@5866: RiverContext flysContext = RiverUtils.getFlysContext(context); ingo@119: StateEngine engine = (StateEngine) flysContext.get( teichmann@5866: RiverContext.STATE_ENGINE_KEY); felix@1067: return engine.getStates(getName()); ingo@119: } ingo@119: felix@1067: felix@1067: /** felix@1067: * Get state with given ID. felix@1067: * @param context Context to get StateEngine from. felix@1067: * @param stateID ID of state to get. felix@1067: * @return state with given ID. felix@1067: */ sascha@722: protected State getState(Object context, String stateID) { teichmann@5866: RiverContext flysContext = RiverUtils.getFlysContext(context); sascha@722: StateEngine engine = (StateEngine) flysContext.get( teichmann@5866: RiverContext.STATE_ENGINE_KEY); sascha@722: return engine.getState(stateID); sascha@722: } sascha@722: ingo@119: ingo@119: /** ingo@122: * Returns the vector of previous state identifiers. ingo@122: * ingo@122: * @return the vector of previous state identifiers. ingo@122: */ sascha@661: protected List getPreviousStateIds() { ingo@122: return previousStateIds; ingo@122: } ingo@122: ingo@122: ingo@122: /** felix@1783: * Get all previous and the current state id. felix@1783: * @return #getPreviousStateIds() + #getCurrentStateId() felix@1783: */ felix@1783: public List getStateHistoryIds() { ingo@2606: ArrayList prevIds = (ArrayList) getPreviousStateIds(); ingo@2606: ArrayList allIds = (ArrayList) prevIds.clone(); ingo@2606: felix@1783: allIds.add(getCurrentStateId()); felix@1783: return allIds; felix@1783: } felix@1783: felix@1783: felix@1783: /** ingo@121: * Adds a new StateData item to the data pool of this artifact. ingo@121: * ingo@121: * @param name the name of the data object. ingo@121: * @param data the data object itself. ingo@121: */ ingo@121: protected void addData(String name, StateData data) { ingo@121: this.data.put(name, data); ingo@121: } ingo@121: ingo@121: felix@4206: /** Remove and return statedata associated to name. */ ingo@1656: protected StateData removeData(String name) { ingo@1656: return this.data.remove(name); ingo@1656: } ingo@1656: ingo@1656: ingo@121: /** ingo@122: * This method returns a specific StateData object that is stored in the ingo@122: * data pool of this artifact. ingo@122: * ingo@122: * @param name The name of the data object. ingo@122: * ingo@122: * @return the StateData object if existing, otherwise null. ingo@122: */ ingo@298: public StateData getData(String name) { ingo@122: return data.get(name); ingo@122: } ingo@122: felix@4206: bjoern@3920: /** bjoern@3920: * A derived Artifact class can use this method to set the data bjoern@3920: */ bjoern@3920: protected void setData(Map data) { bjoern@3920: this.data = data; bjoern@3920: } ingo@122: felix@4206: felix@2169: /** Return named data item, null if not found. */ ingo@937: public String getDataAsString(String name) { ingo@937: StateData data = getData(name); ingo@937: return data != null ? (String) data.getValue() : null; ingo@937: } ingo@937: felix@1763: felix@1763: /** ingo@2013: * This method returns the value of a StateData object stored in the data ingo@2013: * pool of this Artifact as Integer. ingo@2013: * ingo@2013: * @param name The name of the StateData object. ingo@2013: * ingo@2013: * @return an Integer representing the value of the data object or null if ingo@2013: * no object was found for name. ingo@2013: * ingo@2013: * @throws NumberFormatException if the value of the data object could not ingo@2013: * be transformed into an Integer. ingo@2013: */ ingo@2013: public Integer getDataAsInteger(String name) ingo@2013: throws NumberFormatException ingo@2013: { ingo@2013: String value = getDataAsString(name); ingo@2013: ingo@2013: if (value != null && value.length() > 0) { ingo@2013: return Integer.parseInt(value); ingo@2013: } ingo@2013: ingo@2013: return null; ingo@2013: } ingo@2013: ingo@2013: ingo@2013: /** ingo@2038: * This method returns the value of a StateData object stored in the data ingo@2701: * pool of this Artifact as Double. ingo@2701: * ingo@2701: * @param name The name of the StateData object. ingo@2701: * ingo@2701: * @return an Double representing the value of the data object or null if ingo@2701: * no object was found for name. ingo@2701: * ingo@2701: * @throws NumberFormatException if the value of the data object could not ingo@2701: * be transformed into a Double. ingo@2701: */ ingo@2701: public Double getDataAsDouble(String name) ingo@2701: throws NumberFormatException ingo@2701: { ingo@2701: String value = getDataAsString(name); ingo@2701: ingo@2701: if (value != null && value.length() > 0) { ingo@2701: return Double.parseDouble(value); ingo@2701: } ingo@2701: ingo@2701: return null; ingo@2701: } ingo@2701: ingo@2701: ingo@2701: /** ingo@2701: * This method returns the value of a StateData object stored in the data ingo@2128: * pool of this Artifact as Long. ingo@2128: * ingo@2128: * @param name The name of the StateData object. ingo@2128: * ingo@2128: * @return a Long representing the value of the data object or null if ingo@2128: * no object was found for name. ingo@2128: * ingo@2128: * @throws NumberFormatException if the value of the data object could not ingo@2128: * be transformed into a Long. ingo@2128: */ ingo@2128: public Long getDataAsLong(String name) ingo@2128: throws NumberFormatException ingo@2128: { ingo@2128: String value = getDataAsString(name); ingo@2128: ingo@2128: if (value != null && value.length() > 0) { ingo@2128: return Long.parseLong(value); ingo@2128: } ingo@2128: ingo@2128: return null; ingo@2128: } ingo@2128: ingo@2128: ingo@2128: /** ingo@2128: * This method returns the value of a StateData object stored in the data ingo@2038: * pool of this Artifact is Boolean using Boolean.valueOf(). ingo@2038: * ingo@2038: * @param name The name of the StateData object. ingo@2038: * ingo@2038: * @return a Boolean representing the value of the data object or null if no ingo@2038: * such object is existing. ingo@2038: */ ingo@2038: public Boolean getDataAsBoolean(String name) { ingo@2038: String value = getDataAsString(name); ingo@2038: ingo@2038: if (value == null || value.length() == 0) { ingo@2038: return null; ingo@2038: } ingo@2038: ingo@2038: return Boolean.valueOf(value); ingo@2038: } ingo@2038: ingo@2038: ingo@2038: /** felix@1763: * Add StateData containing a given string. felix@1763: * @param name Name of the data object. felix@1763: * @param value String to store. felix@1763: */ felix@1724: public void addStringData(String name, String value) { felix@1724: addData(name, new DefaultStateData(name, null, null, value)); felix@1724: } felix@1724: bjoern@3920: /** bjoern@3920: * This method returns all stored StateData in this artifact as a Collection bjoern@3920: * @return a Collection of all StateData objects in this artifact bjoern@3920: */ sascha@960: public Collection getAllData() { sascha@960: return data.values(); sascha@960: } sascha@960: ingo@937: felix@4206: /** Return all produced facets. */ ingo@2095: public List getFacets() { ingo@2095: List all = new ArrayList(); ingo@2095: felix@4206: // Iterate over facets of each state. sascha@3553: for (List fs: facets.values()) { sascha@3553: all.addAll(fs); ingo@2095: } ingo@2095: ingo@2095: return all; ingo@2095: } ingo@2095: ingo@2095: felix@1771: /** felix@1771: * Get facet as stored internally, with equalling name and index than given felix@1771: * facet. felix@1771: * @param facet that defines index and name of facet searched. felix@1771: * @return facet instance or null if not found. felix@1771: */ aheinecke@6140: public Facet getNativeFacet(Facet facet, String outName) { ingo@696: String name = facet.getName(); ingo@696: int index = facet.getIndex(); aheinecke@6140: if (getBoundToOut() != null && !getBoundToOut().isEmpty() && aheinecke@6140: !getBoundToOut().equals(outName)) { aheinecke@6140: log.debug(name + ": not returning facets for " + outName + aheinecke@6140: " because bound to " + getBoundToOut()); aheinecke@6140: return null; aheinecke@6140: } aheinecke@6140: log.debug("Facet: " + facet.getName()); aheinecke@6140: log.debug("Bound to out: " + getBoundToOut()); aheinecke@6140: log.debug("OutName: " + outName); ingo@696: sascha@3553: for (List fs: facets.values()) { sascha@3553: for (Facet f: fs) { felix@1771: if (f.getIndex() == index && f.getName().equals(name)) { felix@1771: return f; felix@1771: } ingo@696: } ingo@696: } ingo@696: sascha@3554: log.warn("Could not find facet: " + name + " at " + index); bjoern@4495: log.warn("Available facets for : " + getName() + " " + identifier() + bjoern@4495: ": " + facets.values()); ingo@696: return null; ingo@696: } ingo@696: ingo@696: ingo@122: /** ingo@121: * This method stores the data that is contained in the FEED document. ingo@119: * ingo@119: * @param feed The FEED document. ingo@119: * @param xpath The XPath that points to the data nodes. ingo@119: */ sascha@3553: public void saveData(Document feed, CallContext context) ingo@121: throws IllegalArgumentException ingo@121: { sascha@3553: if (feed == null) { ingo@325: throw new IllegalArgumentException("error_feed_no_data"); ingo@121: } ingo@119: ingo@121: NodeList nodes = (NodeList) XMLUtils.xpath( ingo@121: feed, sascha@3553: XPATH_FEED_INPUT, ingo@121: XPathConstants.NODESET, ingo@121: ArtifactNamespaceContext.INSTANCE); ingo@121: ingo@121: if (nodes == null || nodes.getLength() == 0) { ingo@325: throw new IllegalArgumentException("error_feed_no_data"); ingo@121: } ingo@121: sascha@3554: boolean debug = log.isDebugEnabled(); sascha@3554: ingo@121: int count = nodes.getLength(); sascha@3554: sascha@3554: if (debug) { sascha@3554: log.debug("Try to save " + count + " data items."); sascha@3554: } ingo@121: sascha@708: String uri = ArtifactNamespaceContext.NAMESPACE_URI; ingo@121: ingo@1176: DefaultState current = (DefaultState) getCurrentState(context); ingo@1176: teichmann@5866: RiverContext flysContext = RiverUtils.getFlysContext(context); ingo@2205: StateEngine engine = (StateEngine) flysContext.get( teichmann@5866: RiverContext.STATE_ENGINE_KEY); ingo@2205: sascha@708: for (int i = 0; i < count; i++) { sascha@708: Element node = (Element)nodes.item(i); ingo@121: sascha@708: String name = node.getAttributeNS(uri, "name"); sascha@708: String value = node.getAttributeNS(uri, "value"); sascha@708: sascha@708: if (name.length() > 0 && value.length() > 0) { sascha@3554: if (debug) { sascha@3554: log.debug("Save data item for '" + name + "' : " + value); sascha@3554: } ingo@121: ingo@2205: StateData model = engine.getStateData(getName(), name); ingo@2205: ingo@2205: StateData sd = model != null ingo@2205: ? model.deepCopy() ingo@2205: : new DefaultStateData(name, null, null, value); ingo@2205: ingo@2205: addData( ingo@2205: name, current.transform(this, context, sd, name, value)); ingo@121: } ingo@1656: else if (name.length() > 0 && value.length() == 0) { sascha@3554: if (removeData(name) != null && debug) { sascha@3554: log.debug("Removed data '" + name + "' successfully."); ingo@1656: } ingo@1656: } ingo@121: } ingo@322: sascha@1050: current.validate(this); ingo@122: } ingo@122: ingo@122: ingo@122: /** ingo@122: * Determines if the state with the identifier stateId is reachable ingo@122: * from the current state. The determination itself takes place in the ingo@122: * TransitionEngine. ingo@122: * ingo@122: * @param stateId The identifier of a state. ingo@122: * @param context The context object. ingo@122: * ingo@122: * @return true, if the state specified by stateId is reacahble, ingo@122: * otherwise false. ingo@122: */ ingo@122: protected boolean isStateReachable(String stateId, Object context) { sascha@3554: sascha@3554: if (log.isDebugEnabled()) { sascha@3554: log.debug("Determine if the state '" + stateId + "' is reachable."); sascha@3554: } ingo@122: teichmann@5866: RiverContext flysContext = RiverUtils.getFlysContext(context); ingo@122: ingo@624: State currentState = getCurrentState(context); ingo@122: StateEngine sEngine = (StateEngine) flysContext.get( teichmann@5866: RiverContext.STATE_ENGINE_KEY); ingo@122: ingo@122: TransitionEngine tEngine = (TransitionEngine) flysContext.get( teichmann@5866: RiverContext.TRANSITION_ENGINE_KEY); ingo@122: ingo@355: return tEngine.isStateReachable(this, stateId, currentState, sEngine); ingo@122: } ingo@140: ingo@140: ingo@140: /** ingo@140: * Determines if the state with the identifier stateId is a previous ingo@140: * state of the current state. ingo@140: * ingo@140: * @param stateId The target state identifier. ingo@140: * @param context The context object. ingo@140: */ ingo@140: protected boolean isPreviousState(String stateId, Object context) { sascha@3554: if (log.isDebugEnabled()) { sascha@3554: log.debug("Determine if the state '" + stateId + "' is old."); sascha@3554: } ingo@140: sascha@3553: return getPreviousStateIds().contains(stateId); ingo@140: } ingo@317: ingo@317: ingo@317: /** ingo@686: * Computes the hash code of the entered values. ingo@686: * ingo@686: * @return a hash code. ingo@686: */ ingo@686: @Override ingo@686: public String hash() { ingo@686: sascha@705: long hash = 0L; sascha@705: int shift = 3; ingo@686: teichmann@4050: for (Map.Entry entry: data.entrySet()) { ingo@686: String key = entry.getKey(); ingo@686: Object value = entry.getValue().getValue(); ingo@686: sascha@742: hash ^= ((long)key.hashCode() << shift) sascha@722: | ((long)value.hashCode() << (shift + 3)); ingo@686: shift += 2; ingo@686: } ingo@686: ingo@686: return getCurrentStateId() + hash; ingo@686: } ingo@686: felix@1628: felix@1628: /** felix@1763: * Return List of outputs, where combinations of outputname and filtername felix@1763: * that match content in filterFacets is left out. felix@1763: * @return filtered Outputlist. felix@1628: */ sascha@1056: protected List filterOutputs(List outs) { sascha@1056: if (filterFacets == null || filterFacets.isEmpty()) { sascha@3554: log.debug("No filter for Outputs."); sascha@1056: return outs; sascha@1056: } sascha@1056: sascha@3554: boolean debug = log.isDebugEnabled(); sascha@3436: sascha@3436: if (debug) { sascha@3554: log.debug( sascha@3436: "Filter Facets with " + filterFacets.size() + " filters."); sascha@3436: } ingo@2095: sascha@1056: List filtered = new ArrayList(); sascha@1056: sascha@1056: for (Output out: outs) { ingo@2095: String outName = out.getName(); sascha@1056: sascha@3436: if (debug) { sascha@3554: log.debug(" filter Facets for Output: " + outName); sascha@3436: } ingo@2095: ingo@2095: List fFacets = filterFacets.get(outName); sascha@1056: if (fFacets != null) { sascha@3436: if (debug) { sascha@3554: log.debug("" + fFacets.size() + " filters for: " + outName); ingo@2095: for (Facet tmp: fFacets) { sascha@3554: log.debug(" filter = '" + tmp.getName() + "'"); ingo@2095: } ingo@2095: } sascha@1056: sascha@1056: List resultFacets = new ArrayList(); sascha@1056: sascha@1056: for (Facet facet: out.getFacets()) { sascha@1056: for (Facet fFacet: fFacets) { sascha@1056: if (facet.getIndex() == fFacet.getIndex() sascha@1056: && facet.getName().equals(fFacet.getName())) { sascha@1056: resultFacets.add(facet); sascha@1056: break; sascha@1056: } sascha@1056: } sascha@1056: } sascha@1056: sascha@3436: if (debug) { sascha@3554: log.debug( sascha@3436: "Facets after filtering = " + resultFacets.size()); sascha@3436: } ingo@2095: sascha@1056: if (!resultFacets.isEmpty()) { sascha@1056: DefaultOutput nout = new DefaultOutput( sascha@1056: out.getName(), sascha@1056: out.getDescription(), sascha@1056: out.getMimeType(), sascha@1056: resultFacets); sascha@1056: filtered.add(nout); sascha@1056: } sascha@1056: } bjoern@4403: else if (debug) { bjoern@4403: log.debug("No filter Factes for Output: " + outName); bjoern@4403: } sascha@1056: } sascha@1056: sascha@3436: if (debug) { bjoern@4403: log.debug("Number of outs after filtering = " + filtered.size()); sascha@3436: } ingo@2095: sascha@1056: return filtered; sascha@1056: } sascha@1056: ingo@686: felix@1765: /** felix@1765: * Get all outputs that the Artifact can do in this state (which includes felix@1765: * all previous states). felix@1765: * felix@1765: * @return list of outputs felix@1765: */ sascha@1050: public List getOutputs(Object context) { sascha@3554: if (log.isDebugEnabled()) { sascha@3554: log.debug("##### Get Outputs for: " + identifier() + " #####"); sascha@3554: dumpArtifact(); sascha@3436: } ingo@2095: ingo@965: List stateIds = getPreviousStateIds(); ingo@944: List generated = new ArrayList(); ingo@944: ingo@944: for (String stateId: stateIds) { felix@1067: DefaultState state = (DefaultState) getState(context, stateId); sascha@981: generated.addAll(getOutputForState(state)); ingo@944: } ingo@944: ingo@965: generated.addAll(getCurrentOutputs(context)); ingo@944: sascha@1056: return filterOutputs(generated); ingo@944: } ingo@944: ingo@944: felix@1765: /** felix@1765: * Get output(s) for current state. felix@1765: * @return list of outputs for current state. felix@1765: */ sascha@1050: public List getCurrentOutputs(Object context) { ingo@965: DefaultState cur = (DefaultState) getCurrentState(context); ingo@965: ingo@965: try { sascha@1050: if (cur.validate(this)) { sascha@981: return getOutputForState(cur); ingo@965: } ingo@965: } ingo@965: catch (IllegalArgumentException iae) { } ingo@965: ingo@965: return new ArrayList(); ingo@965: } ingo@965: felix@1137: felix@1765: /** felix@1765: * Get output(s) for a specific state. felix@1765: * @param state State of interest felix@1765: * @return list of output(s) for given state. felix@1765: */ sascha@981: protected List getOutputForState(DefaultState state) { sascha@3436: sascha@3436: if (state == null) { sascha@3554: log.error("state == null: This should not happen!"); sascha@3436: return new ArrayList(); sascha@3436: } sascha@3436: sascha@3554: boolean debug = log.isDebugEnabled(); sascha@3436: sascha@3436: if (debug) { sascha@3554: log.debug("Find Outputs for State: " + state.getID()); sascha@3436: } ingo@2095: ingo@965: List list = state.getOutputs(); sascha@3555: if (list == null || list.isEmpty()) { sascha@3436: if (debug) { sascha@3554: log.debug("-> No output modes for this state."); sascha@3436: } ingo@965: return new ArrayList(); ingo@965: } ingo@965: ingo@2095: String stateId = state.getID(); ingo@2095: bjoern@4497: List fs = getFacets(stateId); felix@1771: sascha@3555: if (fs == null || fs.isEmpty()) { sascha@3436: if (debug) { sascha@3554: log.debug("No facets found."); sascha@3436: } ingo@965: return new ArrayList(); ingo@965: } bjoern@4495: if (debug) { bjoern@4495: log.debug("State '" + stateId + "' has facets " + fs); bjoern@4495: } ingo@965: aheinecke@7593: List gen = generateOutputs(list, applyGlobalFilterFacets(fs)); ingo@2095: sascha@3436: if (debug) { sascha@3554: log.debug("State '" + stateId + "' has " + gen.size() + " outs"); sascha@3436: } ingo@2095: ingo@2095: return gen; ingo@965: } ingo@965: aheinecke@7244: /** If we use a facet filter that bases the list of compatible facets felix@7676: * on the output this artifact is bound to then return true. */ aheinecke@7593: public boolean usesOutputGlobalFacetFilter() { aheinecke@7244: if (boundToOut == null || boundToOut.isEmpty()) { aheinecke@7244: return false; aheinecke@7244: } aheinecke@7244: aheinecke@7244: FacetFilter facetFilter = aheinecke@7244: (FacetFilter)RiverContextFactory.getGlobalContext() aheinecke@7244: .get(RiverContext.FACETFILTER_KEY); aheinecke@7244: teichmann@7245: return facetFilter != null; aheinecke@7244: } aheinecke@7244: felix@7676: /** felix@7676: * If a global facet filter and a bounded out are defined felix@7676: * use them to eliminate unwished facets. teichmann@7227: */ aheinecke@7593: protected List applyGlobalFilterFacets(List facets) { aheinecke@7593: if (!usesOutputGlobalFacetFilter()) { teichmann@7227: return facets; teichmann@7227: } teichmann@7227: teichmann@7227: FacetFilter facetFilter = teichmann@7227: (FacetFilter)RiverContextFactory.getGlobalContext() teichmann@7227: .get(RiverContext.FACETFILTER_KEY); teichmann@7227: teichmann@7227: List result = new ArrayList(facets.size()); teichmann@7227: for (Facet facet: facets) { teichmann@7227: if (facetFilter.accept(boundToOut, facet.getName())) { teichmann@7227: result.add(facet); teichmann@7227: } teichmann@7227: } teichmann@7227: return result; teichmann@7227: } ingo@965: felix@1763: /** felix@1763: * Generate a list of outputs with facets from fs if type is found in list felix@1763: * of output. felix@1763: * felix@1763: * @param list List of outputs felix@1763: * @param fs List of facets felix@1763: */ ingo@937: protected List generateOutputs(List list, List fs) { sascha@3554: boolean debug = log.isDebugEnabled(); ingo@937: teichmann@7245: List generated = new ArrayList(); teichmann@7245: teichmann@7245: if (debug) { teichmann@7245: log.debug("generateOutputs for Artifact " + getName() + " " teichmann@7245: + identifier()); teichmann@7245: } teichmann@7245: aheinecke@7593: boolean useFacetFilter = usesOutputGlobalFacetFilter(); teichmann@7245: ingo@937: for (Output out: list) { teichmann@7245: if (debug) { teichmann@7245: log.debug("check facets for output: " + out.getName()); teichmann@7245: } aheinecke@7178: String outName = out.getName(); ingo@937: Output o = new DefaultOutput( aheinecke@7178: outName, ingo@937: out.getDescription(), ingo@937: out.getMimeType(), ingo@937: out.getType()); ingo@937: ingo@937: Set outTypes = new HashSet(); ingo@937: ingo@937: for (Facet f: out.getFacets()) { ingo@937: if (outTypes.add(f.getName()) && debug) { sascha@3554: log.debug("configured facet " + f); ingo@937: } ingo@937: } ingo@937: ingo@937: boolean facetAdded = false; ingo@937: for (Facet f: fs) { ingo@937: String type = f.getName(); ingo@937: aheinecke@7178: /* Match the facets to the output configuration. aheinecke@7244: * This is only done when we are not using the Output aheinecke@7244: * we are bound to to determine the compatible facets. */ teichmann@7245: if (useFacetFilter || outTypes.contains(type)) { ingo@937: if (debug) { sascha@3554: log.debug("Add facet " + f); ingo@937: } ingo@937: facetAdded = true; ingo@937: o.addFacet(f); ingo@937: } ingo@937: } ingo@937: ingo@937: if (facetAdded) { ingo@937: generated.add(o); ingo@937: } ingo@937: } ingo@937: ingo@937: return generated; ingo@937: } ingo@937: ingo@937: ingo@689: /** ingo@689: * Dispatches the computation request to compute(CallContext context, String ingo@689: * hash) with the current hash value of the artifact which is provided by ingo@689: * hash(). ingo@689: * ingo@689: * @param context The CallContext. ingo@689: */ sascha@705: public Object compute( sascha@742: CallContext context, sascha@742: ComputeType type, sascha@705: boolean generateFacets sascha@705: ) { sascha@705: return compute(context, hash(), type, generateFacets); ingo@689: } ingo@689: ingo@689: ingo@689: /** ingo@689: * Dispatches computation requests to the current state which needs to teichmann@5867: * implement a createComputeCallback(String hash, D4EArtifact artifact) ingo@689: * method. ingo@689: * ingo@689: * @param context The CallContext. ingo@689: * @param hash The hash value which is used to fetch computed data from ingo@689: * cache. ingo@689: * ingo@689: * @return the computed data. ingo@689: */ sascha@705: public Object compute( sascha@705: CallContext context, sascha@705: String hash, sascha@742: ComputeType type, sascha@705: boolean generateFacets sascha@705: ) { ingo@693: DefaultState current = (DefaultState) getCurrentState(context); sascha@722: return compute(context, hash, current, type, generateFacets); sascha@722: } ingo@689: felix@1704: felix@1704: /** felix@7676: * Like compute, but identify State by its id (string). felix@1704: */ sascha@722: public Object compute( sascha@722: CallContext context, ingo@1117: String hash, sascha@722: String stateID, sascha@742: ComputeType type, sascha@722: boolean generateFacets sascha@722: ) { felix@2141: DefaultState current = felix@2141: (stateID == null) sascha@722: ? (DefaultState)getCurrentState(context) sascha@722: : (DefaultState)getState(context, stateID); sascha@722: sascha@722: if (hash == null) { sascha@722: hash = hash(); sascha@722: } ingo@689: sascha@705: return compute(context, hash, current, type, generateFacets); ingo@689: } ingo@689: felix@1137: felix@1137: /** felix@1765: * Let current state compute and register facets. felix@1765: * felix@1137: * @param key key of state felix@1137: * @param state state felix@1137: * @param type Type of compute felix@1137: * @param generateFacets Whether new facets shall be generated. felix@1137: */ ingo@690: public Object compute( sascha@697: CallContext context, sascha@697: String key, sascha@697: DefaultState state, sascha@705: ComputeType type, sascha@705: boolean generateFacets sascha@697: ) { sascha@697: String stateID = state.getID(); ingo@691: felix@1137: List fs = (generateFacets) ? new ArrayList() : null; sascha@697: sascha@697: try { sascha@697: Cache cache = CacheFactory.getCache(COMPUTING_CACHE); sascha@697: sascha@697: Object old = null; sascha@697: sascha@697: if (cache != null) { sascha@697: net.sf.ehcache.Element element = cache.get(key); sascha@697: if (element != null) { sascha@3554: log.debug("Got computation result from cache."); sascha@697: old = element.getValue(); sascha@697: } sascha@697: } sascha@3203: else { sascha@3554: log.debug("cache not configured."); sascha@3203: } sascha@697: sascha@697: Object res; ingo@690: switch (type) { ingo@690: case FEED: sascha@697: res = state.computeFeed(this, key, context, fs, old); sascha@697: break; sascha@697: case ADVANCE: sascha@700: res = state.computeAdvance(this, key, context, fs, old); sascha@697: break; ingo@942: case INIT: ingo@958: res = state.computeInit(this, key, context, context.getMeta(), fs); sascha@697: default: sascha@697: res = null; sascha@697: } ingo@690: sascha@697: if (cache != null && old != res && res != null) { sascha@3554: log.debug("Store computation result to cache."); sascha@697: net.sf.ehcache.Element element = sascha@697: new net.sf.ehcache.Element(key, res); sascha@697: cache.put(element); sascha@697: } sascha@697: sascha@697: return res; sascha@697: } sascha@697: finally { sascha@705: if (generateFacets) { sascha@705: if (fs.isEmpty()) { felix@1771: facets.remove(stateID); sascha@705: } sascha@705: else { bjoern@3934: addFacets(stateID, fs); sascha@705: } ingo@690: } ingo@686: } ingo@686: } ingo@686: bjoern@3934: /** felix@7676: * Sets the facets for an ID, which is normally a state ID. bjoern@3934: * bjoern@3934: * @param id ID to map the facets to bjoern@3934: * @param facets List of facets to be stored bjoern@3934: */ bjoern@3934: protected void addFacets(String id, List facets) { aheinecke@6140: for (Facet fac : facets) { aheinecke@6140: fac.setBoundToOut(getBoundToOut()); aheinecke@6140: } bjoern@3934: this.facets.put(id, facets); bjoern@3934: } bjoern@3934: ingo@686: ingo@623: /** ingo@623: * Method to dump the artifacts state/data. ingo@623: */ aheinecke@6856: public void dumpArtifact() { sascha@3554: log.debug("++++++++++++++ DUMP ARTIFACT DATA +++++++++++++++++"); sascha@3554: // Include uuid, type, name bjoern@4495: log.debug(" - Name: " + getName()); bjoern@4495: log.debug(" - UUID: " + identifier()); aheinecke@6140: log.debug(" - Class: " + getClass().getName()); aheinecke@6140: log.debug(" - BoundToOut: " + getBoundToOut()); ingo@623: sascha@3554: log.debug("------ DUMP DATA ------"); sascha@3554: Collection allData = data.values(); ingo@623: sascha@3554: for (StateData d: allData) { sascha@3554: String name = d.getName(); sascha@3554: String value = (String) d.getValue(); ingo@623: sascha@3554: log.debug("- " + name + ": " + value); sascha@3554: } ingo@2095: sascha@3554: log.debug("------ DUMP PREVIOUS STATES ------"); sascha@3554: List stateIds = getPreviousStateIds(); sascha@3554: sascha@3554: for (String id: stateIds) { sascha@3554: log.debug("- State: " + id); ingo@623: } sascha@3554: sascha@3554: log.debug("CURRENT STATE: " + getCurrentStateId()); sascha@3554: sascha@3554: debugFacets(); sascha@3554: dumpFilterFacets(); sascha@3554: sascha@3554: log.debug("++++++++++++++ END ARTIFACT DUMP +++++++++++++++++"); ingo@623: } ingo@1086: ingo@1086: aheinecke@6856: public void debugFacets() { sascha@3554: log.debug("######### FACETS #########"); ingo@2095: teichmann@4050: for (Map.Entry> entry: facets.entrySet()) { ingo@2095: String out = entry.getKey(); ingo@2095: List fs = entry.getValue(); ingo@2095: for (Facet f: fs) { sascha@3554: log.debug(" # " + out + " : " + f.getName()); aheinecke@6140: log.debug(" # boundToOut : " + f.getBoundToOut()); ingo@2095: } ingo@2095: } ingo@2095: sascha@3554: log.debug("######## FACETS END ########"); ingo@2095: } ingo@2095: ingo@2095: aheinecke@6856: public void dumpFilterFacets() { sascha@3554: log.debug("######## FILTER FACETS ########"); ingo@2095: ingo@2095: if (filterFacets == null || filterFacets.isEmpty()) { sascha@3554: log.debug("No Filter Facets defined."); ingo@2095: return; ingo@2095: } ingo@2095: teichmann@4050: for (Map.Entry> entry: filterFacets.entrySet()) { ingo@2095: String out = entry.getKey(); ingo@2095: List filters = entry.getValue(); ingo@2095: felix@7746: log.debug("There are " + filters.size() + " filters for: " + out); ingo@2095: ingo@2095: for (Facet filter: filters) { sascha@3554: log.debug(" filter: " + filter.getName()); ingo@2095: } ingo@2095: } ingo@2095: sascha@3554: log.debug("######## FILTER FACETS END ########"); ingo@2095: } ingo@2095: ingo@2095: felix@4206: /** Destroy and clean up state with given id. */ ingo@1094: protected void destroyState(String id, Object context) { ingo@1094: State s = getState(context, id); ingo@1094: s.endOfLife(this, context); ingo@1094: } ingo@1094: ingo@1094: ingo@1086: /** ingo@1086: * Calls endOfLife() for each state in the list ids. ingo@1086: * ingo@1086: * @param ids The State IDs that should be destroyed. teichmann@5866: * @param context The RiverContext. ingo@1086: */ ingo@1086: protected void destroyStates(List ids, Object context) { ingo@1086: for (int i = 0, num = ids.size(); i < num; i++) { ingo@1094: destroyState(ids.get(i), context); ingo@1086: } ingo@1086: } ingo@1086: ingo@1086: felix@1763: /** felix@1763: * Destroy the states. felix@1763: */ ingo@1086: @Override ingo@1086: public void endOfLife(Object context) { sascha@3554: if (log.isDebugEnabled()) { teichmann@5867: log.debug("D4EArtifact.endOfLife: " + identifier()); sascha@3436: } ingo@1086: ingo@2606: ArrayList ids = (ArrayList) getPreviousStateIds(); ingo@2606: ArrayList toDestroy = (ArrayList) ids.clone(); ingo@1086: ingo@2606: toDestroy.add(getCurrentStateId()); ingo@2606: ingo@2606: destroyStates(toDestroy, context); ingo@1086: } bjoern@4496: bjoern@4496: /** felix@7676: * Return the Facets which a state provides. bjoern@4496: * @param stateid String that identifies the state bjoern@4496: * @return List of Facets belonging to the state identifier bjoern@4496: */ bjoern@4496: protected List getFacets(String stateid) { aheinecke@6140: return facets.get(stateid); bjoern@4496: } aheinecke@5975: aheinecke@5975: public String getBoundToOut() { aheinecke@5975: return boundToOut; aheinecke@5975: } aheinecke@6140: aheinecke@6140: /** felix@7676: * Binds this artifact and all its facet to an out. aheinecke@6140: */ aheinecke@6140: public void setBoundToOut(String out) { aheinecke@6140: boundToOut = out; aheinecke@6140: for (List stateFacets: facets.values()) { aheinecke@6140: for (Facet fac: stateFacets) { aheinecke@6140: fac.setBoundToOut(out); aheinecke@6140: } aheinecke@6140: } aheinecke@6140: } ingo@119: } ingo@119: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :