Tuesday, October 03, 2006

Stopping an MDB via JMX - Cluster

Moving up a tiny bit in sophistication, my next goal was to do my start/stop of my MDB in a cluster. To illustrate this, I have created a cluster of two OC4J instances each called j2ee1, each in a separate OracleAS instance and they tied together for administrative operations in a group called cluster_group.

First let's take a look at this. You can see on each instance (soa_j2ee and soasuite) I have there is an OC4J instance called j2ee1 (they can be different names but I kept them the same).


If I go to the bottom of ASControl and click on cluster_group - the default out of the box is called default_group, but I have customized my configuration - you will be taken to the administrative screen that lets you do administrative operations across multiple OC4J instances simulataneously. In this case it will be against my two OC4J instances called j2ee1 each on different app server instances:


Within cluster_group, if I click on administration, I will see that there is a cluster mbean JMX browser that sits underneath this environment:


If I drill down on this browser a bit I can see the result of my cluster setup - two JVMProxy's each representing a j2ee1 instance, one on each OracleAS instance:




Clicking on one of them you can see that I can get its JVMProxy MBean:

ias:j2eeType=JVMProxy,name=1,J2EEServerGroup=cluster_group,J2EEServer=j2ee1,
ASInstance=soa_j2ee.MLEHMANN-CA


and the other, differentiated by the OracleAS instance name from the other:

ias:j2eeType=JVMProxy,name=1,J2EEServerGroup=cluster_group,J2EEServer=j2ee1,
ASInstance=soasuite.mlehmann-ca.ca.oracle.com


With that I think we are ready to see if we can update our JMX client from before to walk the cluster and turn off/on the MDB.

Remember, I have deployed my MDB application myMDB to each one of these servers which you can see if you go to the applications page of the cluster_group where the combined list of applications of both servers is displayed:


The solution is provided by Steve Button again, this time in Groovy code, but the translation to Java is pretty easy from here:

http://buttso.blogspot.com/2006/05/locating-oc4j-instances-via-opmn.html

As you may be able to interpret, our job is to iterate through our two j2ee1 OC4J instances in cluster_group, grab the MBean server for each instance and then shut off the MDB like we did for the single instance. The full client is here [1]

First and most importantly, instead of connecting to a specific OC4J instance like we did in the single instance case we connect to the cluster and get its Mbean Server - the trick is instead of going to your OC4J instance name like I did before ("/j2ee1") go to "/cluster". The term /cluster tells the MBean server to go to the cluster MBean server. The port used here, again, is the OPMN request port.

JMXConnector clusterConnect = omdb.connect("service:jmx:rmi:///opmn://127.0.0.1:6006/cluster", "oc4jadmin", "welcome1");
MBeanServerConnection mbs = clusterConnect.getMBeanServerConnection();

Then, we query the OC4J server instances in the cluster. I know that my servers of interest are in the J2EEServerGroup cluster_group and I know what I really want is the JVMProxy for each of those OC4J instances. The JVMProxy MBean will give me the methods that return information about where each of the OC4J instance MBean servers are (host and port) thus allowing me to connect to each of them programmatically:

ObjectName query = new ObjectName("ias:j2eeType=JVMProxy,J2EEServerGroup=cluster_group,*"); Set mbeans = mbs.queryNames(query, null);

Next, we need to do some variable set up - a variable for the service URL for each instance, a connection variable for each, an MBean server for each OC4J instance and finally the MessageDrivenBeanMBeanProxy to turn the MDB off/on:


String serviceURL = "";
JMXConnector instanceConnect = null;
ObjectName instanceObjectName = null;
MessageDrivenBeanMBeanProxy instanceMDBMBean = null;
MBeanServerConnection instanceMbs = null;


Next, we walk through the cluster and for each instance instantiate the JVMProxy Proxy MBean (whose naming convention doesn't quite follow my previous recommendation but it is the right MBean):

for(Iterator iterator = mbeans.iterator(); iterator.hasNext();) {
ObjectName objName = (ObjectName) iterator.next();
JVMMBeanProxy jvm = (JVMMBeanProxy)MBeanServerInvocationHandler.newProxyInstance(mbs,
objName, JVMMBeanProxy.class, false);


From the JVMMBeanProxy we can construct the MBean service URL for that instance:

serviceURL = "service:jmx:rmi://" + jvm.getnode() + ":" + jvm.getrmiPort();

And then connect to it:

instanceConnect = omdb.connect(serviceURL, "oc4jadmin", "welcome1");
instanceMbs = instanceConnect.getMBeanServerConnection();

Then we can look up our MessageDrivenBean:

instanceObjectName = new ObjectName("oc4j:j2eeType=MessageDrivenBean,EJBModule=\"myMDB\",J2EEApplication=myMDB,J2EEServer=standalone,name=\"MessageTopicProcessor\""); instanceMDBMBean = (MessageDrivenBeanMBeanProxy)MBeanServerInvocationHandler.newProxyInstance(instanceMbs, instanceObjectName, MessageDrivenBeanMBeanProxy.class, false);

And last but not least stop it:

instanceMDBMBean.stop();

Running this and browsing the myMDB application shows that all of its MessageProcessor MDB's are stopped as shown below:



There you go. Not a trivial example but works like a champ :-)


[1] Cluster MBean MDB Stop via JMX

ppackage demo.oc4j.jmx;

import java.net.URL;

import java.util.ArrayList;
import java.util.Hashtable;

import java.util.Iterator;
import java.util.Set;

import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectName;

import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

import oracle.oc4j.admin.management.mbeans.proxies.JVMMBeanProxy;
import oracle.oc4j.admin.management.mbeans.proxies.MessageDrivenBeanMBeanProxy;

public class OperateOnMDBCluster {
public OperateOnMDBCluster() {
}



private JMXConnector connect (String URL, String username, String password) {

JMXConnector jmxCon = null;

try {

Hashtable credentials = new Hashtable();
credentials.put("login", username);
credentials.put("password", password);

// Properties required to use the OC4J ORMI protocol
Hashtable env = new Hashtable();
env.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, "oracle.oc4j.admin.jmx.remote");
env.put(JMXConnector.CREDENTIALS, credentials);

JMXServiceURL serviceUrl = new JMXServiceURL(URL);
jmxCon = JMXConnectorFactory.newJMXConnector(serviceUrl, env);

// Do it!
jmxCon.connect();

} catch (Exception ex) {
ex.printStackTrace();
}

return jmxCon;
}

public static void main(String[] args) {
try {
OperateOnMDBCluster omdb = new OperateOnMDBCluster();

JMXConnector clusterConnect = omdb.connect("service:jmx:rmi:///opmn://127.0.0.1:6006/cluster", "oc4jadmin", "welcome1");

MBeanServerConnection mbs = clusterConnect.getMBeanServerConnection();

// First get a list of active servers - note the trick of having to
// give it the ias prefix which is part of telling it
ObjectName query = new ObjectName("ias:j2eeType=JVMProxy,J2EEServerGroup=cluster_group,*");
Set mbeans = mbs.queryNames(query, null);

// Now walk through them and construct the JMX service connection from each
String serviceURL = "";
JMXConnector instanceConnect = null;
ObjectName instanceObjectName = null;
MessageDrivenBeanMBeanProxy instanceMDBMBean = null;
MBeanServerConnection instanceMbs = null;

for(Iterator iterator = mbeans.iterator(); iterator.hasNext();) {
ObjectName o = (ObjectName) iterator.next();
JVMMBeanProxy jvm = (JVMMBeanProxy)MBeanServerInvocationHandler.newProxyInstance(mbs, o, JVMMBeanProxy.class, false);
serviceURL = "service:jmx:rmi://" + jvm.getnode() + ":" + jvm.getrmiPort();

//Now lets get the connection to this specific MBeanServer and get that MDB MBean
instanceConnect = omdb.connect(serviceURL, "oc4jadmin", "welcome1");
instanceMbs = instanceConnect.getMBeanServerConnection();
instanceObjectName = new ObjectName("oc4j:j2eeType=MessageDrivenBean,EJBModule=\"myMDB\",J2EEApplication=myMDB,J2EEServer=standalone,name=\"MessageTopicProcessor\"");
instanceMDBMBean = (MessageDrivenBeanMBeanProxy)MBeanServerInvocationHandler.newProxyInstance(instanceMbs, instanceObjectName, MessageDrivenBeanMBeanProxy.class, false);
// And finally stop that sucker
System.out.println("Stopping MDB on: " + serviceURL);
instanceMDBMBean.stop();
}


// ObjectName myMDBObjectName = new ObjectName("oc4j:j2eeType=MessageDrivenBean,EJBModule=\"myMDB\",J2EEApplication=myMDB,J2EEServer=standalone,name=\"MessageTopicProcessor\"");
//
// MDBMBean.start();

System.out.println("Success!");

} catch (Exception ex) {
ex.printStackTrace();
} finally {
}
}

}






1 comment:

Judy Zipfel said...

This is exactly what I needed.
I've been struggling with a RemoteMBeanServer type of connection that HP OpenView provided which was originally writen for JBoss. Without the src code it's been like throwing darts blindfolded. Thank you. I'll be typing you again as soon as I upgrade to 10.1.3.1