JMX

No monitoring would be complete without support form JMX. Given that, Grizzly does provide out-of-the-box support for JMX, however, in order to make our lives easier, we've decided to use another open source library called GMBAL (pronounced "gumball") upon which to build our JMX support. To make our footprint lighter, be default, we only include the stub GMBAL jar that no-ops all GMBAL related operations, so in order to enable JMX support, the full GMBAL jar needs to be included in the application's classpath. If your project is maven based, this is easy: include a dependency like:

<dependency>
    <groupId>org.glassfish.gmbal</groupId>
    <artifactId>gmbal</artifactId>
    <version>3.0.0-b023</version>
</dependency>

or the jar can be downloaded from http://download.java.net/maven/2/org/glassfish/gmbal/gmbal/.

JMX Core Monitoring Artifacts

As before, there are artifacts to enable JMX support within the Grizzly runtime. In fact, they are simply extensions to MonitoringAware and MonitoringConfig within the org.glassfish.grizzly.monitoring.jmx package. The JMX equivalent of MonitoringAware is JmxMonitoringAware:

  1 /**
  2  * The interface, which could be used by the objects, which support JMX monitoring.
  3  * 
  4  * @author Alexey Stashok
  5  */
  6 public interface JmxMonitoringAware<E> extends MonitoringAware<E> {
  7     /**
  8      * Return the object associated {@link JmxMonitoringConfig}.
  9      *
 10      * @return the object associated {@link JmxMonitoringConfig}.
 11      */
 12     @Override
 13     public JmxMonitoringConfig<E> getMonitoringConfig();
 14 }

and the equivalent of MonitoringConfig, in case you didn't guess it, is JmxMonitoringConfig:

  1 /**
  2  * JMX monitoring configuration interface.
  3  *
  4  * @author Alexey Stashok
  5  */
  6 public interface JmxMonitoringConfig<E> extends MonitoringConfig<E> {
  7     /**
  8      * Create the {@link JmxObject}, which represents this object.
  9      *
 10      * @return the {@link JmxObject}, which represents this object.
 11      */
 12     public JmxObject createManagementObject();
 13 }

For ease of development, the class org.glassfish.grizzly.monitoring.jmx.AbstractJmxMonitoringConfig is provided for convenience:

/**
 * The abstract class, which represents the JMX aware configuration object.
 * 
 * @author Alexey Stashok
 */
public abstract class AbstractJmxMonitoringConfig<E> extends MonitoringConfigImpl<E>
        implements JmxMonitoringConfig<E> {

    public AbstractJmxMonitoringConfig(Class<E> clazz) {
        super(clazz);
    }
}

What about the JmxObject that must be implemented? The JmxObject implementation describes the entity that will be registered with the JMX runtime. The concrete JmxObject implementation typically wraps the Grizzly artifact to be managed. Here is a relatively simple example:

 54 /**
 55  * JMX management object for {@link org.glassfish.grizzly.http.KeepAlive}.
 56  *
 57  * @since 2.0
 58  */
 59 @ManagedObject
 60 @Description("The configuration for HTTP keep-alive connections.")
 61 public class KeepAlive extends JmxObject {
 62     /**
 63      * The {@link org.glassfish.grizzly.http.KeepAlive} being managed.
 64      */
 65     private final org.glassfish.grizzly.http.KeepAlive keepAlive;
 66 
 67     /**
 68      * The number of live keep-alive connections.
 69      */
 70     private final AtomicInteger keepAliveConnectionsCount = new AtomicInteger();
 71 
 72     /**
 73      * The number of requests processed on a keep-alive connections.
 74      */
 75     private final AtomicInteger keepAliveHitsCount = new AtomicInteger();
 76 
 77     /**
 78      * The number of times keep-alive mode was refused.
 79      */
 80     private final AtomicInteger keepAliveRefusesCount = new AtomicInteger();
 81 
 82     /**
 83      * The number of times idle keep-alive connections were closed by timeout.
 84      */
 85     private final AtomicInteger keepAliveTimeoutsCount = new AtomicInteger();
 86 
 87     /**
 88      * The {@link JMXKeepAliveProbe} used to track keep-alive statistics.
 89      */
 90     private final JMXKeepAliveProbe keepAliveProbe = new JMXKeepAliveProbe();
 91     
 92     // ------------------------------------------------------------ Constructors
 93 
 94 
 95     /**
 96      * Constructs a new JMX managed KeepAlive for the specified
 97      * {@link org.glassfish.grizzly.http.KeepAlive} instance.
 98      *
 99      * @param keepAlive the {@link org.glassfish.grizzly.http.KeepAlive}
100      *  to manage.
101      */
102     public KeepAlive(org.glassfish.grizzly.http.KeepAlive keepAlive) {
103         this.keepAlive = keepAlive;
104     }
105 
106     // -------------------------------------------------- Methods from JmxObject
107 
108 
109     /**
110      * {@inheritDoc}
111      */
112     @Override
113     public String getJmxName() {
114         return "Keep-Alive";
115     }
116 
117     /**
118      * <p>
119      * {@inheritDoc}
120      * </p>
121      *
122      * <p>
123      * When invoked, this method will add a {@link KeepAliveProbe} to track
124      * statistics.
125      * </p>
126      */
127     @Override
128     protected void onRegister(GrizzlyJmxManager mom, GmbalMBean bean) {
129         keepAlive.getMonitoringConfig().addProbes(keepAliveProbe);
130     }
131 
132     /**
133      * <p>
134      * {@inheritDoc}
135      * </p>
136      *
137      * <p>
138      * When invoked, this method will remove the {@link KeepAliveProbe} added
139      * by the {@link #onRegister(GrizzlyJmxManager, GmbalMBean)}
140      * call.
141      * </p>
142      */
143     @Override
144     protected void onDeregister(GrizzlyJmxManager mom) {
145         keepAlive.getMonitoringConfig().removeProbes(keepAliveProbe);
146     }
147 
148     // --------------------------------------------------- Keep Alive Properties
149 
150 
151     /**
152      * @see org.glassfish.grizzly.http.KeepAlive#getIdleTimeoutInSeconds()
153      */
154     @ManagedAttribute(id="idle-timeout-seconds")
155     @Description("The time period keep-alive connection may stay idle")
156     public int getIdleTimeoutInSeconds() {
157         return keepAlive.getIdleTimeoutInSeconds();
158     }
159 
160     /**
161      * @see org.glassfish.grizzly.http.KeepAlive#getMaxRequestsCount()
162      */
163     @ManagedAttribute(id="max-requests-count")
164     @Description("the max number of HTTP requests allowed to be processed on one keep-alive connection")
165     public int getMaxRequestsCount() {
166         return keepAlive.getMaxRequestsCount();
167     }
168 
169     /**
170      * @return the number live keep-alive connections.
171      */
172     @ManagedAttribute(id="live-connections-count")
173     @Description("The number of live keep-alive connections")
174     public int getConnectionsCount() {
175         return keepAliveConnectionsCount.get();
176     }
177 
178     /**
179      * @return the number of requests processed on a keep-alive connections.
180      */
181     @ManagedAttribute(id="hits-count")
182     @Description("The number of requests processed on a keep-alive connections.")
183     public int getHitsCount() {
184         return keepAliveHitsCount.get();
185     }
186 
187     /**
188      * @return the number of times keep-alive mode was refused.
189      */
190     @ManagedAttribute(id="refuses-count")
191     @Description("The number of times keep-alive mode was refused.")
192     public int getRefusesCount() {
193         return keepAliveRefusesCount.get();
194     }
195 
196     /**
197      * @return the number of times idle keep-alive connections were closed by timeout.
198      */
199     @ManagedAttribute(id="timeouts-count")
200     @Description("The number of times idle keep-alive connections were closed by timeout.")
201     public int getTimeoutsCount() {
202         return keepAliveTimeoutsCount.get();
203     }
204 
205     // ---------------------------------------------------------- Nested Classes
206 
207 
208     /**
209      * JMX statistic gathering {@link KeepAliveProbe}.
210      */
211     private final class JMXKeepAliveProbe implements KeepAliveProbe {
212 
213         @Override
214         public void onConnectionAcceptEvent(Connection connection) {
215             keepAliveConnectionsCount.incrementAndGet();
216             connection.addCloseListener(new Connection.CloseListener() {
217 
218                 @Override
219                 public void onClosed(Connection connection) throws IOException {
220                     keepAliveConnectionsCount.decrementAndGet();
221                 }
222             });
223         }
224 
225         @Override
226         public void onHitEvent(Connection connection, int requestCounter) {
227             keepAliveHitsCount.incrementAndGet();
228         }
229 
230         @Override
231         public void onRefuseEvent(Connection connection) {
232             keepAliveRefusesCount.incrementAndGet();
233         }
234 
235         @Override
236         public void onTimeoutEvent(Connection connection) {
237             keepAliveTimeoutsCount.incrementAndGet();
238         }
239 
240 
241         // ----------------------------------------- Methods from KeepAliveProbe
242 
243 
244     } // END JMXKeepAliveProbe
245 }

There are several things going on here that warrant explaination.

  • Line 59: Declared as a JMX managed entitied via the @ManagedObject annotation from GMBAL. This annotation must be present on the entities to be managed by JMX in order for GMBAL to do its magic.

  • Line 102: Constructor taking in an actual org.glassfish.grizzly.http.KeepAlive instance. This is needed as the actual KeepAlive instance has the monitoring config where probes can be registered.

  • Line 113: Returns the name to be displayed via JMX.

  • Line 128: This callback will be invoked by the GMBAL runtime when this @ManagedObject is registered with JMX. It's at this point in time that we register the probe implementation (starting at line 208) with the KeepAlive instance's JmxMonitoringConfig.

  • Line 144: This callback will be invoked by the GMBAL runtime when this @ManagedObject is de-registered with JMX. It's at this point in time that we remove the probe implementation (starting at line 208) with the KeepAlive instance's JxmMonintoringConfig.

  • Lines 151-203: These define @ManagedAttributes along with their descriptions which will be exposed via JMX.

Using the example JmxObject implementation above, the org.glassfish.grizzly.http.KeepAlive.createJmxManagementObject() is simple:

  1 protected JmxObject createJmxManagementObject() {
  2     return new org.glassfish.grizzly.http.jmx.KeepAlive(this);
  3 }

The final piece here is how to actually hook into the JMX runtime. It's easy!

public static void main(String[] args) {

        final GrizzlyJmxManager manager = GrizzlyJmxManager.instance();
        final TCPNIOTransport transport1 = TCPNIOTransportBuilder.newInstance().build();
        final TCPNIOTransport transport2 = TCPNIOTransportBuilder.newInstance().build();
        try {
            JmxObject jmxTransportObject1 =
                    transport1.getMonitoringConfig().createManagementObject();

            JmxObject jmxTransportObject2 =
                    transport2.getMonitoringConfig().createManagementObject();

            manager.registerAtRoot(jmxTransportObject1, "Transport1");
            manager.registerAtRoot(jmxTransportObject2, "Transport2");
            transport1.start();
            transport1.bind(9999);
            System.out.println("Press any key to stop the example...");
            System.in.read();
        } catch (IOException ioe) {
            ioe.printStackTrace();
            System.exit(1);
        } finally {
            try {
                transport1.stop();
            } catch (IOException ignored) {}
        }
}

Once running, you can connect to the process via JConsole and inspect the results (e.g., see that Transport1 is started while Transport2 is stopped).