An MBean is a managed Java object. They are generally used to expose monitoring statistics of a java application, management capability on it. JDK itself has a default set of MBeans to monitor JVM statistics etc.
It follows the JMX specification. You can use JMX protocol to invoke MBeans. It is possible to connect to MBeans from same machine insecurely, but if you access from a different machine, credentials needs to be provided.
It is often a requirement to receive and view several properties of a set of objects (objects are rows and properties are columns if you put it to a table). This can be achieved using CompositeData under Open MBeans.
This tutorial shows how to define MBean with CompositeData as well as how to access and receive information.
Expose a set of customer objects. A customer has following properties those need to be exposed.
Java follows some standards here. If concrete implementation is "Y" interface name should be "YMBean". Both should be in same package.
Following the convention let us define the MBean provider interface as follows.
We need to define CompositeType with attribute names, attribute descriptions and attribute types. Here let us use attribute names as descriptions for simplicity.
Now the MBean implementation is ready.
Running the client you will see customer information is printed to the console. This way, complex object information can be transferred using MBeans using CompositeData.
It follows the JMX specification. You can use JMX protocol to invoke MBeans. It is possible to connect to MBeans from same machine insecurely, but if you access from a different machine, credentials needs to be provided.
It is often a requirement to receive and view several properties of a set of objects (objects are rows and properties are columns if you put it to a table). This can be achieved using CompositeData under Open MBeans.
This tutorial shows how to define MBean with CompositeData as well as how to access and receive information.
Scenario
Expose a set of customer objects. A customer has following properties those need to be exposed.
- name (String)
- address (String)
- age (Integer)
- amount paid (Long)
- is married (Boolean)
Define a Customer object
Let us define a customer object as below.
package mBeanServer; public class Customer { private String name; private String address; private int age; private long amountPaid; private boolean isMarried; public Customer(String name, String address, int age, long amountPaid, boolean isMarried) { this.name = name; this.address = address; this.age = age; this.amountPaid = amountPaid; this.isMarried = isMarried; } public String getName() { return name; } public String getAddress() { return address; } public int getAge() { return age; } public long getAmountPaid() { return amountPaid; } public boolean isMarried() { return isMarried; } public String toString() { return "name: " + name + "address:" + address + "age:" + age + "amount paid:" + amountPaid + "is married: " + isMarried; } }
Create a Object factory to create customers
Following class is used to create some mock customer objectspackage mBeanServer; import java.util.ArrayList; import java.util.List; public class CustomerFactory { public static Listgenerate() { Customer customer1 = new Customer("Hasitha", "Kandy, Sri Lanka", 30, 2000, false); Customer customer2 = new Customer("Asanka", "Bandarawatte, Kaluthara, Sri Lanka,", 27, 3000, false); Customer customer3 = new Customer("Perera", "Colombo, Sri Lanka", 25, 400, false); List customers = new ArrayList<>(); customers.add(customer1); customers.add(customer2); customers.add(customer3); return customers; } }
Create an interface for MBean
Java follows some standards here. If concrete implementation is "Y" interface name should be "YMBean". Both should be in same package.
package; public interface YMBean {...}
package; public class Y implements YMBean {...}
Following the convention let us define the MBean provider interface as follows.
package mBeanServer; import javax.management.MBeanException; import javax.management.openmbean.CompositeData; public interface CustomerInfoProviderMBean { String NAME = "name"; String ADDRESS = "address"; String AGE = "age"; String AMOUNT_PAID = "amountPaid"; String IS_MARRIED = "isMarried"; CompositeData[] getCustomerInformation() throws MBeanException; }
Implement MBean provider interface
To follow MBean conventions, implementation class name should be as below
public class CustomerInfoProvider implements CustomerInfoProviderMBean {...}
public class CustomerInfoProvider implements CustomerInfoProviderMBean {...}
Create CompositeDataSupport objects and add to CompositeData[]
Each CompositeData object will represent a customer. To create one, you need to give CompositeType (described below), set of attribute names and set of attribute values of a customer.@Override public CompositeData[] getCustomerInformation() throws MBeanException { ListcompositeDataList = new ArrayList<>(); List listOfCustomers = CustomerFactory.generate(); try { for (Customer customer : listOfCustomers) { Object[] itemValues = new Object[]{customer.getName(), customer.getAddress(), customer.getAge(), customer.getAmountPaid(), customer.isMarried()}; CompositeDataSupport support = new CompositeDataSupport(getCustomerDataType(), customerAttributeNames(), itemValues); compositeDataList.add(support); } } catch (OpenDataException e) { throw new MBeanException(e, "Error occurred when getting customer information via JMX"); } return compositeDataList.toArray(new CompositeData[compositeDataList.size()]); }
getCustomerDataType(), customerAttributeNames()
private static CompositeType getCustomerDataType() throws OpenDataException { CompositeType customerDataType = new CompositeType("customer data type", "dta type for customer information", customerAttributeNames(), customerAttributeDescriptions(), customerAttributeTypes()); return customerDataType; } private static String[] customerAttributeNames() { String[] attributeNames = {CustomerInfoProviderMBean.NAME, CustomerInfoProviderMBean.ADDRESS, CustomerInfoProviderMBean.AGE, CustomerInfoProviderMBean.AMOUNT_PAID, CustomerInfoProviderMBean.IS_MARRIED}; return attributeNames; } private static String[] customerAttributeDescriptions() { return customerAttributeNames(); } private static OpenType[] customerAttributeTypes() { OpenType[] attributeTypes = new OpenType[5]; attributeTypes[0] = SimpleType.STRING; //name attributeTypes[1] = SimpleType.STRING; //address attributeTypes[2] = SimpleType.INTEGER; //age attributeTypes[3] = SimpleType.LONG; //amount paid attributeTypes[4] = SimpleType.BOOLEAN; //for marital status return attributeTypes; }
Now the MBean implementation is ready.
Start a MBean Server on a given port
Start MBean server and register the MBean we prepared above.package mBeanServer; import java.io.IOException; import java.lang.management.*; import java.rmi.registry.LocateRegistry; import javax.management.*; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; public class Main { public static void main (String[] args) throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException, IOException { MBeanServer mbs = startMBeanServer(); ObjectName name = new ObjectName("mBeanServer:type=Customer"); CustomerInfoProvider mbean = new CustomerInfoProvider(); mbs.registerMBean(mbean, name); System.out.println("Waiting forever..."); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException e) { //ignore } } private static MBeanServer startMBeanServer() throws IOException { int jmxPort = 1919; LocateRegistry.createRegistry(jmxPort); MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer(); JMXServiceURL jmxUrl = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + jmxPort + "/jmxrmi"); JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxUrl, null, beanServer); connectorServer.start(); return beanServer; } }
Test and Verify if MBean is exposed correctly
Compile and run above java program.
Find process id of the java program using "jps" command.
type "jconsole " in terminal and open jconsole. You will see MBean exposed as below.
Get customer information using a JMX client
It is possible to write below program to connect and receive MBean information. All three customer information is printed. Note that instance name and attribute name can be picked up from jconsole.
package jmxClient; import mBeanServer.Customer; import mBeanServer.CustomerInfoProviderMBean; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import javax.management.openmbean.CompositeData; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import java.util.ArrayList; import java.util.List; public class CustomerInfoRetriever { public static void main(String args[]) throws Exception { CustomerInfoRetriever customerInfoRetriever = new CustomerInfoRetriever(); Listcustomers = customerInfoRetriever.getCustomers(); //print information, this can be printed as a table for (Customer customer : customers) { System.out.println(customer); } } public List getCustomers() throws Exception { List customerList = new ArrayList<>(); try { ObjectName objectName = new ObjectName("mBeanServer:type=Customer"); Object result = getMBeanConnector().getAttribute(objectName, "CustomerInformation"); if (result != null) { CompositeData[] customerInformationList = (CompositeData[]) result; for (CompositeData queueData : customerInformationList) { String name = (String) queueData.get(CustomerInfoProviderMBean.NAME); String address = (String) queueData.get(CustomerInfoProviderMBean.ADDRESS); Integer age = (Integer) queueData.get(CustomerInfoProviderMBean.AGE); Long paidAmount = (Long) queueData.get(CustomerInfoProviderMBean.AMOUNT_PAID); Boolean isMarried = (Boolean) queueData.get(CustomerInfoProviderMBean.IS_MARRIED); Customer queue = new Customer(name, address, age, paidAmount, isMarried); customerList.add(queue); } } } catch (MalformedObjectNameException | ReflectionException | MBeanException | InstanceNotFoundException e) { throw new Exception("Cannot access mBean operations to get customer information:", e); } catch (AttributeNotFoundException e) { throw new Exception("Cannot access mBean operations for message counts. Attribute not found", e); } return customerList; } private static MBeanServerConnection getMBeanConnector() { MBeanServerConnection remote = null; try { int jmxPort = 1919; JMXServiceURL target = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + "127.0.0.1" + ":" + jmxPort + "/jmxrmi"); JMXConnector connector = JMXConnectorFactory.connect(target); remote = connector.getMBeanServerConnection(); } catch (Exception e) { System.out.println("Error when connecting to MBean " + e); } return remote; } }
Running the client you will see customer information is printed to the console. This way, complex object information can be transferred using MBeans using CompositeData.
No comments:
Post a Comment