View Javadoc

1   /*
2    * $Id: ProcessingTime.java 20593 2010-12-10 03:56:00Z mike.schilling $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.management.stats;
12  
13  import org.mule.api.MuleContext;
14  import org.mule.api.MuleSession;
15  import org.mule.api.construct.FlowConstruct;
16  import org.mule.util.concurrent.ThreadNameHelper;
17  
18  import java.io.Serializable;
19  import java.lang.ref.ReferenceQueue;
20  import java.lang.ref.WeakReference;
21  import java.util.Map;
22  
23  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
24  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong;
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  
28  /**
29   * Accumulates the processing time for all branches of a flow
30   */
31  public class ProcessingTime implements Serializable
32  {
33      /**
34       * Serial version
35       */
36      private static final long serialVersionUID = 1L;
37  
38      private static final Log logger = LogFactory.getLog(ProcessingTime.class);
39      private static volatile Thread referenceThread;
40      private static ReferenceQueue<ProcessingTime> queue = new ReferenceQueue<ProcessingTime>();
41      private static Map refs = new ConcurrentHashMap();
42  
43      private AtomicLong accumulator = new AtomicLong();
44      private FlowConstructStatistics statistics;
45      private String threadName;
46  
47      /**
48       * Create a ProcessingTime for the specified MuleSession.
49       * @return ProcessingTime if the session has an enabled FlowConstructStatistics or null otherwise
50       */
51      public static ProcessingTime newInstance(MuleSession session, MuleContext muleContext)
52      {
53          if (session != null)
54          {
55              FlowConstruct fc = session.getFlowConstruct();
56              if (fc != null)
57              {
58                  FlowConstructStatistics stats = fc.getStatistics();
59                  if (stats != null && fc.getStatistics().isEnabled())
60                  {
61                      return new ProcessingTime(stats, muleContext);
62                  }
63              }
64  
65          }
66  
67          return null;
68      }
69  
70      /**
71       * Create a Processing Time
72       * @param stats never null
73       * @param muleContext
74       */
75      private ProcessingTime(FlowConstructStatistics stats, MuleContext muleContext)
76      {
77          this.statistics = stats;
78          this.threadName = String.format("%sprocessing.time.monitor", ThreadNameHelper.getPrefix(muleContext));
79          if (referenceThread == null)
80          {
81              startThread();
82          }
83          refs.put(new Reference(this), refs);
84      }
85  
86      /**
87       * Add the execution time for this branch to the flow construct's statistics
88       * @param startTime  time this branch started
89       */
90      public void addFlowExecutionBranchTime(long startTime)
91      {
92          if (statistics.isEnabled())
93          {
94              long elapsedTime = getEffectiveTime(System.currentTimeMillis() - startTime);
95              statistics.addFlowExecutionBranchTime(elapsedTime, accumulator.addAndGet(elapsedTime));
96          }
97      }
98  
99      /**
100      * Convert processing time to effective processing time.  If processing took less than a tick, we consider
101      * it to have been one millisecond
102      */
103     public static long getEffectiveTime(long time)
104     {
105         return (time <= 0) ? 1L : time;
106     }
107 
108     /**
109      * Start timer that processes reference queue
110      */
111     public void startThread()
112     {
113         synchronized (ProcessingTime.class)
114         {
115             if (referenceThread == null)
116             {
117                 referenceThread = new Thread(new Runnable()
118                 {
119                     /**
120                      * As weak references to completed ProcessingTimes are delivered, record them
121                      */
122                     public void run()
123                     {
124 
125                         try
126                         {
127                             while (true)
128                             {
129                                 if (Thread.currentThread() != referenceThread)
130                                 {
131                                     break;
132                                 }
133                                 try
134                                 {
135                                     // The next two lines look silly, but
136                                     //       ref = (Reference) queue.poll();
137                                     // fails on the IBM 1.5 compiler
138                                     Object temp = queue.remove();
139                                     Reference ref = (Reference) temp;
140                                     refs.remove(ref);
141                                     FlowConstructStatistics stats = ref.getStatistics();
142                                     if (stats.isEnabled())
143                                     {
144                                         stats.addCompleteFlowExecutionTime(ref.getAccumulator().longValue());
145                                     }
146                                 }
147                                 catch (InterruptedException ex )
148                                 {
149                                     Thread.currentThread().interrupt();
150                                     break;
151                                 }
152                                 catch (Exception ex)
153                                 {
154                                     // Don't let exception escape -- it kills the thread
155                                     logger.error(this, ex);
156                                 }
157                             }
158                         }
159                         finally
160                         {
161                             referenceThread = null;
162                         }
163                     }
164                 }, this.threadName);
165                 referenceThread.setDaemon(true);
166                 referenceThread.start();
167             }
168         }
169     }
170 
171     /**
172      * Stop timer that processes reference queue
173      */
174     public synchronized static void stopTimer()
175     {
176         if (referenceThread != null)
177         {
178             referenceThread.interrupt();
179             referenceThread = null;
180         }
181         refs.clear();
182     }
183 
184     /**
185      * Weak reference that includes flow statistics to be updated
186      */
187     static class Reference extends WeakReference<ProcessingTime>
188     {
189         private FlowConstructStatistics statistics;
190         private AtomicLong accumulator;
191 
192         Reference(ProcessingTime time)
193         {
194             super(time, queue);
195             this.statistics = time.statistics;
196             this.accumulator = time.accumulator;
197         }
198 
199         public AtomicLong getAccumulator()
200         {
201             return accumulator;
202         }
203 
204         public FlowConstructStatistics getStatistics()
205         {
206             return statistics;
207         }
208     }
209 }