User: juhalindfors
  Date: 01/04/18 08:25:51

  Modified:    src/main/org/jboss/ejb/plugins MetricsInterceptor.java
  Log:
  Reimplemented to move msg.publish() overhead out of the invocation thread.
  
  Revision  Changes    Path
  1.8       +219 -78   jboss/src/main/org/jboss/ejb/plugins/MetricsInterceptor.java
  
  Index: MetricsInterceptor.java
  ===================================================================
  RCS file: 
/cvsroot/jboss/jboss/src/main/org/jboss/ejb/plugins/MetricsInterceptor.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- MetricsInterceptor.java   2001/04/11 10:01:06     1.7
  +++ MetricsInterceptor.java   2001/04/18 15:25:51     1.8
  @@ -7,14 +7,16 @@
   package org.jboss.ejb.plugins;
   
   // standard imports
  -import java.util.Properties;
  -
  +import java.lang.reflect.Method;
   import java.security.Principal;
  +import java.util.Properties;
  +import java.util.List;
  +import java.util.ArrayList;
   
  +import javax.transaction.Transaction;
   import javax.naming.Context;
   import javax.naming.InitialContext;
   import javax.naming.NamingException;
  -
   import javax.jms.DeliveryMode;
   import javax.jms.Topic;
   import javax.jms.TopicPublisher;
  @@ -23,11 +25,10 @@
   import javax.jms.TopicConnectionFactory;
   import javax.jms.Message;
   import javax.jms.Session;
  +import javax.jms.Connection;
   import javax.jms.JMSException;
  -
  -import javax.transaction.Transaction;
  -import javax.transaction.Status;
  -
  +import javax.jms.TransactionRolledBackException;
  +import javax.jms.IllegalStateException;
   
   // jboss imports
   import org.jboss.ejb.Container;
  @@ -35,33 +36,43 @@
   import org.jboss.monitor.MetricsConstants;
   
   /**
  - * MetricsInterceptor is used for gathering data from the container for admin
  - * interface.
  + * MetricsInterceptor collects data from the bean invocation call and publishes
  + * them on a JMS topic (bound to <tt>topic/metrics</tt> in the name service). <p>
    *
  + *
    * @since   jBoss 2.0
    *
    * @author  <a href="mailto:[EMAIL PROTECTED]">Juha Lindfors</a>
    */
  -public class MetricsInterceptor extends AbstractInterceptor 
  -                                implements MetricsConstants {
  +public class MetricsInterceptor extends AbstractInterceptor
  +        implements MetricsConstants {
   
  -    // Constants -----------------------------------------------------
  -    
       // Attributes ----------------------------------------------------
  +    /** The container for this interceptor.             */
       private Container container           = null;
  -    private Context  namingContext        = null;
  -    private TopicPublisher metricsPub     = null;
  -    private TopicSession metricsSession   = null;
  -    private Topic metricsTopic            = null;
       
  +    /** Application name this bean belongs to.          */
       private String applicationName        = "<undefined>";
  +    /** Bean name in the container.                     */ 
       private String beanName               = "<undefined>";
  -    
  -    // Static --------------------------------------------------------
   
  -    // Constructors --------------------------------------------------
  +    /**
  +     * Message queue for the outgoing JMS messages. This list is accessed
  +     * by the interceptor when adding new messages, and by the publisher
  +     * thread when copying and clearing the contents of the queue. The list
  +     * must be locked for access and locks should be kept down to minimum
  +     * as they degrade the interceptor stack performance.
  +     **/
  +    private List msgQueue  =  new ArrayList(2000);
  +    
      
       // Public --------------------------------------------------------
  +    /**
  +     * Stores the container reference and the application and bean JNDI
  +     * names.
  +     *
  +     * @param   container   set by the container initialization code
  +     */
       public void setContainer(Container container) {
           this.container  = container;
           
  @@ -69,96 +80,104 @@
           beanName        = container.getBeanMetaData().getJndiName();
       }
       
  +    /**
  +     * Returns the container reference.
  +     *
  +     * @return the container this interceptor belongs to
  +     */
       public Container getContainer() {
           return container;
       }
   
       // Interceptor implementation ------------------------------------
  -   public Object invokeHome(MethodInvocation mi) throws Exception {
  +    public Object invokeHome(MethodInvocation mi) throws Exception {
        
  -     try {
  -        sendMessage(System.currentTimeMillis(), createMessage(mi, "START"));
  -        return super.invokeHome(mi);
  -     }
  -     finally {
  -         sendMessage(System.currentTimeMillis(), createMessage(mi, "STOP"));
  -     }
  +        long begin = System.currentTimeMillis();
  +     
  +        try {
  +            return super.invokeHome(mi);
  +        }
  +        finally {
  +            addEntry(mi, begin, System.currentTimeMillis());
  +        }
      }
   
       public Object invoke(MethodInvocation mi) throws Exception {
   
  +        long begin = System.currentTimeMillis();
  +        
           try {
  -            sendMessage(System.currentTimeMillis(), createMessage(mi, "START"));
               return super.invoke(mi);
           }
  -      
           finally {
  -            sendMessage(System.currentTimeMillis(), createMessage(mi, "STOP"));
  +            addEntry(mi, begin, System.currentTimeMillis());
           }
      }
   
  +   /**
  +    * Starts the JMS publisher thread.
  +    */
      public void init() {
  -
  -       try {
  -           final boolean IS_TRANSACTED    = false;
  -           final int     ACKNOWLEDGE_MODE = Session.DUPS_OK_ACKNOWLEDGE;
  -           
  -           namingContext = new InitialContext();
  -           
  -           TopicConnectionFactory factory = (TopicConnectionFactory)
  -                namingContext.lookup("TopicConnectionFactory");
  -
  -           TopicConnection connection = factory.createTopicConnection();
  -
  -           metricsTopic     = (Topic)namingContext.lookup("topic/metrics");
  -           metricsSession   = connection.createTopicSession(IS_TRANSACTED, 
ACKNOWLEDGE_MODE);
  -           metricsPub       = metricsSession.createPublisher(metricsTopic);     
  -           
  -           connection.start();
  -       }
  -       catch (NamingException e) {
  -           System.out.println(e);
  -       }
  -       catch (JMSException e) {
  -           System.out.println(e);
  -       }
          
  +        /*
  +         * looks like init() is called after setContainer().
  +         * wonder if container method callback order is documented somewhere, it 
should be.. 
  +         */
  +         
  +        Thread thread = new Thread(new Publisher());
  +        thread.setName("Metrics Publisher Thread for " + beanName + ".");
  +        thread.setDaemon(true);
  +        thread.start();
      }
   
      
       // Private --------------------------------------------------------
  -
  -    private void sendMessage(long time, Message msg) {        
  -
  -        try {
  -            msg.setLongProperty(TIME,  time);
  -            metricsPub.publish(msg, DeliveryMode.NON_PERSISTENT, 1, 10000);
  -        }
  -        catch (Exception e) {
  -            // catch JMSExceptions, NPE's etc and prevent them from propagating
  -            // up if the metrics fail
  +    
  +    /**
  +     * Store the required information from this invocation: principal,
  +     * transaction, method, time.
  +     *
  +     * @param   begin   invocation begin time in ms
  +     * @param   end     invocation end time in ms
  +     */
  +    private void addEntry(MethodInvocation mi, long begin, long end) {
  +        
  +        /* this gets called by the interceptor */
  +        
  +        Transaction tx  = mi.getTransaction();
  +        Principal princ = mi.getPrincipal();
  +        Method method   = mi.getMethod();
  +        Entry start     = new Entry(princ, method, tx, begin, "START");
  +        Entry stop      = new Entry(princ, method, tx, end, "STOP");
  +
  +        // add both entries, order is guaranteed, synchronized to prevent
  +        // publisher from touching the queue while working on it
  +        synchronized(msgQueue) {
  +            
  +            // Two entries for now, one should suffice but requires changes in
  +            // the client.
  +            msgQueue.add(start);
  +            msgQueue.add(stop);
           }
       }
  -    
  -    private Message createMessage(MethodInvocation mi, String checkpoint) {
  +        
  +    private Message createMessage(Session session, String principal, int txID,
  +                                  String method, String checkpoint, long time) {
           
           try {            
  -            Message  msg    =  metricsSession.createMessage();
  -            Transaction tx  =  mi.getTransaction();
  -            Principal principal = mi.getPrincipal();
  -            
  +            Message  msg    =  session.createMessage();
  +
               msg.setJMSType(INVOCATION_METRICS);
  -           // msg.setJMSExpiration(1);
  -            
               msg.setStringProperty(CHECKPOINT, checkpoint);
  -            msg.setStringProperty(BEAN,   beanName);
  -            msg.setObjectProperty(METHOD, mi.getMethod().getName());    
  +            msg.setStringProperty(BEAN, beanName);
  +            msg.setObjectProperty(METHOD, method);    
  +            msg.setLongProperty(TIME, time);
               
  -            if (tx != null) 
  -                msg.setStringProperty("ID",  String.valueOf(tx.hashCode()));
  +            if (txID != -1) 
  +                msg.setStringProperty("ID",  String.valueOf(txID));
                           
               if (principal != null)
  -                msg.setStringProperty("PRINCIPAL", principal.getName());
  +                msg.setStringProperty("PRINCIPAL", principal);
                   
               return msg;
           }
  @@ -168,6 +187,128 @@
               return null;
           }
       }
  +
  +
  +    /**
  +     * JMS Publisher thread implementation.
  +     */
  +    private class Publisher implements Runnable {
  +     
  +        /** Thread keep-alive field. */
  +        private boolean running = true;
  +        /** Thread sleep delay. */
  +        private int delay = 2000;
  +        /** JMS Connection */
  +        private TopicConnection connection = null;
  +        
  +        /**
  +         * Thread implementation. <p>
  +         *
  +         * When started, looks up a topic connection factory from the name
  +         * service, and attempts to create a publisher to <tt>topic/metrics</tt>
  +         * topic. <p>
  +         *
  +         * While alive, locks the <tt>msgQueue</tt> every two seconds to make a
  +         * copy of the contents and then clear it. <p>
  +         *
  +         * Interrupting this thread will kill it.
  +         *
  +         * @see #msgQueue
  +         * @see java.lang.Thread#interrupt()
  +         */
  +        public void run() {
       
  +            try {
  +                final boolean IS_TRANSACTED    = false;
  +                final int     ACKNOWLEDGE_MODE = Session.DUPS_OK_ACKNOWLEDGE;
  +               
  +                // lookup the connection factory and topic and create a JMS session
  +                Context namingContext       = new InitialContext();
  +                TopicConnectionFactory fact = 
(TopicConnectionFactory)namingContext.lookup("TopicConnectionFactory");
  +                
  +                connection  = fact.createTopicConnection();
  +    
  +                Topic topic          = (Topic)namingContext.lookup("topic/metrics");
  +                TopicSession session = connection.createTopicSession(IS_TRANSACTED, 
ACKNOWLEDGE_MODE);
  +                TopicPublisher pub   = session.createPublisher(topic);     
  +               
  +                pub.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
  +                pub.setPriority(Message.DEFAULT_PRIORITY);
  +                pub.setTimeToLive(Message.DEFAULT_TIME_TO_LIVE);
  +
  +                // start the JMS connection
  +                connection.start();
  +               
  +                // copy the message queue every x seconds, and publish the messages
  +                while (running) {
  +
  +                    Object[] array;
  +
  +                    try {
  +                        Thread.sleep(delay);
  +                        
  +                        // synchronized during the copy... the interceptor will
  +                        // have to wait til done
  +                        synchronized(msgQueue) {
  +                            array = msgQueue.toArray();
  +                            msgQueue.clear();    
  +                        }
  +                        
  +                        // publish the messages
  +                        for (int i = 0; i < array.length; ++i) {
  +                            Message msg = createMessage(session,
  +                                            ((Entry)array[i]).principal,
  +                                            ((Entry)array[i]).id,
  +                                            ((Entry)array[i]).method,
  +                                            ((Entry)array[i]).checkpoint,
  +                                            ((Entry)array[i]).time
  +                                          );
  +                                          
  +                            pub.publish(msg);
  +                        }                                          
  +                    }
  +                    catch (InterruptedException e) {
  +                        // kill this thread
  +                        running = false;
  +                    }                          
  +               }
  +               
  +               // thread cleanup
  +               connection.close();
  +                   
  +           }
  +           catch (NamingException e) {
  +               System.out.println(e);
  +           }
  +           catch (JMSException e) {
  +               System.out.println(e);
  +           }
  +        }
  +    }
  +        
  +    /**
  +     * Wrapper class for message queue entries.
  +     *
  +     * @see #msgQueue
  +     */
  +    private class Entry {
  +     
  +       int  id = -1;
  +       long time;
  +       String principal = null;
  +       String checkpoint;
  +       String method;
  +       
  +       Entry(Principal principal, Method method, Transaction tx, long time, String 
checkpoint) {
  +           this.time = time;
  +           this.checkpoint = checkpoint;
  +           this.method = method.getName();
  +           
  +           if (tx != null)
  +               this.id = tx.hashCode();
  +           if (principal != null)
  +               this.principal = principal.getName();
  +       }
  +    }
   }
   
  
  
  

_______________________________________________
Jboss-development mailing list
[EMAIL PROTECTED]
http://lists.sourceforge.net/lists/listinfo/jboss-development

Reply via email to