This year at JavaOne Eamonn
and I presented during our technocal Session, where
we stand with respect to JMX. Eamonn covered JMX in general and
JMX 2.0, while I covered the Web
Services Connector for JMX. At the end of the talk, we performed
a demo named “CSPoker,
JMX Technology, Java™
VisualVM and WinRM
at the JavaOne Tournament's Final Table”.
We linked together a set of
JMX related technologies to offer, in an original Online Poker Web
Site use case, an interesting setup highlighting the power of JMX
(here's a PDF file of the demo
At the end of the talk, some people asked me some questions about
access to JMX we had just demonstrated. More details on the
WS-Management to JMX interoperability can be found in this
Here, I am providing some details on the actual script used during
the demo. This script automates the Poker Engine monitoring and
management tasks. It first registers to listen for JMX Notifications
exposed as WS-Eventing
notifications. Each time a “Poker table joined”
notification is received, it checks that the IP address from which
the player joined is not already known to the system. If an IP
address is already known, it means that another user already joined
from the same host.... which is very bad if you want to avoid the
same player using multiple identities....
When such a “bad user” is detected, the script ejects
all the players that joined from that machine.
Intialisation of the WS-Management access
set wsmanObj = CreateObject("WSMAN.Automation")
set objConnectionOptions = wsmanObj.CreateConnectionOptions
iFlags = wsmanObj.SessionFlagUseNoAuthentication
set session = wsmanObj.CreateSession("http://localhost:8080/admin", iFlags)
At this point, you have a WS-Management proxy (the session
object), that allows you to interact with a JMX Agent attached to the
Web Services Connector.
Subscription to the MBean notifications
The MBean we want to subscribe to is
named : cspoker:type=CSPokerControl
the WS-Management session doesn't offer a nice API for subscriptions,
we need to construct the subscription invocation request using the
“all purpose” invoke
reply = session.invoke("http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe", _
& "<wse:Delivery Mode='http://schemas.dmtf.org/wbem/wsman/1/wsman/Pull'/></wse:Subscribe>")
this point we have sent a WS-Eventing subscription request (in PULL
mode, meaning that the client will send a request to pull
notifications from the server) and received a response. The response
contains the WS-Eventing context to be used to retrieve
Retrieving the Eventing
get the eventing context from the response, we need to make use of
the VBScript DOM library : Microsoft.XMLDOM
: Because namespaces are not handled well in this DOM library, we are
trying to discover the Namespace prefix based on our knowledge of the
way they are declared (ns<i>). I am not a VBScript expert and
would be very interested to know if there is an equivalent to
getElementsByTagNameNS using VBScript???
Function findElement(elem, tagElem)
' wscript.echo "Finding Element " & tagElem
for i = 0 to 15
set findElement = elem.getElementsByTagName("ns"& i &":" & tagElem)
If findElement.Length > 0 Then Exit For
set objXMLDoc = CreateObject("Microsoft.XMLDOM")
objXMLDoc.async = False
' Get the Eventing context to use in the next WS-Eventing Pull request
set nodeList = findElement(objXMLDoc.documentElement, "EnumerationContext")
context = nodeList(0).text
wscript.echo "Subscription Enumeration Context " & context
this point, the context
variable contains the WS-Eventing context that identifies our
subscription. This context is to be used to retrieve notifications.
Retrieving the Notifications
is done by sending a Pull request to the server and providing it the
context. The pull request blocks until some Notifications are emitted
or the timeout (1 minute by default) is reached. As for subscribe
requests, no Pull API is offered. We need to use the general purpose
invoke operation. We
have written a pull function that handles pull request timeouts
properly. When the timeout occurs, instead of exiting the script, the
error is trapped and the script continues. Because we expect multiple
notifications, the Notification pulling is done in a loop.
Function pull(session, context)
On Error Resume Next
pullXml = "<wsen:Pull xmlns:wsen='http://schemas.xmlsoap.org/ws/2004/09/enumeration'><wsen:EnumerationContext>" & context & "</wsen:EnumerationContext><wsen:MaxTime>PT1M0.000S</wsen:MaxTime><wsen:MaxElements>1000</wsen:MaxElements></wsen:Pull>"
pull = session.invoke("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull", "http://jsr262.dev.java.net/MBeanNotificationSubscriptionManager", pullXml)
On Error Goto 0
' This piece of script is called in an infinite loop
notifs = pull(session, context)
this point, the notifs
variable contains (or does not contain, if the timeout occurred) the
JMX notifications in an XML format.
Extracting the Notification
content and tracking the cheat
only do this extraction if the list of notifications is not null. The
VBScript DOM library is again used to parse the notifications.
Because a pull request can return multiple notifications, we must
iterate on all TargetedNotification elements
contained in the notification list. A TargetedNotification
is an XML representation of a JMX Notification defined by the JSR 262
(JSR in which the Web Services Connector is defined).
our case, the TargetedNotification/Message
XML Element contains the Player name. The
XML Element contains the IP Address as a string
keep the IP <==> Player relationship, we use a Map data
structure and array. In VBScript, we use a dictionnary
' Create a Map
set dict = CreateObject("scripting.dictionary")
arrayIndex = 0
if Not(isNull(notifs)) then
set objXMLNotif = CreateObject("Microsoft.XMLDOM")
objXMLNotif.async = False
set NodeListNotifs = findElement(objXMLNotif.documentElement, "TargetedNotification")
WScript.echo "Pull returned " & NodeListNotifs.length & " notifications"
' Now that we get the list of notifications, extract the content.
' Loop over the received Notifications
For i = 0 To NodeListNotifs.length - 1
set listMsg = findElement(NodeListNotifs(i), "Message")
eventMessage = listMsg(0).text
set listUserData = findElement(NodeListNotifs(i2), "String")
' We have an IP address
if(listUserData.length > 0) then
ipAddress = listUserData(0).text
wscript.echo eventMessage & " Joined From " & ipAddress
if dict.exists(ipAddress) then
prev = dict.item(ipAddress)
if Not StrComp(prev,eventMessage) Eqv 0 Then
users = Array(eventMessage, prev)
For Each user In users
wscript.echo user & " already connected from same host [" & ipAddress &"], will eject him." '"Type return."
wscript.echo "Ejecting player " & user
ejectPlayer session, user
wscript.echo "Player " & user & " ejected"
dict.add ipAddress, eventMessage
Ejecting the player
function ejectPlayer calls the ejectPlayer
MBean operation by making use of the general purpose invoke
Function ejectPlayer(session, playerName)
pokerURI = "http://jsr262.dev.java.net/DynamicMBeanResource?ObjectName=cspoker:type=CSPokerControl"
xmlInvoke = "<jmx:ManagedResourceOperation name=" & Chr(34) & "ejectPlayer" & Chr(34) & " xmlns:jmx=" & Chr(34) & "http://jsr262.dev.java.net/jmxconnector" & Chr(34) & "><jmx:Input><jmx:Param><jmx:String>" & playerName & "</jmx:String></jmx:Param></jmx:Input></jmx:ManagedResourceOperation>"
session.invoke "http://jsr262.dev.java.net/DynamicMBeanResource/Invoke", pokerURI, xmlInvoke
that these VBScript extracts helped you understand better how you can
use this path to interoperate with JMX.