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/.
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).