/* Copyright 2014 MITRE Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.mitre.provenance.workflowengine; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.logging.Logger; import org.mitre.provenance.db.neo4j.Neo4JStorage; import org.mitre.provenance.plusobject.PLUSActivity; import org.mitre.provenance.plusobject.PLUSActor; import org.mitre.provenance.plusobject.PLUSEdge; import org.mitre.provenance.plusobject.PLUSInvocation; import org.mitre.provenance.plusobject.PLUSObject; import org.mitre.provenance.plusobject.PLUSWorkflow; import org.mitre.provenance.plusobject.ProvenanceCollection; import org.mitre.provenance.surrogate.SurrogateGeneratingFunction; import org.mitre.provenance.user.PrivilegeSet; import org.mitre.provenance.workflowengine.activity.Activity; /** * This object implements the lineage tracer API and is used for capturing information about workflows * that are being executed. The captured information is stored in an PLUS-specific schema within a * relational database. * @author moxious * */ public class RelationalLineageCapture extends LineageTracer { protected Logger log = Logger.getLogger(RelationalLineageCapture.class.getName()); protected int workflowID; protected Hashtable <String,PLUSObject> currentInputs; protected PLUSInvocation curInvoke; protected Date start; protected Date end; protected boolean writeImmediate = false; protected PLUSWorkflow tracing; protected ProvenanceCollection col; public RelationalLineageCapture() { col = new ProvenanceCollection(); currentInputs = null; workflowID = -1; } // End RelationalLineageCapture /** * Get a provenance collection that contains all objects captured by this tracer so far. * @return a ProvenanceCollection */ public ProvenanceCollection getCollection() { return col; } /** * Controls whether objects are immediately written to the database as soon as they * are observed. Set to true if you want to write things as they're found; set to * false if you intend to pull the entire provenance collection out at a later time. * @param writeImmediate */ public void setWriteImmediate(boolean writeImmediate) { this.writeImmediate = writeImmediate; } /** * @see LineageTracer#startWorkflow(Workflow, Activity) */ public void startWorkflow(Workflow wf, Activity startingPoint) { start = new Date(); tracing = new PLUSWorkflow(); tracing.setName(wf.getName()); tracing.setWhenStart(start.toString()); } // End startWorkflowRun /** * @see LineageTracer#endWorkflow(Workflow, Activity) */ public void endWorkflow(Workflow wf, Activity endingPoint) { end = new Date(); tracing.setWhenEnd(end.toString()); try { col.addNode(tracing); if(writeImmediate) Neo4JStorage.store(tracing); else log.finest("Skipping write on workflow " + tracing.getName()); } catch(Exception e) { log.severe("RelationalLineageCapture.endWorkflow: " + e.getMessage()); e.printStackTrace(); } } // End endWorkflow /** * @see LineageTracer#startActivity(Activity, Hashtable) */ public void startActivity(Activity current, Hashtable <String,PLUSObject> parameters) { // Don't need to do anything here except keep a reference to the inputs for later ... currentInputs = parameters; curInvoke = new PLUSInvocation(current.getName()); curInvoke.setId((String)current.getMetadata().get("invokeid")); Enumeration<String> e = parameters.keys(); while(e.hasMoreElements()) { String n = (String)e.nextElement(); PLUSObject input = parameters.get(n); try { col.addNode(input); if(writeImmediate) Neo4JStorage.store(input); else log.finest("Skipping data write for " + input); } catch(Exception exc) { log.severe("RelationalLineageCapture.finishActivity: (data logging) " + exc); } curInvoke.addInput(n, input.getId()); PLUSEdge edge = new PLUSEdge(input, curInvoke, tracing, PLUSEdge.EDGE_TYPE_INPUT_TO); try { col.addEdge(edge); if(writeImmediate) Neo4JStorage.store(edge); else log.finest("Skipping edge write on " + edge); } catch(Exception exc) { log.severe("startActivity: Error writing edge: " + exc); exc.printStackTrace(); } // End catch } // End while } // End startActivity /** * @see LineageTracer#transition(Transition) */ public void transition(Transition t) { Activity from = t.getFrom(); Activity to = t.getTo(); String fromID = (String)from.getMetadata().get("invokeid"); String toID = (String)to.getMetadata().get("invokeid"); // Don't log START and END. /* if(from != Activity.START_STATE && to != Activity.END_STATE) { PLUSEdge edge = new PLUSEdge(fromID, toID, tracing.getId(), PLUSEdge.EDGE_TYPE_TRIGGERED); try { col.addEdge(edge); if(writeImmediate && !edge.wasWritten()) edge.writeToDB(); else log.finest("Skipping write on edge " + edge); } catch(Exception e) { log.severe("RelationalLineageCapture.transition: " + e, e); } } // End if */ } // End transition /** * @see LineageTracer#finishActivity(Activity, Hashtable) */ public void finishActivity(Activity current, Hashtable <String,PLUSObject> results) { String activityID = (String)current.getMetadata().get("id"); String invokeID = (String)current.getMetadata().get("invokeid"); PLUSActivity pact = new PLUSActivity(current.getName()); pact.setId(activityID); pact.setInputs(currentInputs.size()); pact.setOutputs(results.size()); pact.setDescription("None available."); if(current.getMetadata().get("PrivSet") != null) { try { PrivilegeSet ps = (PrivilegeSet)current.getMetadata().get("PrivSet"); log.finest("Tagged activity with PrivSet " + ps); curInvoke.setPrivileges(ps); } catch(Exception e) { log.warning("Activity had bad PrivSet tag: " + current.getMetadata().get("PrivSet") + "/" + e.getMessage()); } } if(current.getMetadata().get("actor") != null) { try { PLUSActor actor = (PLUSActor)current.getMetadata().get("actor"); log.finest("Tagged activity with actor " + actor); curInvoke.setOwner(actor); } catch(Exception e) { log.severe("Activity had bad actor tag: " + current.getMetadata().get("actor")); e.printStackTrace(); } // End catch } // End if if(current.getMetadata().get("SGF") != null) { try { SurrogateGeneratingFunction c = (SurrogateGeneratingFunction)current.getMetadata().get("SGF"); log.finest("Tagged activity with SGF " + c); curInvoke.useSurrogateComputation(c); } catch(Exception e) { log.severe("Activity had bad SGF tag: " + current.getMetadata().get("SGF")); } } // End if Enumeration<String> e = results.keys(); while(e.hasMoreElements()) { String inName = (String)e.nextElement(); PLUSObject result = results.get(inName); curInvoke.addOutput(inName, result.getId()); try { col.addNode(result); if(writeImmediate) Neo4JStorage.store(result); else log.finest("Skipping data write for " + result); } catch(Exception exc) { log.severe("finishActivity: (data logging) " + exc.getMessage()); } PLUSEdge edge = new PLUSEdge(curInvoke, result, tracing, PLUSEdge.EDGE_TYPE_GENERATED); try { col.addEdge(edge); if(writeImmediate) Neo4JStorage.store(edge); } catch(Exception exc) { log.severe("finishActivity: Failed writing edge " + exc.getMessage()); } // End catch } // End while // Write the activity.... try { col.addNode(pact); if(writeImmediate) Neo4JStorage.store(pact); else log.finest("finishActivity: Skipping write on activity " + pact.getName()); } catch(Exception exc) { log.severe("RelationalLineageCapture.finishActivity: " + exc.getMessage()); } // End catch // Finish up setting some fields so that things line up properly in the DB curInvoke.setId(invokeID); curInvoke.setActivity(pact); curInvoke.setWorkflow(tracing); curInvoke.getMetadata().copy(current.getMetadata()); // Write the invocation. try { col.addNode(curInvoke); if(writeImmediate) Neo4JStorage.store(curInvoke); else log.finest("finishActivity: Skipping write on invocation " + curInvoke.getName()); } catch(Exception exc) { log.severe(exc.getMessage()); } } // End finishActivity } // End RelationalLineageCapture