KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > MetricsInterceptor


1 /*
2  * JBoss, Home of Professional Open Source
3  * Copyright 2005, JBoss Inc., and individual contributors as indicated
4  * by the @authors tag. See the copyright.txt in the distribution for a
5  * full listing of individual contributors.
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this software; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21  */

22 package org.jboss.ejb.plugins;
23
24 import java.lang.reflect.Method JavaDoc;
25 import java.security.Principal JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.List JavaDoc;
28
29 import javax.jms.DeliveryMode JavaDoc;
30 import javax.jms.JMSException JavaDoc;
31 import javax.jms.Message JavaDoc;
32 import javax.jms.Session JavaDoc;
33 import javax.jms.Topic JavaDoc;
34 import javax.jms.TopicConnection JavaDoc;
35 import javax.jms.TopicConnectionFactory JavaDoc;
36 import javax.jms.TopicPublisher JavaDoc;
37 import javax.jms.TopicSession JavaDoc;
38 import javax.naming.Context JavaDoc;
39 import javax.naming.InitialContext JavaDoc;
40 import javax.naming.NamingException JavaDoc;
41 import javax.transaction.Transaction JavaDoc;
42
43 import org.jboss.ejb.Container;
44 import org.jboss.invocation.Invocation;
45 import org.jboss.monitor.MetricsConstants;
46
47 /**
48  * MetricsInterceptor collects data from the bean invocation call and publishes
49  * them on a JMS topic (bound to <tt>topic/metrics</tt> in the name service).
50  *
51  * @author <a HREF="mailto:jplindfo@helsinki.fi">Juha Lindfors</a>
52  * @author <a HREF="mailto:dimitris@jboss.org">Dimitris Anreadis</a>
53  * @version $Revision: 40398 $
54  *
55  * @since v2.0
56  */

57 public class MetricsInterceptor extends AbstractInterceptor
58    implements MetricsConstants
59 {
60    // Constants -----------------------------------------------------
61

62    // Attributes ----------------------------------------------------
63

64    /** Application name this bean belongs to */
65    private String JavaDoc applicationName = "<undefined>";
66    
67    /** Bean name in the container */
68    private String JavaDoc beanName = "<undefined>";
69    
70    /** Publisher thread */
71    private Thread JavaDoc publisher = null;
72
73    /**
74     * Message queue for the outgoing JMS messages. This list is accessed
75     * by the interceptor when adding new messages, and by the publisher
76     * thread when copying and clearing the contents of the queue. The list
77     * must be locked for access and locks should be kept down to minimum
78     * as they degrade the interceptor stack performance.
79     */

80    private List JavaDoc msgQueue = new ArrayList JavaDoc(2000);
81
82
83    // Public --------------------------------------------------------
84
/**
85     * Stores the container reference and the application and bean JNDI
86     * names.
87     *
88     * @param container set by the container initialization code
89     */

90    public void setContainer(Container container)
91    {
92       super.setContainer(container);
93       if (container != null)
94       {
95          applicationName = container.getEjbModule().getName();
96          beanName = container.getBeanMetaData().getJndiName();
97       }
98    }
99
100    // Interceptor implementation ------------------------------------
101

102    public Object JavaDoc invokeHome(Invocation mi) throws Exception JavaDoc
103    {
104
105       long begin = System.currentTimeMillis();
106
107       try
108       {
109          return super.invokeHome(mi);
110       }
111       finally
112       {
113          if (mi.getMethod() != null && publisher.isAlive())
114          {
115             addEntry(mi, begin, System.currentTimeMillis());
116          }
117       }
118    }
119
120    public Object JavaDoc invoke(Invocation mi) throws Exception JavaDoc
121    {
122
123       long begin = System.currentTimeMillis();
124
125       try
126       {
127          return super.invoke(mi);
128       }
129       finally
130       {
131          if (mi.getMethod() != null && publisher.isAlive())
132          {
133             addEntry(mi, begin, System.currentTimeMillis());
134          }
135       }
136    }
137
138    /**
139     * Starts the JMS publisher thread.
140     */

141    public void create()
142    {
143       log.warn("\n" +
144          "----------------------------------------------------------------------\n" +
145          "Deprecated MetricsInterceptor activated for bean: '" + beanName + "'\n" +
146          "Invocation metrics will be published in JMS Topic: 'topic/metrics'\n" +
147          "----------------------------------------------------------------------"
148          );
149             
150       // looks like create() is called after setContainer().
151
// wonder if container method callback order is documented somewhere, it should be..
152
publisher = new Thread JavaDoc(new Publisher JavaDoc());
153       publisher.setName("Metrics Publisher Thread for " + beanName);
154       publisher.setDaemon(true);
155       publisher.start();
156    }
157
158    /**
159     * Kills the publisher thread.
160     */

161    public void destroy()
162    {
163       publisher.interrupt();
164    }
165
166    // Private --------------------------------------------------------
167

168    /**
169     * Store the required information from this invocation: principal,
170     * transaction, method, time.
171     *
172     * @param begin invocation begin time in ms
173     * @param end invocation end time in ms
174     */

175    private final void addEntry(Invocation mi, long begin, long end)
176    {
177
178       /* this gets called by the interceptor */
179
180       Transaction JavaDoc tx = mi.getTransaction();
181       Principal JavaDoc princ = mi.getPrincipal();
182       Method JavaDoc method = mi.getMethod();
183       Entry start = new Entry(princ, method, tx, begin, "START");
184       Entry stop = new Entry(princ, method, tx, end, "STOP");
185
186       // add both entries, order is guaranteed, synchronized to prevent
187
// publisher from touching the queue while working on it
188
synchronized (msgQueue)
189       {
190
191          // Two entries for now, one should suffice but requires changes in
192
// the client.
193
msgQueue.add(start);
194          msgQueue.add(stop);
195       }
196    }
197
198    private Message JavaDoc createMessage(Session JavaDoc session, String JavaDoc principal, int txID,
199          String JavaDoc method, String JavaDoc checkpoint, long time)
200    {
201
202       try
203       {
204          Message JavaDoc msg = session.createMessage();
205
206          msg.setJMSType(INVOCATION_METRICS);
207          msg.setStringProperty(CHECKPOINT, checkpoint);
208          msg.setStringProperty(BEAN, beanName);
209          msg.setObjectProperty(METHOD, method);
210          msg.setLongProperty(TIME, time);
211
212          if (txID != -1)
213             msg.setStringProperty("ID", String.valueOf(txID));
214
215          if (principal != null)
216             msg.setStringProperty("PRINCIPAL", principal);
217
218          return msg;
219       }
220       catch (Exception JavaDoc e)
221       {
222          // catch JMSExceptions, tx.SystemExceptions, and NPE's
223
// don't want to bother the container even if the metrics fail.
224
return null;
225       }
226    }
227
228    /**
229     * JMS Publisher thread implementation.
230     */

231    private class Publisher implements Runnable JavaDoc
232    {
233
234       /** Thread keep-alive field. */
235       private boolean running = true;
236       /** Thread sleep delay. */
237       private int delay = 2000;
238       /** JMS Connection */
239       private TopicConnection JavaDoc connection = null;
240
241       /**
242        * Thread implementation. <p>
243        *
244        * When started, looks up a topic connection factory from the name
245        * service, and attempts to create a publisher to <tt>topic/metrics</tt>
246        * topic. <p>
247        *
248        * While alive, locks the <tt>msgQueue</tt> every two seconds to make a
249        * copy of the contents and then clear it. <p>
250        *
251        * Interrupting this thread will kill it.
252        *
253        * @see #msgQueue
254        * @see java.lang.Thread#interrupt()
255        */

256       public void run()
257       {
258
259          try
260          {
261             final boolean IS_TRANSACTED = true;
262             final int ACKNOWLEDGE_MODE = Session.DUPS_OK_ACKNOWLEDGE;
263
264             // lookup the connection factory and topic and create a JMS session
265
Context JavaDoc namingContext = new InitialContext JavaDoc();
266             TopicConnectionFactory JavaDoc fact = (TopicConnectionFactory JavaDoc) namingContext.lookup("java:/ConnectionFactory");
267
268             connection = fact.createTopicConnection();
269
270             Topic JavaDoc topic = (Topic JavaDoc) namingContext.lookup("topic/metrics");
271             TopicSession JavaDoc session = connection.createTopicSession(IS_TRANSACTED, ACKNOWLEDGE_MODE);
272             TopicPublisher JavaDoc pub = session.createPublisher(topic);
273
274             pub.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
275             pub.setPriority(Message.DEFAULT_PRIORITY);
276             pub.setTimeToLive(Message.DEFAULT_TIME_TO_LIVE);
277
278             // start the JMS connection
279
connection.start();
280
281             // copy the message queue every x seconds, and publish the messages
282
while (running)
283             {
284
285                Object JavaDoc[] array;
286                long sleepTime = delay;
287
288                try
289                {
290                   Thread.sleep(sleepTime);
291
292                   // measure message processing cost and try to deal
293
// with congestion
294
long begin = System.currentTimeMillis();
295
296                   // synchronized during the copy... the interceptor will
297
// have to wait til done
298
synchronized (msgQueue)
299                   {
300                      array = msgQueue.toArray();
301                      msgQueue.clear();
302                   }
303
304                   // publish the messages
305
for (int i = 0; i < array.length; ++i)
306                   {
307                      Message JavaDoc msg = createMessage(session,
308                            ((Entry) array[i]).principal,
309                            ((Entry) array[i]).id,
310                            ((Entry) array[i]).method,
311                            ((Entry) array[i]).checkpoint,
312                            ((Entry) array[i]).time
313                      );
314
315                      pub.publish(msg);
316                   }
317
318                   // try to deal with congestion a little better, alot of
319
// small messages fast will kill JBossMQ performance, this is
320
// a temp fix to group many messages into one operation
321
try
322                   {
323                      session.commit();
324                   }
325                   catch (Exception JavaDoc e)
326                   {
327                   }
328
329                   // stop the clock and reduce the work time from our
330
// resting time
331
long end = System.currentTimeMillis();
332
333                   sleepTime = delay - (end - begin);
334                }
335                catch (InterruptedException JavaDoc e)
336                {
337                   // kill this thread
338
running = false;
339                }
340             }
341          }
342          catch (NamingException JavaDoc e)
343          {
344             log.warn(Thread.currentThread().getName() + " exiting", e);
345          }
346          catch (JMSException JavaDoc e)
347          {
348             log.warn(Thread.currentThread().getName() + " exiting", e);
349          }
350          finally
351          {
352             // thread cleanup
353
synchronized (msgQueue)
354             {
355                msgQueue.clear();
356             }
357             
358             try
359             {
360                if (connection != null)
361                   connection.close();
362             }
363             catch (JMSException JavaDoc e)
364             {
365                 log.warn(e);
366             }
367          }
368       }
369    }
370
371    /**
372     * Wrapper class for message queue entries.
373     *
374     * @see #msgQueue
375     */

376    private final class Entry
377    {
378       int id = -1;
379       long time;
380       String JavaDoc principal = null;
381       String JavaDoc checkpoint;
382       String JavaDoc method;
383
384       Entry(Principal JavaDoc principal, Method JavaDoc method, Transaction JavaDoc tx, long time, String JavaDoc checkpoint)
385       {
386          this.time = time;
387          this.checkpoint = checkpoint;
388          this.method = method.getName();
389
390          if (tx != null)
391             this.id = tx.hashCode();
392          if (principal != null)
393             this.principal = principal.getName();
394       }
395    }
396 }
397
Popular Tags