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