Version 4

    The ScriptingListener Service

     

    The ScriptingListener service was introduced in JBoss v4.0.3. The idea is that

    there can be cases that we want to react to some JMX notification, in a way

    this should not be hardcoded. For example we want to receive a notification

    and map it to another notification, log the original notification,

    apply complex logic to decide if a combination of facts constitutes an alarm,

    get an mbean attribute from a 2nd mbean and do something with it,

    call an operation onto a 3rd mbean, etc.

     

    Combining the ScriptingListener with a simple JMX Timer (configured through

    the TimerService to produce TimerNotification notifications at regular intervals)

    you can essentially achieve a polling effect. Your script will be called

    periodically to apply arbitrary scripting logic (e.g. perform cleanup tasks,

    log stuff, monitor a set of attributes).

     

    The ScriptingListener uses the Bean Scripting Framework

    (BSF) library to execute a script in any of the scripting languages that can hook

    up to BSF (e.g. Jython, Jacl, Groovy, BeanShell, etc.). The script is executed

    whenever a notification is received and it is passed the notification along

    with some other context info that includes the following variables:

     

    • log, an instance of org.jboss.logging.Logger that can be used

    to log messages; this is essentially the ScriptingListener's logger.

    • server, an instance of javax.management.MBeanServer providing access

    to the central MBeanServer used by jboss; it can be used to get/set

    attributes and invoke operation on other mbeans.

    • manager, an instance of org.jboss.monitor.alarm.AlarmManager, essentialy

    a helper class that is of interest if you want to generate stateless or

    stateful alarms that can be stored in the ActiveAlarmTable.

    • notification, this holds the notification instance received each time

    by the ScriptingListener.

    • handback, the handback object passed to ScriptingListener

    handleNotification() along with the notification.

     

    Since executing a script in the context of the notification broadcaster

    thread can take up some time (usually milliseconds, still a lot more time

    than a broadcaster would expect) we enqueue the received notification

    in memory and process it from a background thread.

     

    You have to be a little careful with this, because it may have some issues

    with mbeans that produce mutable notifications. Normally mbeans should

    always emit immutable notifications, or copies of mutable ones. However

    there are cases where this is not true. For example, notifications emitted

    by deployers in jboss carry the original DeploymentInfo object (and not a

    copy of it, at the time the notification is created). If you process the

    notification in a different thread, from that of the sender, you may find

    the data in the DeploymentInfo object to have changed.

     

    Also, at this time there is no mechanism to prevent overflowing the internal

    queue with notifications, in case we have very fast producers or very slow

    scripts, so this is something that could be improved.

     

    Configuration

     

    The attributes that can be configured are:

     

    • ScriptLanguage, the language in which the provided script is written.

    Out of the box, jboss will support beanshell since bsh.jar is already

    present in the server/xxx/lib directory. If you want a different language,

    you need to make sure the implementation is available in the classpath.

    The easiest thing is to just drop the .jar file in the lib directory.

    • Script, the script inlined in the service descriptor. Since scripts

    may contain funny characters it is best to escape the xml content of

    this attribute, i.e.

    <attribute name="Script"><![CDATA[ ...whatever... \]\]\></attribute>
    • DynamicSubscriptions, since the ScriptingListener is based on

    ListenerServiceMBeanSupport, you can indicate whether this service should

    subscribe dynamically for notifications to newly registered mbeans

    • SubscriptionList, the mbeans/notifications to subscribe for.

     

    There are also some read only attributes:

     

    • NotificationsReceived, the number of notifications received (and

    therefore enqueued) by the ScriptingListener.

    • NotificationsProcessed, the number of notifications processed

    (dequeued) by the script.

    • TotalProcessingTime, the total time spent by the script in

    processing notifications, in milliseconds.

    • AverageProcessingTime, the average time spent by the script in

    processing every notification, again, in milliseconds. This is a good

    indication of how much time the script takes to execute.

     

    Example

     

    In the following example, a JMX Timer is instantiated through the

    TimerService wrapper, just to provide us with periodic notifications

    that trigger our script every 30 seconds. The ScriptingListener subscribes

    for those notifications, as you can see, using the SubscriptionList

    attribute.

     

    A simple script is provided in beanshell that, upon the reception of

    each notification, it queries and logs the free system memory and the total

    number of threads. The server and log variables are already

    setup in the script context by the ScriptingListener.

    <server>
      
       <mbean code="org.jboss.monitor.services.ScriptingListener" 
              name="jboss.monitor:service=ScriptingListener">
          <attribute name="ScriptLanguage">beanshell</attribute>
          <attribute name="SubscriptionList">
             <subscription-list>
                <mbean name="jboss.monitor:name=MonitorClock,type=Timer"></mbean>
             </subscription-list>
          </attribute>    
          <attribute name="Script">
    <![CDATA[
       import javax.management.ObjectName;
    
       /* poll free memory and thread count */   
       ObjectName target = new ObjectName("jboss.system:type=ServerInfo");
    
       long freeMemory = server.getAttribute(target, "FreeMemory");
       long threadCount = server.getAttribute(target, "ActiveThreadCount");
    
       log.info("freeMemory" + freeMemory + ", threadCount" + threadCount);
    \]\]\>
          </attribute>
       </mbean>
      
      <mbean code="org.jboss.monitor.services.TimerService"
             name="jboss.monitor:name=MonitorClock,type=TimerService">
        <attribute name="NotificationType">jboss.monitor.clock</attribute>
        <attribute name="TimerPeriod">30sec</attribute>
        <depends optional-attribute-name="TimerMBean">
          <mbean code="javax.management.timer.Timer"
                 name="jboss.monitor:name=MonitorClock,type=Timer"></mbean>
        </depends>
      </mbean>
      
    </server>
    

    The output in the console looks similar to:

    17:08:52,425 INFO  [ScriptingListener] freeMemory103875728, threadCount37
    17:09:22,439 INFO  [ScriptingListener] freeMemory102232792, threadCount37
    17:09:52,442 INFO  [ScriptingListener] freeMemory103872032, threadCount37
    17:10:22,455 INFO  [ScriptingListener] freeMemory102229096, threadCount37
    17:10:52,448 INFO  [ScriptingListener] freeMemory104121072, threadCount37
    17:11:22,451 INFO  [ScriptingListener] freeMemory102478136, threadCount37
    17:11:52,464 INFO  [ScriptingListener] freeMemory104117376, threadCount37
    17:12:22,467 INFO  [ScriptingListener] freeMemory102474440, threadCount37
    

     

    Instructions for upgrading BSF and BSH can be found here.

     

    Related:

     

     

    Referenced by: