/* * 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); } } }