/*
* 2012-3 Red Hat Inc. and/or its affiliates and other contributors.
*
* 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.overlord.rtgov.call.trace;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.inject.Singleton;
import javax.transaction.UserTransaction;
import org.overlord.rtgov.activity.model.ActivityType;
import org.overlord.rtgov.activity.model.ActivityUnit;
import org.overlord.rtgov.activity.model.Context;
import org.overlord.rtgov.activity.model.app.LogMessage;
import org.overlord.rtgov.activity.model.soa.RPCActivityType;
import org.overlord.rtgov.activity.model.soa.RequestReceived;
import org.overlord.rtgov.activity.model.soa.RequestSent;
import org.overlord.rtgov.activity.model.soa.ResponseReceived;
import org.overlord.rtgov.activity.model.soa.ResponseSent;
import org.overlord.rtgov.activity.server.ActivityServer;
import org.overlord.rtgov.call.trace.descriptors.TaskDescriptorFactory;
import org.overlord.rtgov.call.trace.model.Call;
import org.overlord.rtgov.call.trace.model.CallTrace;
import org.overlord.rtgov.call.trace.model.Task;
import org.overlord.rtgov.call.trace.model.TraceNode;
import org.overlord.rtgov.call.trace.model.TraceNode.Status;
import org.overlord.rtgov.call.trace.util.CallTraceUtil;
/**
* This class is responsible for deriving a call trace from
* activity information.
*
*/
@Singleton
public class CallTraceServiceImpl implements CallTraceService {
private static final Logger LOG=Logger.getLogger(CallTraceServiceImpl.class.getName());
private static final long DEFAULT_TIMEFRAME=10000; // 10 seconds
@Resource
private UserTransaction _tx;
private ActivityServer _activityServer=null;
/**
* This method sets the activity server.
*
* @param as The activity server
*/
public void setActivityServer(ActivityServer as) {
_activityServer = as;
}
/**
* This method gets the activity server.
*
* @return The activity server
*/
public ActivityServer getActivityServer() {
return (_activityServer);
}
/**
* This method starts the transaction.
*
* @return Whether the transaction has been started
* @throws Exception Failed to start txn
*/
protected boolean startTxn() throws Exception {
boolean ret=false;
if (_tx != null && _tx.getStatus()
== javax.transaction.Status.STATUS_NO_TRANSACTION) {
_tx.begin();
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Started transaction");
}
ret = true;
}
return (ret);
}
/**
* This method commits the transaction.
*
* @throws Exception Failed to commit
*/
protected void commitTxn() throws Exception {
try {
_tx.commit();
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Committed transaction");
}
} catch (Exception e) {
_tx.rollback();
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Rolling back transaction: exception="+e);
}
throw e;
}
}
/**
* This method rolls back the transaction.
*
* @throws Exception Failed to rollback
*/
protected void rollbackTxn() throws Exception {
try {
_tx.rollback();
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Rolled back transaction");
}
} catch (Exception e) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Failed to roll back transaction: exception="+e);
}
throw e;
}
}
/**
* This method creates a call trace associated with the
* supplied correlation value.
*
* @param context The context value
* @return The call trace, or null if not found
* @throws Exception Failed to create call trace
*/
public CallTrace createCallTrace(Context context)
throws Exception {
boolean f_txnStarted=startTxn();
CallTrace ret=null;
try {
CTState state=new CTState();
// Recursively load activity units that are directly or
// indirectly associated with the context
loadActivityUnits(state, context, null);
ret = processAUs(state);
if (f_txnStarted) {
commitTxn();
}
} catch (Exception e) {
if (f_txnStarted) {
rollbackTxn();
}
throw e;
}
return (ret);
}
/**
* This method loads activity units associated with the supplied
* correlation key.
*
* @param state The state
* @param context The context
* @param actType The activity type
*/
protected void loadActivityUnits(CTState state, Context context, ActivityType actType) {
if (!state.isContextInitialized(context)) {
// Retrieve activity types associated with correlation
try {
long fromTimestamp=0;
long toTimestamp=0;
if (actType != null) {
if (context.linkSource()) {
fromTimestamp = actType.getTimestamp()-(context.getTimeframe()/2);
toTimestamp = actType.getTimestamp()+(context.getTimeframe()/2);
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Linked context: base timestamp="+actType.getTimestamp()
+" timeframe="+context.getTimeframe()
+" from="+fromTimestamp
+" to="+toTimestamp);
}
} else if (context.linkTarget()) {
// Find a default from/to range
fromTimestamp = actType.getTimestamp()-(DEFAULT_TIMEFRAME/2);
toTimestamp = actType.getTimestamp()+(DEFAULT_TIMEFRAME/2);
}
}
java.util.List<ActivityType> ats=
_activityServer.getActivityTypes(context, fromTimestamp, toTimestamp);
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Retrieved activity types for context="+context
+" from="+fromTimestamp+" to="+toTimestamp+": "+ats);
}
// Check each activity type's unit id to see whether
// it needs to be retrieved
java.util.List<ActivityUnit> aus=
new java.util.ArrayList<ActivityUnit>();
for (ActivityType at : ats) {
if (!state.isActivityUnitLoaded(at.getUnitId())) {
ActivityUnit au=_activityServer.getActivityUnit(at.getUnitId());
// Check if link target
if (context.linkTarget()) {
// Ensure that AUs are only included if they contain the
// link source, and that the timeframe of that link source
// is valid for this target
for (Context c : at.getContext()) {
if (c.linkSource() && c.equals(context)) {
// Check that the activity is in the right timeframe
long timediff=Math.abs(at.getTimestamp()-actType.getTimestamp());
if (timediff <= (c.getTimeframe()/2)) {
aus.add(au);
// Add to state
state.add(au);
break;
}
}
}
} else {
aus.add(au);
// Add to state
state.add(au);
}
}
}
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Retrieved activity units: "+aus);
}
// Mark this context as initialized
state.initialized(context);
// For each new activity unit, scan for unknown correlation
// fields, and recursively load their associated units
for (ActivityUnit au : aus) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.log(Level.FINEST, "Scanning Activity Unit="+au);
}
for (ActivityType at : au.getActivityTypes()) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.log(Level.FINEST, "Scanning Activity Type="+at);
}
for (Context c : at.getContext()) {
loadActivityUnits(state, c, at);
}
}
}
} catch (Exception e) {
LOG.log(Level.SEVERE, MessageFormat.format(
java.util.PropertyResourceBundle.getBundle(
"call-trace.Messages").getString("CALL-TRACE-1"),
context), e);
}
}
}
/**
* This method processes the supplied call trace state
* to return the call trace model.
*
* @param state The state
* @return The model
*/
protected static CallTrace processAUs(CTState state) {
CallTrace ret=new CallTrace();
java.util.List<ActivityUnit> topLevel=getTopLevelAUs(state);
state.getTasksStack().push(ret.getTasks());
for (ActivityUnit au : topLevel) {
processAU(state, au, topLevel);
}
state.finalizeScope();
// Iterate over call trace to set the percentages
long duration=0;
// First calculate overall duration of top level nodes
for (TraceNode tn : ret.getTasks()) {
duration += tn.getDuration();
}
// Update percentage for each top level node
for (TraceNode tn : ret.getTasks()) {
initPercentages(duration, tn);
}
return (ret);
}
/**
* This method initializes the percentages related to the
* supplied trace node, traversing the tree structure if
* appropriate.
*
* @param duration The parent duration
* @param node The node
*/
protected static void initPercentages(long duration, TraceNode node) {
if (node.getDuration() > 0 && duration > 0) {
node.setPercentage((int)(((double)node.getDuration()/duration) * 100));
}
if (node instanceof Call) {
for (TraceNode tn : ((Call)node).getTasks()) {
initPercentages(node.getDuration(), tn);
}
}
}
/**
* This method processes the supplied activity unit to
* create a set of trace nodes.
*
* @param state The state
* @param startau The activity unit being processed
* @param topLevel The top level activity units
*/
protected static void processAU(CTState state, ActivityUnit startau,
java.util.List<ActivityUnit> topLevel) {
ActivityType cur=null;
Call call=(state.getCallStack().size() > 0 ? state.getCallStack().peek() : null);
java.util.List<TraceNode> tasks=(state.getTasksStack().size() > 0 ? state.getTasksStack().peek() : null);
ActivityType prev=null;
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Start Process Initial AU="+startau);
}
java.util.List<ActivityUnit> aus=state.getActivityUnits();
int aupos=aus.indexOf(startau);
if (aupos == -1) {
LOG.severe("Failed to find activity unit in list="+startau);
return;
}
boolean f_end=false;
boolean f_scopeFinalized=false;
// Process a sequence of activity units, starting with the one supplied,
// but skipping any that are listed in the top level collection.
// Break out of the sequence when a response sent is detected.
for (int i=aupos; !f_end && i < aus.size(); i++) {
ActivityUnit au=aus.get(i);
if (i != aupos) {
if (topLevel.contains(au)) {
// Skip top level units
continue;
} else if (!processSubsequentAU(startau, au)) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("AU="+au+" has non-Endpoint context values, "
+"so should only be processed when linked by those contexts");
}
f_scopeFinalized = true;
break;
}
}
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Process AU="+aus.get(i));
}
ActivityUnitCursor cursor=state.getCursor(au);
while ((cur=cursor.next()) != null) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Processing cur="+cur);
}
if (shouldPostpone(state, au, topLevel, cur)) {
break;
}
if (cur instanceof RPCActivityType) {
if (cur instanceof RequestSent || (cur instanceof RequestReceived && call == null)) {
// Create call, and search for activity unit
// containing scoped tasks
call = createCall((RPCActivityType)cur);
if (tasks != null) {
tasks.add(call);
} else if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Attempt to add call node to 'null' tasks list");
}
tasks = call.getTasks();
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Pushing call="+call);
LOG.finest("Pushing tasks="+tasks);
}
state.getCallStack().push(call);
state.getTasksStack().push(tasks);
state.getTriggerActivities().put(call, (RPCActivityType)cur);
}
if (cur instanceof RequestSent) {
instrumentCall(call, au, (RequestSent)cur);
RPCActivityType rr=state.getSOAActivity(RequestReceived.class,
((RequestSent)cur).getServiceType(),
((RequestSent)cur).getOperation());
if (rr != null) {
call.setRequestLatency(rr.getTimestamp()-cur.getTimestamp());
ActivityUnit subAU=state.getActivityUnit(rr.getUnitId());
if (subAU != null) {
processAU(state, subAU, topLevel);
call = (state.getCallStack().size() > 0 ? state.getCallStack().peek() : null);
tasks = (state.getTasksStack().size() > 0 ? state.getTasksStack().peek() : null);
}
}
} else if (cur instanceof RequestReceived) {
instrumentCall(call, au, (RequestReceived)cur);
call.setRequest(((RequestReceived)cur).getContent());
} else if (cur instanceof ResponseSent) {
initializeResponseSent(state, (ResponseSent)cur, call);
// Finalise the tasks in the scope, and pop the stack
state.finalizeScope();
f_scopeFinalized = true;
// Get new values
call = (state.getCallStack().size() > 0 ? state.getCallStack().peek() : null);
tasks = (state.getTasksStack().size() > 0 ? state.getTasksStack().peek() : null);
// If not top level call, then break out
// of loop, to finish processing the scope
if (state.getCallStack().size() > 0) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Break on response sent");
}
// Break out of processing the cursor, and also the method
f_end = true;
break;
}
} else if (cur instanceof ResponseReceived) {
initializeResponseReceived(state, (ResponseReceived)cur);
// Set end flag, to break out of this method once
// this cursor has finished
f_end = true;
}
} else {
Task task=createTask(cur);
tasks.add(task);
if (prev != null) {
task.setDuration(cur.getTimestamp()-prev.getTimestamp());
}
}
checkForLinkedAU(cur, state, au, topLevel);
prev = cur;
}
}
if (!f_scopeFinalized) {
state.finalizeScope();
}
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Finished Process Initial AU="+startau);
}
}
/**
* This method checks whether there is a linked context.
*
* @param cur The current activity type
* @param state The state
* @param au The activity unit
* @param topLevel The list of top level activity units
*/
protected static void checkForLinkedAU(ActivityType cur, CTState state, ActivityUnit au,
java.util.List<ActivityUnit> topLevel) {
// Check for linked activity units
for (Context con : cur.getContext()) {
if (con.getType() == Context.Type.Link
&& !state.isLinkProcessed(con)) {
state.linkProcessed(con);
for (ActivityUnit other : state.getActivityUnits(con)) {
if (other != au) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Process linked AU="+other);
}
processAU(state, other, topLevel);
}
}
}
}
}
/**
* This method determines whether the subsequent activity unit should be processed
* following the previous activity unit.
*
* @param previous The previously processed activity unit
* @param subsequent The subsequent activity unit to assess
* @return Whether to process the subsequent activity unit
*/
protected static boolean processSubsequentAU(ActivityUnit previous, ActivityUnit subsequent) {
boolean ret=true;
// Check if subsequent activity unit is triggered by a receive
if (subsequent.getActivityTypes().size() > 0
&& (subsequent.getActivityTypes().get(0) instanceof RequestReceived
|| subsequent.getActivityTypes().get(0) instanceof ResponseReceived)) {
ret = false;
} else {
// Check if link target
for (Context con : subsequent.contexts()) {
// Timeframe of 0 indicates link target
if (con.getType() == Context.Type.Link
&& con.linkTarget()) {
ret = false;
break;
}
}
}
return (ret);
}
/**
* This method initializes the call details based on current
* state and the supplied response sent.
*
* @param state The state
* @param rs The response sent event
* @param call The call
*/
protected static void initializeResponseSent(CTState state, ResponseSent rs, Call call) {
// Check if call node found
if (call == null) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("No call, so unable to initialize ResponseSent '"
+rs+"' for state="+state);
}
return;
}
call.setResponse(rs.getContent());
// Add further properties from the response
call.getProperties().putAll(rs.getProperties());
RPCActivityType rr=state.getSOAActivity(ResponseReceived.class,
rs.getServiceType(), rs.getOperation());
if (rr != null) {
call.setResponseLatency(rr.getTimestamp()-rs.getTimestamp());
}
// Set duration of call, if based on server side scope
if (state.getTriggerActivities().get(call)
instanceof RequestReceived) {
call.setDuration(rs.getTimestamp()
- state.getTriggerActivities().get(call).getTimestamp());
}
// If fault, then need to set the details on the Call
if (rs.getFault() != null && rs.getFault().trim().length() > 0) {
call.setFault(rs.getFault());
call.setStatus(Status.Fail);
}
}
/**
* This method initializes the call details based on current
* state and the supplied response received event.
*
* @param state The state
* @param rr The response received event
*/
protected static void initializeResponseReceived(CTState state, ResponseReceived rr) {
// Find completed call for this operation
for (int j=state.getCompletedCallStack().size()-1; j >= 0; j--) {
Call c=state.getCompletedCallStack().get(j);
// Set duration of call, if based on client side scope
if (state.getTriggerActivities().get(c)
instanceof RequestSent) {
RequestSent rs=(RequestSent)state.getTriggerActivities().get(c);
if (rs.getOperation().equals(rr.getOperation())
&& rs.getServiceType().equals(rr.getServiceType())) {
c.setDuration(rr.getTimestamp()
- rs.getTimestamp());
break;
}
}
}
}
/**
* This method identifies whether processing should be postponed
* based on the current activity type.
*
* @param state The state
* @param au The activity unit
* @param topLevel The top level units
* @param cur The current activity type
* @return Whether the processing of this unit should be postponed
*/
protected static boolean shouldPostpone(CTState state, ActivityUnit au,
java.util.List<ActivityUnit> topLevel, ActivityType cur) {
boolean ret=false;
Call call=(state.getCallStack().size() > 0 ? state.getCallStack().peek() : null);
// Check RequestReceived, then check whether
// activity unit is top level - otherwise
// need to postpone processing of this activity
// unit until the send request has been processed
if (cur instanceof RequestReceived && call == null
&& !topLevel.contains(au)) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Postpone processing unit due to receiving request before it has been sent");
}
ret = true;
}
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Should postpone processing of unit="+au+" cur="+cur+"? ret="+ret);
}
return (ret);
}
/**
* This method returns a call node associated with the supplied
* activity event.
*
* @param at The activity event
* @return The Call node
*/
protected static Call createCall(RPCActivityType at) {
Call call = new Call();
call.setInterface(at.getInterface());
call.setOperation(at.getOperation());
call.setComponent(at.getServiceType());
call.setPrincipal(at.getPrincipal());
call.getProperties().putAll(at.getProperties());
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Created call="+call);
}
return (call);
}
/**
* This method adds client or server specific information to the call,
* depending upon whether the activity is the client sending or server
* receiving the interaction.
*
* @param call The call
* @param au The activity unit
* @param at The activity type
*/
protected static void instrumentCall(Call call, ActivityUnit au, RPCActivityType at) {
if (au != null && au.getOrigin() != null) {
if (at instanceof RequestSent) {
call.getProperties().put("client-host", au.getOrigin().getHost());
call.getProperties().put("client-node", au.getOrigin().getNode());
} else if (at instanceof RequestReceived) {
call.getProperties().put("server-host", au.getOrigin().getHost());
call.getProperties().put("server-node", au.getOrigin().getNode());
}
}
}
/**
* This method returns a task associated with the supplied
* activity event.
*
* @param at The activity event
* @return The task
*/
protected static Task createTask(ActivityType at) {
Task ret=new Task();
// Transfer properties
ret.getProperties().putAll(at.getProperties());
try {
BeanInfo bi=java.beans.Introspector.getBeanInfo(at.getClass());
for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
if (CallTraceUtil.shouldIncludeProperty(pd)) {
try {
Object value=pd.getReadMethod().invoke(at);
if (value != null) {
ret.getProperties().put(pd.getDisplayName(), value.toString());
}
} catch (Exception ex) {
LOG.log(Level.SEVERE, "Failed to get property '"
+pd.getDisplayName()+"'", ex);
}
}
}
} catch (IntrospectionException e) {
LOG.log(Level.SEVERE, MessageFormat.format(
java.util.PropertyResourceBundle.getBundle(
"call-trace.Messages").getString("CALL-TRACE-2"),
at.getClass().getName()), e);
}
// Set the description
ret.setDescription(TaskDescriptorFactory.getTaskDescriptor(at).getDescription(at));
ret.setStatus(getStatus(at));
return (ret);
}
/**
* This method determines the status for the supplied
* activity type.
*
* @param at The activity type
* @return The status
*/
protected static Status getStatus(ActivityType at) {
Status ret=Status.Success;
if (at instanceof LogMessage) {
LogMessage lm=(LogMessage)at;
if (lm.getLevel() == LogMessage.Level.Warning) {
ret = Status.Warning;
} else if (lm.getLevel() == LogMessage.Level.Error) {
ret = Status.Fail;
}
}
return (ret);
}
/**
* This method identifies the top level activity units that
* contain receive request activities, with no corresponding
* send request.
*
* @param state The state
* @return The list of top level activity units
*/
protected static java.util.List<ActivityUnit> getTopLevelAUs(CTState state) {
java.util.List<ActivityUnit> ret=new java.util.ArrayList<ActivityUnit>();
// Identify top level candidates - where a receive request
// exists with no equivalent send request
for (ActivityUnit au : state.getActivityUnits()) {
for (ActivityType at : au.getActivityTypes()) {
if (at instanceof RequestReceived
&& state.getSOAActivity(RequestSent.class,
((RequestReceived)at).getServiceType(),
((RequestReceived)at).getOperation()) == null) {
ret.add(au);
continue;
}
}
}
return (ret);
}
/**
* This class provides a cursor for working through the
* activity types associated with the unit.
*
*/
public static class ActivityUnitCursor {
private ActivityUnit _unit=null;
private int _index=0;
/**
* This is the constructor for the cursor.
*
* @param unit The activity unit
*/
public ActivityUnitCursor(ActivityUnit unit) {
_unit = unit;
}
/**
* This method returns the list of remaining activity
* types that have not yet been visited using the
* cursor.
*
* @return The list of remaining activity types
*/
public java.util.List<ActivityType> getActivityTypes() {
java.util.List<ActivityType> ret=new java.util.ArrayList<ActivityType>();
for (int i=_index; i < _unit.getActivityTypes().size(); i++) {
ret.add(_unit.getActivityTypes().get(i));
}
return (ret);
}
/**
* This method peeks at the next activity type.
*
* @return The activity type
*/
public ActivityType peek() {
if (_index < _unit.getActivityTypes().size()) {
return (_unit.getActivityTypes().get(_index));
}
return (null);
}
/**
* This method returns the current activity type and
* moves the cursor to the next entry.
*
* @return The activity type
*/
public ActivityType next() {
if (_index < _unit.getActivityTypes().size()) {
return (_unit.getActivityTypes().get(_index++));
}
return (null);
}
}
/**
* This class maintains state information associated with the
* derivation of a call trace.
*
*/
public static class CTState {
private java.util.List<Context> _contexts=new java.util.ArrayList<Context>();
private java.util.List<Context> _linksProcessed=new java.util.ArrayList<Context>();
private java.util.List<ActivityUnit> _units=new java.util.ArrayList<ActivityUnit>();
private java.util.Map<String,ActivityUnit> _unitIndex=new java.util.HashMap<String,ActivityUnit>();
private java.util.Map<String,ActivityUnitCursor> _cursors=
new java.util.HashMap<String,ActivityUnitCursor>();
private java.util.Stack<Call> _callStack=new java.util.Stack<Call>();
private java.util.Stack<Call> _completedCallStack=new java.util.Stack<Call>();
private java.util.Stack<java.util.List<TraceNode>> _tasksStack=new java.util.Stack<java.util.List<TraceNode>>();
private java.util.Map<Call,RPCActivityType> _triggerActivity=
new java.util.HashMap<Call,RPCActivityType>();
/**
* This method determines whether the supplied context
* is already initialized.
*
* @param context The context to check
* @return Whether the context is already initialized
*/
public boolean isContextInitialized(Context context) {
return (_contexts.contains(context));
}
/**
* This method indicates that the supplied context
* has now been initialized.
*
* @param context The context
*/
public void initialized(Context context) {
if (!_contexts.contains(context)) {
_contexts.add(context);
}
}
/**
* This method determines if the supplied link
* context has been processed.
*
* @param context The link context
* @return Whether it has been processed
*/
public boolean isLinkProcessed(Context context) {
return (_linksProcessed.contains(context));
}
/**
* This method records the fact that the link has been processed.
*
* @param context The link context
*/
public void linkProcessed(Context context) {
_linksProcessed.add(context);
}
/**
* This method determines whether the activity unit,
* associated with the supplied id, has already been loaded.
*
* @param id The id
* @return Whether the activity unit has been loaded
*/
public boolean isActivityUnitLoaded(String id) {
return (_unitIndex.containsKey(id));
}
/**
* This method adds the supplied activity unit to the
* state information.
*
* @param au The activity unit
*/
public void add(ActivityUnit au) {
_units.add(au);
_unitIndex.put(au.getId(), au);
_cursors.put(au.getId(), new ActivityUnitCursor(au));
}
/**
* This method returns the list of activity units.
*
* @return The activity units
*/
public java.util.List<ActivityUnit> getActivityUnits() {
return (_units);
}
/**
* This method returns the list of activity units containing
* the specified context.
*
* @param context The context
* @return The activity units for this context
*/
public java.util.List<ActivityUnit> getActivityUnits(Context context) {
java.util.List<ActivityUnit> ret=new java.util.ArrayList<ActivityUnit>();
for (ActivityUnit au : _units) {
if (au.contexts().contains(context)) {
ret.add(au);
}
}
return (ret);
}
/**
* This method returns the map of call to trigger activity.
*
* @return The map of call to trigger activities
*/
public java.util.Map<Call,RPCActivityType> getTriggerActivities() {
return (_triggerActivity);
}
/**
* This method returns the activity unit associated with
* the supplied id.
*
* @param id The id
* @return The activity unit, or null if not found
*/
public ActivityUnit getActivityUnit(String id) {
return (_unitIndex.get(id));
}
/**
* This method returns the cursor associated with the
* supplied activity unit.
*
* @param au The activity unit
* @return The cursor, or null if not found
*/
public ActivityUnitCursor getCursor(ActivityUnit au) {
return (_cursors.get(au.getId()));
}
/**
* This method sorts the list of activity units
* based on time.
*/
public void sortActivityUnitsByTime() {
Collections.sort(_units, new Comparator<ActivityUnit>() {
public int compare(ActivityUnit o1, ActivityUnit o2) {
return ((int)(o1.getActivityTypes().get(0).getTimestamp()
- o2.getActivityTypes().get(0).getTimestamp()));
}
});
}
/**
* This method returns the call stack.
*
* @return The stack of Call nodes
*/
public java.util.Stack<Call> getCallStack() {
return (_callStack);
}
/**
* This method returns the completed call stack.
*
* @return The stack of completed Call nodes
*/
public java.util.Stack<Call> getCompletedCallStack() {
return (_completedCallStack);
}
/**
* This method returns the tasks stack.
*
* @return The stack of Tasks
*/
public java.util.Stack<java.util.List<TraceNode>> getTasksStack() {
return (_tasksStack);
}
/**
* This method finalizes the set of tasks within the current
* task list and then pops the relevant stacks.
*/
public void finalizeScope() {
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Finalize scope");
}
// If no work performed in this scope, then just return
if (getTasksStack().isEmpty()) {
return;
}
java.util.List<TraceNode> tasks=getTasksStack().peek();
Status status=Status.Success;
for (TraceNode task : tasks) {
if (task.getStatus().ordinal() > status.ordinal()) {
status = task.getStatus();
}
}
Call call=(getCallStack().size()>0 ? getCallStack().pop() : null);
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Popping call="+call);
LOG.finest("Popping tasks="+getTasksStack().peek());
}
if (call != null) {
_completedCallStack.push(call);
// Only overwrite the status of the call, if the lowest status
// of its contained task nodes is not success, and it is
// a lower status than warning (i.e. to avoid reducing the
// status of the call node based on its contents)
if (status != Status.Success
&& call.getStatus().ordinal() < Status.Warning.ordinal()) {
call.setStatus(Status.Warning);
}
}
getTasksStack().pop();
}
/**
* This method identifies the RPC activity associated with the
* specified class, service type and operation.
*
* @param cls The class
* @param serviceType The service type
* @param operation The operation
* @return The RPC activity, or null if not found
*/
protected RPCActivityType getSOAActivity(Class<?> cls,
String serviceType, String operation) {
// Use the activity unit order found in the units field
for (ActivityUnit au : _units) {
ActivityUnitCursor cursor=_cursors.get(au.getId());
for (ActivityType at : cursor.getActivityTypes()) {
if (at.getClass() == cls
&& ((RPCActivityType)at).getServiceType().equals(serviceType)
&& ((RPCActivityType)at).getOperation().equals(operation)) {
return ((RPCActivityType)at);
}
}
}
return (null);
}
}
}