9.2 Instrumentation Patterns

Any Java resource can be managed with JMX. How it is managed can vary pretty widely. This section will discuss some of the approaches you can use when designing new applications to be manageable. We will also point out which ones are useful for enabling existing applications to be manageable.

9.2.1 Applications as MBeans

In this pattern the application is also an MBean. The time of day daemon (todd) discussed in Chapters 2 and 3 illustrates this pattern. todd is an example of a very simple application that may have some control methods as part of its interface. Applications of any complexity usually have a separate class that contains the control methods. These control methods are invoked in various ways: through a command-line interface, through a browser interface, or sometimes through a custom user application. Control classes are a good candidates to double as an MBean. All you need to do is modify the class to add " implements classnameMBean " and create a new Java interface class containing the signatures of the methods that you want to be part of the MBean.

Here is an example of a simple tack catalog application that is also a standard MBean. There are base interfaces and classes for the catalog that are not specific to selling tack: Item , Category , and Catalog . The implementations of these interfaces for the tack catalog are TackItem , TackCategory , and TackCatalog . In addition, a TackCatalogMBean interface is implemented by the TackCatalog class. The management system, through the adapters of course, will have access to only the methods in catalogMBean , and not any in the catalog interface. For the full sample code, see the appendix or the book's Web site (http://www.awprofessional.com/titles/0672324083):

  • Interface Item :

      package  jnjmx.ch9;  public interface Item {  int  getCatalogNo();        String getName();  int  getPrice();  int  getQuantity(); } 
  • Interface Category :

      package  jnjmx.ch9;  import  java.util.List; public interface Category {  int  getItemCount();        List getItems();        String getName(); } 
  • Interface Catalog :

      package  jnjmx.ch9;  import  java.io.Serializable;  import  java.util.ArrayList;  import  java.io.IOException;  import  java.util.List;  import  java.util.Map; public interface Catalog  {  public  Category createCategory(String name)  throws  CategoryExistsException;  public  Category getCategory(String name);  public  Map getCategories();  public   void  removeCategory(Category category)  throws  NoSuchCategoryException;  public   void  removeCategory(String name)  throws  NoSuchCategoryException  public   void  classify(Item item, Category category);  public   void  declassify(Item item, Category category);  public   void  increaseInventory(Item item,  int  amount);  public   void  decreaseInventory(Item item,  int  amount)  throws  InventoryUnderflowException;  public  Item createItem(String name,  int  price,  int  quantity)  throws  ItemExistsException;  public  List getItems();  public   void  removeItem(Item item)  throws  NoSuchItemException;  public   void  removeItem(  int  catalogNo)  throws  NoSuchItemException; } 
  • Interface TackCatalogMBean :

      package  jnjmx.ch9;  import  java.io.Serializable;  import  java.io.IOException;  public   interface  TackCatalogMBean  {  public  Catalog load(String name)  throws  IOException, ClassNotFoundException;  public   boolean  isAutoCheckpointing();  public   void  setAutoCheckpoint(  boolean  autoCheckpoint);  public   void  doCheckpoint();  public   int  getNumberOfItems();  public   int  getTotalSales();  public   int  getAccesses();  public   int  getInventorySize(); } 
  • Class TackItem :

      package  jnjmx.ch9;  import  java.io.Serializable;  public   class  TackItem  implements  Item, Serializable {            Catalog catalog;  int  catalogNo;            String name;  int  price;  int  quantity;  public  TackItem(              Catalog catalog,  int  catalogNo,              String name,  int  price,  int  quantity) {  this.  catalogNo = catalogNo;  this.  catalog = catalog;  this.  name = name;  this.  price = price;  this.  quantity = quantity;          }  public   int  getCatalogNo() {  return   this.  catalogNo;          }  public   boolean  equals(Object obj) {  if  (!(obj  instanceof  TackItem)) {  return   false;  }              Item item = (TackItem) obj;  return  (  this.  name.compareTo(item.getName())                     == 0)&& (  this.  price == item.getPrice()););                }  public  String getName() {  return   this.  name;                }  public   int  getPrice() {  return   this.  price;                }  public   int  getQuantity() {  return   this.  quantity;                }  public   void  setQuantity(  int  quantity) {  this.  quantity = quantity;        } } 
  • Class TackCategory :

      Package  jnjmx.ch9;  import  java.io.Serializable;  import  java.util.List;  import  java.util.ArrayList;  public   class  TackCategory  implements  Category, Serializable {        /* Categories can be: new, old, sale,         * saddles, bridles, girths, breastcollers,         * saddleblankets */        public String getCategory() {return "";};        public void setCategory(String currentCategory) {};  private  Catalog catalog;  private  String name;  private  List items;  public  TackCategory(Catalog catalog, String name) {  this.  catalog = catalog;  this.  name = name;  this.  items =  new  ArrayList();        }  public   boolean  equals(Object obj) {  if  (!(obj  instanceof  TackCategory)) {  return   false;  }  return   this.  name.compareTo(                    ((TackCategory) obj).name) == 0;        }  public   int  getItemCount() {  return   this.  items.size();        }  public  List getItems() {  return   this.  items;        }  public  String getName() {  return   this.  name;        } } 
  • Class TackCatalog :

      package  jnjmx.ch9;  import  java.io.File;  import  java.io.FileInputStream;  import  java.io.FileNotFoundException;  import  java.io.FileOutputStream;  import  java.io.IOException;  import  java.io.ObjectInputStream;  import  java.io.ObjectOutputStream;  import  java.io.Serializable;  import  java.util.ArrayList;  import  java.util.HashMap;  import  java.util.Iterator;  import  java.util.List;  import  java.util.Map;  public   class  TackCatalog  implements  Catalog, Serializable, TackCatalogMBean {  private   static   final  String FILEEXT = ".catalog";  private   final  String name;  private   boolean  autoCheckpoint =  false;   private  Map categories;  private  List items;  private   int  nextCatalogNo = 1;  private   int  accesses=0, sales=0, inventorySize=0;  public  TackCatalog(String inName) {  this.  name = inName;  this.  categories =  new  HashMap();  this.  items =  new  ArrayList();        }  public   static  TackCatalog create(String inName)  throws  IOException {                String cfn = inName + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }                TackCatalog catalog =  new  TackCatalog(inName);                ObjectOutputStream db =  new  ObjectOutputStream(  new  FileOutputStream(cf));                db.writeObject(catalog);                db.flush();                db.close();  return  catalog;        }        // load is exposed in TackCatalogMBean  public  TackCatalog load(String name)  throws  IOException, ClassNotFoundException {                String cfn = name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists() ==  false  ) {  throw   new  FileNotFoundException(cfn +                           " doesn't exist");                }                ObjectInputStream db =  new  ObjectInputStream(  new  FileInputStream(cf));                TackCatalog catalog = (TackCatalog)                    db.readObject();                db.close();  return  catalog;        }  public   synchronized  Map getCategories() {  return   this.  categories;        }  public   synchronized   void  classify(Item item,            Category category) {                List items = ((TackCategory)                    category).getItems();  if  (items.contains(item) ==  false  ) {                       items.add(item);                }        }  public   synchronized  Category createCategory(String name)  throws  CategoryExistsException {                TackCategory result =  new  TackCategory(  this,  name);  if  (  this.  categories.get(name) !=  null  ) {  throw   new  CategoryExistsException();                }  this.  categories.put(name, result);  return  result;        }  public   synchronized  Item createItem(String name,  int  price,  int  quantity)  throws  ItemExistsException {                TackItem result =  new  TackItem(  this,  nextCatalogNo++, name, price, quantity);  if  (  this.  items.contains(result)) {  throw   new  ItemExistsException();                }  this.  items.add(result);                inventorySize = inventorySize + quantity;  return  result;        }  public   synchronized   void  declassify(            Item item, Category category) {                List items = category.getItems();                items.remove(item);        }  public   synchronized   void  decreaseInventory(            Item item,  int  amount)  throws  InventoryUnderflowException {                inventorySize = inventorySize - amount;                sales = sales + amount;  int  nq = item.getQuantity() - amount;  if  (nq < 0) {  throw   new  InventoryUnderflowException();                }                ((TackItem)item).setQuantity(nq);        }  public   void  destroy()  throws  IOException {                String cfn =  this.  name + FILEEXT;                                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }        }  public   synchronized  Category getCategory(String name) {                Category result = (Category)  this.  categories.get(name);  return  result;        }  public   synchronized   void  increaseInventory(            Item item,  int  amount) {                inventorySize = inventorySize + amount;  if  (amount < 0) {  throw   new  IllegalArgumentException();                }  int  nq = item.getQuantity() + amount;                ((TackItem) item).setQuantity(nq);        }  public   synchronized  List getItems() {                accesses++;  return   this.  items;        }  public   synchronized   void  removeCategory(            Category category)  throws  NoSuchCategoryException {                removeCategory(category.getName());        }  public   synchronized   void  removeCategory(String name)  throws  NoSuchCategoryException {            Category category = (Category)  this.  categories.remove(name);  if  (category ==  null  ) {  throw   new  NoSuchCategoryException();                }        }  public   synchronized   void  removeItem(Item item)  throws  NoSuchItemException {  boolean  exists =  this.  items.remove(item);  if  (exists ==  false  ) {  throw   new  NoSuchItemException();                }                inventorySize = inventorySize -                    item.getQuantity();                removeItemFromCategories(item);        }  public   synchronized   void  removeItem(  int  catalogNo)  throws  NoSuchItemException {  boolean  removed =  false;  Iterator i =  this.  items.iterator();  while  (i.hasNext()) {                       Item item = (Item) i.next();  if  (item.getCatalogNo() == catalogno) {  this.  items.remove(item);                               inventorySize = inventorySize                                   - item.getQuantity();                               removeItemFromCategories(item);                               removed =  true;   break;  }                }  if  (removed ==  false  ) {  throw   new  NoSuchItemException();                }        }  private   void  removeItemFromCategories(Item item) {                Iterator i =  this.  categories.keySet().iterator();  while  (i.hasNext()) {                       String category = (String) i.next();                       declassify(item, (Category)  this.  categories.get(category));                }        }        // TackCatalogMBean methods  public   boolean  isAutoCheckpointing() {  return   this.  autoCheckpoint;        }  public   synchronized   void  setAutoCheckpoint(  boolean  autoCheckpoint) {  this.  autoCheckpoint = autoCheckpoint;        }  public   void  doCheckpoint() {  if  (  this.  autoCheckpoint ==  true  ) {                       checkpoint();                }        }  public   synchronized   void  checkpoint() {        }  public   int  getNumberOfItems() {  return  items.size();        }  public   int  getInventorySize() {  return  inventorySize;        }  public   int  getTotalSales() {  return  sales;        }  public   int  getAccesses() {  return  accesses;        } } 

You can see that this approach requires you to modify the base application code to accommodate JMX accessibility. Therefore, this approach works best when you are creating new applications and building in manageability. Because the changes are actually pretty minimal and not disruptive to the general flow of the application, it is possible to modify existing applications, provided that you have and own the code for JMX manageability.

The MBeanInfo instance created for this situation would look like this:

  • Attributes:

     TotalSales  NumberOfItems TotalSales Accesses InventorySize AutoCheckpoint 
  • Operations:

     load()  doCheckpoint() 
  • Notifications: none

Each attribute has at least a getter in order for the attribute to be recognized in standard MBeans.

You can also make your application a dynamic MBean. You would have to add the DynamicMBean interface to your application. One of the benefits of this approach is that you don't have to keep your application and the MBean interface in synch. If your application is not using get/set pairs for its attributes in the JavaBean style, then you might want to use the dynamic-MBean option. In the following example ( TackCatalogD ) the tack catalog application is also a dynamic MBean. You can see that we no longer implement the TackCatalogMBean interface, but we do implement the DynamicMBean interface:

  package  jnjmx.ch9;  import  java.io.File;  import  java.io.FileInputStream;  import  java.io.FileNotFoundException;  import  java.io.FileOutputStream;  import  java.io.IOException;  import  java.io.ObjectInputStream;  import  java.io.ObjectOutputStream;  import  java.io.Serializable;  import  java.util.ArrayList;  import  java.util.HashMap;  import  java.util.Iterator;  import  java.util.List;  import  java.util.Map;  import  java.lang.reflect.*;  import  javax.management.*;  public   class  TackCatalogD  implements  Catalog, Serializable, DynamicMBean {  private   static   final  String FILEEXT = ".catalog";  private   final  String name;  private   boolean  autoCheckpoint =  false;   private  Map categories;  private  List items;  private   int  nextCatalogNo = 1;  private   int  accesses=0, sales=0, inventorySize=0;  public  TackCatalogD(String inName) {  this.  name = inName;  this.  categories =  new  HashMap();  this.  items =  new  ArrayList();        }  public   static  TackCatalogD create(String inName)  throws  IOException {                String cfn = inName + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }                TackCatalogD catalog =  new  TackCatalogD(inName);                ObjectOutputStream db =  new  ObjectOutputStream(  new  FileOutputStream(cf));                db.writeObject(catalog);                db.flush();                db.close();  return  catalog;        }        // load is exposed in TackCatalogMBean  public  Catalog load(String name)  throws  IOException, ClassNotFoundException {                String cfn = name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists() ==  false  ) {  throw   new  FileNotFoundException(cfn + " doesn't exist");                }                ObjectInputStream db =  new  ObjectInputStream(  new  FileInputStream(cf));                TackCatalogD catalog = (TackCatalogD) db.readObject();                db.close();  return  catalog;        }  public   synchronized  Map getCategories() {  return   this.  categories;        }  public   synchronized   void  classify(Item item, Category category) {                List items = ((TackCategory) category).getItems();  if  (items.contains(item) ==  false  ) {                       items.add(item);                }        }  public   synchronized  Category createCategory(String name)  throws  CategoryExistsException {                TackCategory result =  new  TackCategory(  this,  name);  if  (  this.  categories.get(name) !=  null  ) {  throw   new  CategoryExistsException();                }  this.  categories.put(name, result);  return  result;        }  public   synchronized  Item createItem(String name,  int  price,  int  quantity)  throws  ItemExistsException {                TackItem result =  new  TackItem(  this,  nextCatalogNo++, name,                    price, quantity);  if  (  this.  items.contains(result)) {  throw   new  ItemExistsException();                }  this.  items.add(result);                inventorySize = inventorySize + quantity;  return  result;        }  public   synchronized   void  declassify(Item item, Category category) {                List items = category.getItems();                items.remove(item);        }  public   synchronized   void  decreaseInventory(Item item,  int  amount)  throws  InventoryUnderflowException {                inventorySize = inventorySize - amount;                sales = sales + amount;  int  nq = item.getQuantity() - amount;  if  (nq < 0) {  throw   new  InventoryUnderflowException();                }                ((TackItem)item).setQuantity(nq);        }  public   void  destroy()  throws  IOException {                String cfn =  this.  name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }        }  public   synchronized  Category getCategory(String name) {                Category result = (Category)  this.  categories.get(name);  return  result;        }  public   synchronized   void  increaseInventory(Item item,  int  amount) {                inventorySize = inventorySize + amount;  if  (amount < 0) {  throw   new  IllegalArgumentException();                }  int  nq = item.getQuantity() + amount;                ((TackItem) item).setQuantity(nq);        }  public   synchronized  List getItems() {                accesses++;  return   this.  items;        }  public   synchronized   void  removeCategory(Category category)  throws  NoSuchCategoryException {                removeCategory(category.getName());        }  public   synchronized   void  removeCategory(String name)  throws  NoSuchCategoryException {                Category category = (Category)  this.  categories.remove(name);  if  (category ==  null  ) {  throw   new  NoSuchCategoryException();                }        }  public   synchronized   void  removeItem(Item item)  throws  NoSuchItemException {  boolean  exists =  this.  items.remove(item);  if  (exists ==  false  ) {  throw   new  NoSuchItemException();                }                inventorySize = inventorySize - item.getQuantity();                removeItemFromCategories(item);        }  public   synchronized   void  removeItem(  int  catalogno)  throws  NoSuchItemException {  boolean  removed =  false;  Iterator i =  this.  items.iterator();  while  (i.hasNext()) {                       Item item = (Item) i.next();  if  (item.getCatalogNo() == catalogno) {  this.  items.remove(item);                             inventorySize = inventorySize - item.getQuantity();                             removeItemFromCategories(item);                             removed =  true;   break;  }                }  if  (removed ==  false  ) {  throw   new  NoSuchItemException();                }        }  private   void  removeItemFromCategories(Item item) {                Iterator i =  this.  categories.keySet().iterator();  while  (i.hasNext()) {                     String category = (String) i.next();                     declassify(item, (Category)  this.  categories.get(category));                }        }        // TackCatalogMBean methods  public   boolean  isAutoCheckpointing() {  return   this.  autoCheckpoint;        }  public   void  setAutoCheckpoint(  boolean  autoCheckpoint) {  this.  autoCheckpoint = autoCheckpoint;        }  public   void  doCheckpoint() {  if  (  this.  autoCheckpoint ==  true  ) {                       checkpoint();                }        }  public   void  checkpoint() {        }        /* MBean methods */  public  Object getAttribute(String attributeName)  throws  AttributeNotFoundException,                 MBeanException,                 ReflectionException        {  if  ( attributeName.equals("AutoCheckpointing") )  return   new  Boolean(autoCheckpoint);  if  ( attributeName.equals("NumberOfItems") )  return   new  Integer(items.size());  if  ( attributeName.equals("Accesses") )  return   new  Integer(accesses);  if  ( attributeName.equals("TotalSales") )  return   new  Integer(sales);  if  ( attributeName.equals("InventorySize") )  return   new  Integer(inventorySize);  else   throw  (  new  AttributeNotFoundException("Attribute " +                    attributeName + " not found in " +  this.  getClass().getName()));        }  public  AttributeList getAttributes(String[] attributes) {                // In this simple example we always return all attributes                // in a production example, you need to only return the                // attributes in the list passed in                AttributeList returnList =  new  AttributeList();  if  ( (attributes ==  null  )  (attributes.length == 0)                      (attributes.length > 0)) {                       returnList.add(  new  Attribute("AutoCheckpointing",  new  Boolean(autoCheckpoint)) );                       returnList.add(  new  Attribute("NumberOfItems",  new  Integer(items.size())));                       returnList.add(  new  Attribute("Accesses",  new  Integer(accesses)) );                       returnList.add(  new  Attribute("TotalSales",  new  Integer(sales)) );                       returnList.add(  new  Attribute("InventorySize",  new  Integer(inventorySize)) );                }  return  returnList;        }  public   void  setAttribute(Attribute newAttribute)  throws  AttributeNotFoundException,                 InvalidAttributeValueException,                 MBeanException,                 ReflectionException        {                String attributeName = newAttribute.getName();  if  ( attributeName.equals("Autocheckpointing") )                       autoCheckpoint =                           ((Boolean)(newAttribute.getValue())).booleanValue();  else   if  ( (attributeName.equals("NumberOfItems"))                                  (attributeName.equals("TotalSales" ))                                  (attributeName.equals("Accesses" ))                                  (attributeName.equals("InventorySize")) ) {  throw  (  new  AttributeNotFoundException("Attribute " + name +                          " not updateable in " +  this.  getClass().getName()));                }  else   throw  (  new  AttributeNotFoundException("Attribute " + name +                          " not found in " +  this.  getClass().getName()));        }  public  AttributeList setAttributes(AttributeList newAttributes) {                AttributeList returnList =  new  AttributeList();  if  ( newAttributes ==  null  newAttributes.isEmpty() )  return  returnList;  for  ( Iterator I=newAttributes.iterator(); I.hasNext(); ) {                       Attribute newAttribute= (Attribute)I.next();  try  {                            setAttribute(newAttribute);                            returnList.add(  new  Attribute(newAttribute.getName(),                                           newAttribute.getValue() ) );                       }  catch  ( Exception e ) {                       }  return  returnList;                }  return  returnList;        }  public  Object invoke(String operationName, Object[] parms, String[] sig) {  try  {  if  ( operationName.equals("doCheckpoint") ) {                       checkpoint();                }  else   if  ( operationName.equals("load") ) {  try  {  return  load((String)(parms[0]));                       }  catch  (Exception e) {                               System.out.println("Load of TackCatalog failed");                       }  return   null;  }  else  {  throw   new  ReflectionException(  new  NoSuchMethodException(operationName),                               "Cannot find the operation " + operationName +                               " in TackCatalog");                }            }  catch  (ReflectionException e) {                   System.out.println("Load of TackCatalog failed");            }  return   null;  }  public  MBeanInfo getMBeanInfo() {        /* attributes: boolean AutoCheckpointing r/w,        int numberOfItems r/o,        int Accesses r/o,        int Sales r/o        int InventorySize r/o */        /* operations: void saveCatalog,        Catalog load,        void doCheckpoint */        /* notifications: none */                String catDescription = "Tack Catalog Dynamic MBean";                MBeanAttributeInfo[] catAttributes =  new  MBeanAttributeInfo[5];                catAttributes[0] =  new  MBeanAttributeInfo("AutoCheckpointing",                           "java.lang.Boolean",                           "AutoCheckpointing: boolean value if checkpointing" +                                " of catalog is done automatically",  true,   true,   false  );                catAttributes[1] =  new  MBeanAttributeInfo("NumberOfItems",                               "java.lang.Integer",                               "NumberOfItems: number of items in the catalog",  true,   false,   false  );                catAttributes[2] =  new  MBeanAttributeInfo("Accesses",                               "java.lang.Integer",                               "Accesses: number of time items in the catalog" +                                   " database have been listed",  true,   false,   false  );                catAttributes[3] =  new  MBeanAttributeInfo("TotalSales",                               "java.lang.Integer",                               "Sales: number of items sold",  true,   false,   false  );                catAttributes[4] =  new  MBeanAttributeInfo("InventorySize",                               "java.lang.Integer",                               "InventorySize: Total number of all items " +                                          "currently in inventory",  true,   true,   false  );                MBeanConstructorInfo[] catConstructors =  new  MBeanConstructorInfo[2];                Constructor[] constructors =  this.  getClass().getConstructors();                catConstructors[0] =  new  MBeanConstructorInfo(                               "tackCatalog(dbName): Constructs a tackCatalog " +                                      "from an existing database",                               constructors[0]);                MBeanOperationInfo[] catOperations =  new  MBeanOperationInfo[2];                MBeanParameterInfo[] params =  new  MBeanParameterInfo[0];                catOperations[0] =  new  MBeanOperationInfo("doCheckpoint",                               "doCheckpoint(): saves the catalog to a file",                               params ,                               "void",                                MBeanOperationInfo.ACTION);                params =  new  MBeanParameterInfo[] {                               (  new  MBeanParameterInfo("name",                               "java.lang.String",                               "Tack Catalog name"))};                catOperations[1] =  new  MBeanOperationInfo("load",                               "load(dbName): loads the catalog from a file",                               params ,                               "void",                               MBeanOperationInfo.ACTION);                MBeanNotificationInfo[] catNotifications =  new  MBeanNotificationInfo[0];                String className =  this.  getClass().getName();                MBeanInfo catMBeanInfo =  new  MBeanInfo(className,                               catDescription,                               catAttributes,                               catConstructors,                               catOperations,                               catNotifications );  return  catMBeanInfo;        }// getMBeanInfo }     // class 

Note that the getAttribute() method doesn't delegate to the original get methods. We could have retained the MBean interface methods from the previous example and simply added the dynamic-MBean delegators, but we wanted to show an example of a case in which actual methods on the actual MBean may not exist.

Making an application that is also a model MBean is a little more complicated and less common. For the sake of completeness, we will discuss it. One way to do this is to have your application inherit from the model MBean implementation that accompanies the MBeanServer. Another way is to have your application implement the model MBean interfaces. However, we don't recommend the latter approach. Because Model MBeans may be specific to a particular MBeanServer, you may have some functional mismatch between your model MBean and the MBeanServer. If you ever need to extend or override the behavior of a model MBean, you should inherit from the one provided with the MBeanServer rather than implement one from scratch. Model MBeans are much better suited to remaining separate from the applications, as we will see in the next section.

9.2.2 MBeans on Behalf of Applications

Sometimes it is not appropriate to have the application code serve the dual purpose of application and MBean. In such cases you will have a separate MBean that manages the application. This MBean can be created and registered by the application itself, another class, or an adapter. These options are discussed in Section 9.2.3.

MBeans that manage separate applications should be fully implemented and can delegate some of the methods to the application itself. This delegation can be very useful when the management methods of the application are dispersed among multiple classes or distributed components . The methods in the MBean could also do out-of- band work like reading logs or interacting with system APIs, JVM APIs, or other management systems.

Standard MBeans are easy to create for this design. Standard MBeans can be used when the management interface, MBeanInfo , is stable and completely known at runtime. The standard MBean method implementations would invoke the appropriate methods on your application.

The standard MBean in the following example has a method that invokes methods on the application and methods that perform functions in their own right. We saw this application of MBeans for the Apache examples in Chapters 2 and 3. We are going to refactor the TackCatalog example into the following TackCatalogSS example, but now there are two classes: TackCatalogSS and CatalogManager , an MBean that has an MBean interface CatalogManagerMBean . Notice that we had to add a method ” getStats() ”and expose the existing load() and the AutoCheckpointing methods to TackCatalogSS so that CatalogManager would have the same functionality as when the two were part of the same class. We consolidated getAccesses() , getTotalSales() , getInventorySize() , and getNumberOfItems() into one method. This example is somewhat contrived because it would be better programming practice to have a get method for each as we did in the original TackCatalog , but we did this to make sure you see that there does not have to be one-to-one correspondence between the managed resource ( TackCatalogSS ) and the standard MBean ( CatalogManager ).

  package  jnjmx.ch9;  import  java.io.File;  import  java.io.FileInputStream;  import  java.io.FileNotFoundException;  import  java.io.FileOutputStream;  import  java.io.IOException;  import  java.io.ObjectInputStream;  import  java.io.ObjectOutputStream;  import  java.io.Serializable;  import  java.util.ArrayList;  import  java.util.HashMap;  import  java.util.Iterator;  import  java.util.List;  import  java.util.Map; // Separate Standard MBean Tack Catalog Example  public   class  TackCatalogSS  implements  Catalog, Serializable {  private   static   final  String FILEEXT = ".catalog";  private   final  String name;  private   boolean  autoCheckpoint =  false;   private  Map categories;  private  List items;  private   int  nextCatalogNo = 1;  private   int  accesses=0, sales=0, inventorySize=0;  public  TackCatalogSS(String inName) {  this.  name = inName;  this.  categories =  new  HashMap();  this.  items =  new  ArrayList();        }  public   static  TackCatalogSS create(String inName)  throws  IOException {                String cfn = inName + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }                TackCatalogSS catalog =  new  TackCatalogSS(inName);                ObjectOutputStream db =  new  ObjectOutputStream(  new  FileOutputStream(cf));                db.writeObject(catalog);                db.flush();                db.close();  return  catalog;        }        // Load is exposed in TackCatalogMBean  public  TackCatalogSS load(String name)  throws  IOException, ClassNotFoundException {                String cfn = name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists() ==  false  ) {  throw   new  FileNotFoundException(cfn + " doesn't exist");                }                ObjectInputStream db =  new  ObjectInputStream(  new  FileInputStream(cf));                TackCatalogSS catalog = (TackCatalogSS) db.readObject();                db.close();  return  catalog;        }  public   synchronized  Map getCategories() {  return   this.  categories;        }  public   synchronized   void  classify(Item item, Category category) {                List items = ((TackCategory) category).getItems();  if  (items.contains(item) ==  false  ) {                       items.add(item);                }        }  public   synchronized  Category createCategory(String name)  throws  CategoryExistsException {                TackCategory result =  new  TackCategory(  this,  name);  if  (  this.  categories.get(name) !=  null  ) {  throw   new  CategoryExistsException();                }  this.  categories.put(name, result);  return  result;        }  public   synchronized  Item createItem(String name,  int  price,  int  quantity)  throws  ItemExistsException {                TackItem result =  new  TackItem(  this,  nextCatalogNo++, name,                    price, quantity);  if  (  this.  items.contains(result)) {  throw   new  ItemExistsException();                }  this.  items.add(result);                inventorySize = inventorySize + quantity;  return  result;        }  public   synchronized   void  declassify(Item item, Category category) {                List items = category.getItems();                items.remove(item);        }  public   synchronized   void  decreaseInventory(Item item,  int  amount)  throws  InventoryUnderflowException {                inventorySize = inventorySize - amount;                sales = sales + amount;  int  nq = item.getQuantity() - amount;  if  (nq < 0) {  throw   new  InventoryUnderflowException();                }                ((TackItem)item).setQuantity(nq);        }  public   void  destroy()  throws  IOException {                String cfn =  this.  name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }        }  public   synchronized  Category getCategory(String name) {                Category result = (Category)  this.  categories.get(name);  return  result;        }  public   synchronized   void  increaseInventory(Item item,  int  amount) {                inventorySize = inventorySize + amount;  if  (amount < 0) {  throw   new  IllegalArgumentException();                }  int  nq = item.getQuantity() + amount;                ((TackItem) item).setQuantity(nq);        }  public   synchronized  List getItems() {                accesses++;  return   this.  items;        }  public   synchronized   void  removeCategory(Category category)  throws  NoSuchCategoryException {                removeCategory(category.getName());        }  public   synchronized   void  removeCategory(String name)  throws  NoSuchCategoryException {                    Category category = (Category)  this.  categories.remove(name);  if  (category ==  null  ) {  throw   new  NoSuchCategoryException();                }        }  public   synchronized   void  removeItem(Item item)  throws  NoSuchItemException {  boolean  exists =  this.  items.remove(item);  if  (exists ==  false  ) {  throw   new  NoSuchItemException();                }                inventorySize = inventorySize - item.getQuantity();                removeItemFromCategories(item);        }  public   synchronized   void  removeItem(  int  catalogno)  throws  NoSuchItemException {  boolean  removed =  false;  Iterator i =  this.  items.iterator();  while  (i.hasNext()) {                       Item item = (Item) i.next();  if  (item.getCatalogNo() == catalogno) {  this.  items.remove(item);                               inventorySize = inventorySize -                                   item.getQuantity();                               removeItemFromCategories(item);                               removed =  true;   break;  }                }  if  (removed ==  false  ) {  throw   new  NoSuchItemException();                }        }  private   void  removeItemFromCategories(Item item) {                Iterator i =  this.  categories.keySet().iterator();  while  (i.hasNext()) {                     String category = (String) i.next();                     declassify(item, (Category)  this.  categories.get(category));                }        }        // TackCatalogMBean Methods  public   boolean  isAutoCheckpointing() {  return   this.  autoCheckpoint;        }  public   synchronized   void  setAutoCheckpoint(  boolean  autoCheckpoint) {  this.  autoCheckpoint = autoCheckpoint;        }  public   void  doCheckpoint() {  if  (  this.  autoCheckpoint ==  true  ) {                       checkpoint();                }        }  public   synchronized   void  checkpoint() {                // this method would write the data from the                // catalog into a database        }  public   int  getStats(String statName) {  if  (statName.equals("NumberOfItems"))  return  items.size();  else   if  (statName.equals("InventorySize"))  return  inventorySize;  else   if  (statName.equals("getTotalSales"))  return  sales;  else   if  (statName.equals("Accesses"))  return  accesses;  else   return  0;        } } 

Here is the MBean implementation for TackCatalogSS :

  package  jnjmx.ch9;  import  java.io.Serializable;  import  java.io.IOException;  public   interface  CatalogManagerMBean  {  public  TackCatalogSS load(String name)  throws  IOException, ClassNotFoundException;  public   boolean  isAutoCheckpointing();  public   void  setAutoCheckpoint(  boolean  autoCheckpoint);  public   void  doCheckpoint();  public   int  getNumberOfItems();  public   int  getTotalSales();  public   int  getAccesses();  public   int  getInventorySize(); } 

Here is the MBean implementation for CatalogManagerMBean , CatalogManager :

  package  jnjmx.ch9;  import  java.io.IOException;  public   class  CatalogManager  implements  CatalogManagerMBean {        // TackCatalogMBean Methods        TackCatalogSS catalog;  public  CatalogManager (Catalog inCatalog) {                catalog = (TackCatalogSS)inCatalog;        }  public  TackCatalogSS load(String name)  throws  IOException, ClassNotFoundException{  return  (catalog.load(name));        }  public   boolean  isAutoCheckpointing() {  return  catalog.isAutoCheckpointing();        }  public   synchronized   void  setAutoCheckpoint(  boolean  autoCheckpoint) {                catalog.setAutoCheckpoint(autoCheckpoint);        }  public   void  doCheckpoint() {  if  (catalog.isAutoCheckpointing() ==  true  ) {                       catalog.doCheckpoint();                }        }  public   int  getNumberOfItems() {  return  catalog.getStats("NumberOfItems");        }  public   int  getInventorySize() {  return  catalog.getStats("InventorySize");        }  public   int  getTotalSales() {  return  catalog.getStats("TotalSales");        }  public   int  getAccesses() {  return  catalog.getStats("Accesses");        } } 

The instance of MBeanInfo generated by the MBeanServer would be the same as before:

  • Attributes:

     TotalSales  NumberOfItems TotalSales Accesses InventorySize AutoCheckpoint 
  • Operations:

     load()  doCheckpoint() 
  • Notifications: none

Dynamic MBeans are suitable for this design as well. Dynamic MBeans should be used when the application doesn't follow the JavaBean design pattern or when you might not know the exact management interface during development. Dynamic MBeans allow their MBeanInfo to be set during development or runtime. One common scenario is to create dynamic MBeans to represent management functionality already developed in an existing management application. In such cases, as management objects or managed resources in the existing application are instantiated , a DynamicMBean implementation is instantiated as well. The MBeanInfo instance would be created by introspection of the existing management object or managed resource.

Suppose that we already have a management application for a Web server farm that uses three different brands of Web servers. We could create a dynamic MBean for the Web server in general. As each Web server was instantiated, the MBeanInfo instance specific to that server would be created. Here is the TackCatalogDD example using a separate, external dynamic MBean; it is essentially the same as TackCatalogSS . The real differences come in CatalogManagerDD :

  package  jnjmx.ch9;  import  java.io.File;  import  java.io.FileInputStream;  import  java.io.FileNotFoundException;  import  java.io.FileOutputStream;  import  java.io.IOException;  import  java.io.ObjectInputStream;  import  java.io.ObjectOutputStream;  import  java.io.Serializable;  import  java.util.ArrayList;  import  java.util.HashMap;  import  java.util.Iterator;  import  java.util.List;  import  java.util.Map;  import  java.lang.reflect.*;  import  javax.management.*; // Separate Dynamic MBean Example  public   class  TackCatalogDD  implements  Catalog, Serializable {  private   static   final  String FILEEXT = ".catalog";  private   final  String name;  private   boolean  autoCheckpoint =  false;   private  Map categories;  private  List items;  private   int  nextCatalogNo = 1;  private   int  accesses=0, sales=0, inventorySize=0;  public  TackCatalogDD(String inName) {  this.  name = inName;  this.  categories =  new  HashMap();  this.  items =  new  ArrayList();        }  public   static  TackCatalogDD create(String inName)  throws  IOException {                String cfn = inName + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }                TackCatalogDD catalog =  new  TackCatalogDD(inName);                ObjectOutputStream db =  new  ObjectOutputStream(  new  FileOutputStream(cf));                db.writeObject(catalog);                db.flush();                db.close();  return  catalog;        }        // load is exposed in TackCatalogMBean  public  Catalog load(String name)  throws  IOException, ClassNotFoundException {                String cfn = name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists() ==  false  ) {  throw   new  FileNotFoundException(cfn + " doesn't exist");                }                ObjectInputStream db =  new  ObjectInputStream(  new  FileInputStream(cf));                TackCatalogDD catalog = (TackCatalogDD) db.readObject();                db.close();  return  catalog;        }  public   synchronized  Map getCategories() {  return   this.  categories;        }  public   synchronized   void  classify(Item item, Category category) {                List items = ((TackCategory) category).getItems();  if  (items.contains(item) ==  false  ) {                       items.add(item);                }        }  public   synchronized  Category createCategory(String name)  throws  CategoryExistsException {                TackCategory result =  new  TackCategory(  this,  name);  if  (  this.  categories.get(name) !=  null  ) {  throw   new  CategoryExistsException();                }  this.  categories.put(name, result);  return  result;        }  public   synchronized  Item createItem(String name,  int  price,  int  quantity)  throws  ItemExistsException {                TackItem result =  new  TackItem(  this,  nextCatalogNo++, name,                    price, quantity);  if  (  this.  items.contains(result)) {  throw   new  ItemExistsException();                }  this.  items.add(result);                inventorySize = inventorySize + quantity;  return  result;        }  public   synchronized   void  declassify(Item item, Category category) {                List items = category.getItems();                items.remove(item);        }  public   synchronized   void  decreaseInventory(Item item,  int  amount)  throws  InventoryUnderflowException {                inventorySize = inventorySize - amount;                sales = sales + amount;  int  nq = item.getQuantity() - amount;  if  (nq < 0) {  throw   new  InventoryUnderflowException();                }                ((TackItem)item).setQuantity(nq);        }  public   void  destroy()  throws  IOException {                String cfn =  this.  name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }        }  public   synchronized  Category getCategory(String name) {                Category result = (Category)  this.  categories.get(name);  return  result;        }  public   synchronized   void  increaseInventory(Item item,  int  amount) {                inventorySize = inventorySize + amount;  if  (amount < 0) {  throw   new  IllegalArgumentException();                }  int  nq = item.getQuantity() + amount;                ((TackItem) item).setQuantity(nq);        }  public   synchronized  List getItems() {                accesses++;  return   this.  items;        }  public   synchronized   void  removeCategory(Category category)  throws  NoSuchCategoryException {                removeCategory(category.getName());        }  public   synchronized   void  removeCategory(String name)  throws  NoSuchCategoryException {                Category category = (Category)  this.  categories.remove(name);  if  (category ==  null  ) {  throw   new  NoSuchCategoryException();                }        }  public   synchronized   void  removeItem(Item item)  throws  NoSuchItemException {  boolean  exists =  this.  items.remove(item);  if  (exists ==  false  ) {  throw   new  NoSuchItemException();                }                inventorySize = inventorySize - item.getQuantity();                removeItemFromCategories(item);        }  public   synchronized   void  removeItem(  int  catalogno)  throws  NoSuchItemException {  boolean  removed =  false;  Iterator i =  this.  items.iterator();  while  (i.hasNext()) {                       Item item = (Item) i.next();  if  (item.getCatalogNo() == catalogno) {  this.  items.remove(item);                             inventorySize = inventorySize - item.getQuantity();                             removeItemFromCategories(item);                             removed =  true;   break;  }                }  if  (removed ==  false  ) {  throw   new  NoSuchItemException();                }        }  private   void  removeItemFromCategories(Item item) {                Iterator i =  this.  categories.keySet().iterator();  while  (i.hasNext()) {                     String category = (String) i.next();                     declassify(item, (Category)  this.  categories.get(category));                }        }        // TackCatalogMBean methods  public   boolean  isAutoCheckpointing() {  return   this.  autoCheckpoint;        }  public   void  setAutoCheckpoint(  boolean  autoCheckpoint) {  this.  autoCheckpoint = autoCheckpoint;        }  public   void  doCheckpoint() {  if  (  this.  autoCheckpoint ==  true  ) {                       checkpoint();                }        }  public   void  checkpoint() {        }  public   int  getStats(String statName) {  if  (statName.equals("NumberOfItems"))  return  items.size();  else   if  (statName.equals("InventorySize"))  return  inventorySize;  else   if  (statName.equals("getTotalSales"))  return  sales;  else   if  (statName.equals("Accesses"))  return  accesses;  else   return  0;        } }     // class 

Here is the Dynamic MBean ( CatalogManagerDD ) for TackCatalogDD . Notice that no separate interface is necessary:

  package  jnjmx.ch9;  import  java.io.IOException;  import  java.lang.reflect.*;  import  java.util.Iterator;  import  javax.management.*;  public   class  CatalogManagerDD  implements  DynamicMBean  {        // TackCatalogMBean methods        TackCatalogDD catalog;  public  CatalogManagerDD (Catalog inCatalog) {                catalog = (TackCatalogDD)inCatalog;        }        /* MBean methods */  public  Object getAttribute(String attributeName)  throws  AttributeNotFoundException,                 MBeanException,                 ReflectionException        {  if  ( attributeName.equals("AutoCheckpointing") )  return   new  Boolean(catalog.isAutoCheckpointing());  if  ( attributeName.equals("NumberOfItems") )  return   new  Integer(catalog.getStats("NumberOfItems"));  if  ( attributeName.equals("Accesses") )  return   new  Integer(catalog.getStats("Accesses"));  if  ( attributeName.equals("TotalSales") )  return   new  Integer(catalog.getStats("TotalSales"));  if  ( attributeName.equals("InventorySize") )  return   new  Integer(catalog.getStats("InventorySize"));  else   throw  (  new  AttributeNotFoundException("Attribute " +                    attributeName + " not found in " +  this.  getClass().getName()));        }  public  AttributeList getAttributes(String[] attributes) {                // In this simple example we always return all attributes                // in a production example, you need to only return the                // attributes in the list passed in                AttributeList returnList =  new  AttributeList();  if  ( (attributes ==  null  )  (attributes.length == 0)                      (attributes.length > 0)) {                       returnList.add(  new  Attribute("AutoCheckpointing",  new  Boolean(catalog.isAutoCheckpointing())) );                       returnList.add(  new  Attribute("NumberOfItems",  new  Integer(catalog.getStats("NumberOfItems"))));                       returnList.add(  new  Attribute("Accesses",  new  Integer(catalog.getStats("Accesses"))) );                       returnList.add(  new  Attribute("TotalSales",  new  Integer(catalog.getStats("TotalSales"))) );                       returnList.add(  new  Attribute("InventorySize",  new  Integer(catalog.getStats("InventorySize"))) );                }  return  returnList;        }  public   void  setAttribute(Attribute newAttribute)  throws  AttributeNotFoundException,                 InvalidAttributeValueException,                 MBeanException,                 ReflectionException        {                String attributeName = newAttribute.getName();  if  ( attributeName.equals("Autocheckpointing") )                       catalog.setAutoCheckpoint(((Boolean)                           (newAttribute.getValue())).booleanValue());  else   if  ( (attributeName.equals("NumberOfItems"))                                  (attributeName.equals("TotalSales" ))                                  (attributeName.equals("Accesses" ))                                  (attributeName.equals("InventorySize")) ) {  throw  (  new  AttributeNotFoundException("Attribute " +                           attributeName + " not updateable in " +  this.  getClass().getName()));                }  else   throw  (  new  AttributeNotFoundException("Attribute " +                           attributeName + " not found in " +  this.  getClass().getName()));        }  public  AttributeList setAttributes(AttributeList newAttributes) {                AttributeList returnList =  new  AttributeList();  if  ( newAttributes ==  null  newAttributes.isEmpty() )  return  returnList;  for  ( Iterator I=newAttributes.iterator(); I.hasNext(); ) {                       Attribute newAttribute= (Attribute)I.next();  try  {                             setAttribute(newAttribute);                               returnList.add(  new  Attribute(newAttribute.getName(),                                      newAttribute.getValue() ) );                       }  catch  ( Exception e ) {                       }  return  returnList;                }  return  returnList;        }  public  Object invoke(String operationName, Object[] parms, String[] sig) {  try  {  if  ( operationName.equals("doCheckpoint") ) {                           catalog.doCheckpoint();                    }  else   if  ( operationName.equals("load") ) {  try  {  return  catalog.load((String)(parms[0]));                           }  catch  (Exception e) {                               System.out.println("Load of TackCatalog failed");                           }  return   null;  }  else  {  throw   new  ReflectionException(  new  NoSuchMethodException(operationName),                               "Cannot find the operation " + operationName +                               " in TackCatalog");                    }                }  catch  (ReflectionException e) {                       System.out.println("Load of TackCatalog failed");                }  return   null;  }  public  MBeanInfo getMBeanInfo() {        /* attributes: boolean AutoCheckpointing r/w,        int numberOfItems r/o,        int Accesses r/o,        int Sales r/o        int InventorySize r/o */        /* operations: void saveCatalog,        Catalog load,        void doCheckpoint */        /* notifications: none */                String catDescription = "Tack Catalog Dynamic MBean";                MBeanAttributeInfo[] catAttributes =  new  MBeanAttributeInfo[5];                catAttributes[0] =  new  MBeanAttributeInfo("AutoCheckpointing",                           "java.lang.Boolean",                              "AutoCheckpointing: boolean value if checkpointing " +                                  "of catalog is done automatically",  true,   true,   false  );                catAttributes[1] =  new  MBeanAttributeInfo("NumberOfItems",                               "java.lang.Integer",                               "NumberOfItems: number of items in the catalog",  true,   false,   false  );                catAttributes[2] =  new  MBeanAttributeInfo("Accesses",                               "java.lang.Integer",                               "Accesses: number of time items in the catalog " +                                       "database have been listed",  true,   false,   false  );                catAttributes[3] =  new  MBeanAttributeInfo("TotalSales",                               "java.lang.Integer",                               "Sales: number of items sold",  true,   false,   false  );                catAttributes[4] =  new  MBeanAttributeInfo("InventorySize",                               "java.lang.Integer",                           "InventorySize: Total number of all items currently " +                               "in inventory",  true,   true,   false  );                MBeanConstructorInfo[] catConstructors =  new  MBeanConstructorInfo[2];                Constructor[] constructors = catalog.getClass().getConstructors();                 catConstructors[0] =  new  MBeanConstructorInfo(                               "tackCatalog(dbName): Constructs a tackCatalog " +                               "from an existing database",                               constructors[0]);                MBeanOperationInfo[] catOperations =  new  MBeanOperationInfo[2];                MBeanParameterInfo[] params =  new  MBeanParameterInfo[0];                catOperations[0] =  new  MBeanOperationInfo("doCheckpoint",                                 "doCheckpoint(): saves the catalog to a file",                                 params ,                                 "void",                                 MBeanOperationInfo.ACTION);                 params =  new  MBeanParameterInfo[] {                               (  new  MBeanParameterInfo("name",                               "java.lang.String",                               "Tack Catalog name"))};                catOperations[1] =  new  MBeanOperationInfo("load",                                 "load(dbName): loads the catalog from a file",                                 params ,                                 "void",                                 MBeanOperationInfo.ACTION);                MBeanNotificationInfo[] catNotifications =  new  MBeanNotificationInfo[0];                String className =  this.  getClass().getName();                MBeanInfo catMBeanInfo =  new  MBeanInfo(className,                                               catDescription,                                               catAttributes,                                               catConstructors,                                               catOperations,                                               catNotifications );  return  catMBeanInfo;        }// getMBeanInfo } 

Dynamic MBeans are also interesting as separate MBeans when the application has a separate management class. It is easy to write a dynamic MBean that reflects everything in the separate management class. In fact it is possible to take a TackCatalogDD application with a CatalogManager class and write a dynamic MBean implementation that sets its MBeanInfo directly from CatalogManager using reflection at runtime.

Model MBeans are best suited to being separate MBeans for the application. They are already implemented and have a built-in ability to delegate operations to the application. Chapter 4, on model MBeans, gave an example of this approach. Here is the TackCatalog application using model MBeans:

  package  jnjmx.ch9;  import  java.io.File;  import  java.io.FileInputStream;  import  java.io.FileNotFoundException;  import  java.io.FileOutputStream;  import  java.io.IOException;  import  java.io.ObjectInputStream;  import  java.io.ObjectOutputStream;  import  java.io.Serializable;  import  java.util.ArrayList;  import  java.util.HashMap;  import  java.util.Iterator;  import  java.util.List;  import  java.util.Map;  import  java.lang.reflect.*;  import  javax.management.*;  import  javax.management.modelmbean.*; // Separate Model MBean Example  public   class  TackCatalogMM  implements  Catalog, Serializable {  private   static   final  String FILEEXT = ".catalog";  private   final  String name;  private   boolean  autoCheckpoint =  false;   private  Map categories;  private  List items;  private   int  nextCatalogNo = 1;  private   int  accesses=0, sales=0, inventorySize=0;  public  TackCatalogMM(String inName) {  this.  name = inName;  this.  categories =  new  HashMap();  this.  items =  new  ArrayList();        }  public   static  TackCatalogMM create(String inName)  throws  IOException {                String cfn = inName + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }                TackCatalogMM catalog =  new  TackCatalogMM(inName);                ObjectOutputStream db =  new  ObjectOutputStream(  new  FileOutputStream(cf));                db.writeObject(catalog);                db.flush();                db.close();  return  catalog;        }        // Load is exposed in TackCatalogMBean  public  Catalog load(String name)  throws  IOException, ClassNotFoundException {                String cfn = name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists() ==  false  ) {  throw   new  FileNotFoundException(cfn + " doesn't exist");                }                ObjectInputStream db =  new  ObjectInputStream(  new  FileInputStream(cf));                TackCatalogMM catalog = (TackCatalogMM) db.readObject();                db.close();  return  catalog;        }  public   synchronized  Map getCategories() {  return   this.  categories;        }  public   synchronized   void  classify(Item item, Category category) {                List items = ((TackCategory) category).getItems();  if  (items.contains(item) ==  false  ) {                       items.add(item);                }        }  public   synchronized  Category createCategory(String name)  throws  CategoryExistsException {                TackCategory result =  new  TackCategory(  this,  name);  if  (  this.  categories.get(name) !=  null  ) {  throw   new  CategoryExistsException();                }  this.  categories.put(name, result);  return  result;        }  public   synchronized  Item createItem(String name,  int  price,  int  quantity)  throws  ItemExistsException {                TackItem result =  new  TackItem(  this,  nextCatalogNo++, name,                    price, quantity);  if  (  this.  items.contains(result)) {  throw   new  ItemExistsException();                }  this.  items.add(result);                inventorySize = inventorySize + quantity;  return  result;        }  public   synchronized   void  declassify(Item item, Category category) {                List items = category.getItems();                items.remove(item);        }  public   synchronized   void  decreaseInventory(Item item,  int  amount)  throws  InventoryUnderflowException {                inventorySize = inventorySize - amount;                sales = sales + amount;  int  nq = item.getQuantity() - amount;  if  (nq < 0) {  throw   new  InventoryUnderflowException();                }                ((TackItem)item).setQuantity(nq);        }  public   void  destroy()  throws  IOException {                String cfn =  this.  name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }        }  public   synchronized  Category getCategory(String name) {                Category result = (Category)  this.  categories.get(name);  return  result;        }  public   synchronized   void  increaseInventory(Item item,  int  amount) {                inventorySize = inventorySize + amount;  if  (amount < 0) {  throw   new  IllegalArgumentException();                }  int  nq = item.getQuantity() + amount;                ((TackItem) item).setQuantity(nq);        }  public   synchronized  List getItems() {                accesses++;  return   this.  items;        }  public   synchronized   void  removeCategory(Category category)  throws  NoSuchCategoryException {                removeCategory(category.getName());        }  public   synchronized   void  removeCategory(String name)  throws  NoSuchCategoryException {                Category category = (Category)  this.  categories.remove(name);  if  (category ==  null  ) {  throw   new  NoSuchCategoryException();                }        }  public   synchronized   void  removeItem(Item item)  throws  NoSuchItemException {  boolean  exists =  this.  items.remove(item);  if  (exists ==  false  ) {  throw   new  NoSuchItemException();                }                inventorySize = inventorySize - item.getQuantity();                removeItemFromCategories(item);        }  public   synchronized   void  removeItem(  int  catalogno)  throws  NoSuchItemException {  boolean  removed =  false;  Iterator i =  this.  items.iterator();  while  (i.hasNext()) {                       Item item = (Item) i.next();  if  (item.getCatalogNo() == catalogno) {  this.  items.remove(item);                             inventorySize = inventorySize - item.getQuantity();                             removeItemFromCategories(item);                             removed =  true;   break;  }                }  if  (removed ==  false  ) {  throw   new  NoSuchItemException();                }        }  private   void  removeItemFromCategories(Item item) {                Iterator i =  this.  categories.keySet().iterator();  while  (i.hasNext()) {                     String category = (String) i.next();                     declassify(item, (Category)  this.  categories.get(category));                }        }        // TackCatalogMBean Methods  public   boolean  isAutoCheckpointing() {  return   this.  autoCheckpoint;        }  public   void  setAutoCheckpoint(  boolean  autoCheckpoint) {  this.  autoCheckpoint = autoCheckpoint;        }  public   void  doCheckpoint() {  if  (  this.  autoCheckpoint ==  true  ) {                       checkpoint();                }        }  public   void  checkpoint() {        }  public   int  getNumberOfItems() {  return  items.size();        }  public   int  getInventorySize() {  return  inventorySize;        }  public   int  getTotalSales() {  return  sales;        }  public   int  getAccesses() {  return  accesses;        }        ModelMBean createCatalogMBean() {          ModelMBean catalogMBean =  null;   try  {                     catalogMBean =  new  RequiredModelMBean();                     catalogMBean.setModelMBeanInfo(createCatalogMBeanInfo());                     catalogMBean.setManagedResource(  this,  "ObjectReference");               }  catch  (Exception e) {                      System.out.println("Creating Tack Catalog MBean failed: " +                          e.getMessage());               }  return  catalogMBean;        }        ModelMBeanInfo createCatalogMBeanInfo() {                ModelMBeanInfo info =  null;  /* attributes: boolean AutoCheckpointing r/w,        int numberOfItems r/o,        int Accesses r/o,        int Sales r/o        int InventorySize r/o */        /* operations: void saveCatalog,        Catalog load,        void doCheckpoint */        /* notifications: none */                String catDescription = "Tack Catalog Dynamic MBean";                Descriptor catDescriptor =  new  DescriptorSupport(  new  String[] {                                      ("name=TackCatalog"),                                      "descriptorType=mbean",                                      ("displayName=TackCatalogManager"),                                      "type=jnjmxch9.TackCatalogMM",                                      "log=T",                                      "logFile=jmxmain.log",                                      "currencyTimeLimit=10" });                ModelMBeanAttributeInfo[] catAttributes =  new  ModelMBeanAttributeInfo[5];                Descriptor catDescAuto =  new  DescriptorSupport(  new  String[] {                                      "name=AutoCheckpointing",                                      "descriptorType=attribute",                                      "displayName=AutoCheckpointing",                                      "getMethod=getAutoCheckpointing",                                      "currencyTimeLimit=10" });                catAttributes[0] =  new  ModelMBeanAttributeInfo("AutoCheckpointing",                          "java.lang.Boolean",                          "AutoCheckpointing: boolean value if checkpointing " +                               "of catalog is done automatically",  false,   true,   true,  catDescAuto);                Descriptor catDescItems =  new  DescriptorSupport(  new  String[] {                                      "name=NumberOfItems",                                      "descriptorType=attribute",                                      "displayName=NumberOfItems",                                      "getMethod=getNumberOfItems",                                      "currencyTimeLimit=10" });                catAttributes[1] =  new  ModelMBeanAttributeInfo("NumberOfItems",                               "java.lang.Integer",                               "NumberOfItems: number of items in the catalog",  true,   false,   false,  catDescItems);                Descriptor catDescAccess =  new  DescriptorSupport(  new  String[] {                                      "name=Accesses",                                      "descriptorType=attribute",                                      "displayName=Accesses",                                      "getMethod=getAccesses",                                      "currencyTimeLimit=10" });                catAttributes[2] =  new  ModelMBeanAttributeInfo("Accesses",                              "java.lang.Integer",                              "Accesses: number of time items in the catalog " +                              "database have been listed",  true,   false,   false,  catDescAccess);                Descriptor catDescSales =  new  DescriptorSupport(  new  String[] {                                      "name=TotalSales",                                      "descriptorType=attribute",                                      "displayName=TotalSales",                                      "getMethod=getTotalSales",                                      "currencyTimeLimit=10" });                catAttributes[3] =  new  ModelMBeanAttributeInfo("TotalSales",                                       "java.lang.Integer",                                       "Sales: number of items sold",  true,   false,   false,  catDescSales);                Descriptor catDescInv =  new  DescriptorSupport(  new  String[] {                                      "name=InventorySize",                                      "descriptorType=attribute",                                      "displayName=InventorySize",                                      "getMethod=getInventorySize",                                      "currencyTimeLimit=10" });                catAttributes[4] =  new  ModelMBeanAttributeInfo("InventorySize",                                "java.lang.Integer",                                "InventorySize: Total number of all " +                                "items currently in inventory",  true,   true,   false,  catDescInv);                ModelMBeanConstructorInfo[] catConstructors =  new  ModelMBeanConstructorInfo[1];                Constructor[] constructors =  this.  getClass().getConstructors();                Descriptor tackConstDesc =  new  DescriptorSupport(  new  String[] {                                      "name=TackCatalogMM",                                      "descriptorType=operation",                                      "role=constructor" });                catConstructors[0] =  new  ModelMBeanConstructorInfo(                               "tackCatalog(dbName): Constructs a tackCatalog" +                                      " from an existing database",                               constructors[0],                               tackConstDesc);                ModelMBeanOperationInfo[] catOperations =  new  ModelMBeanOperationInfo[2];                MBeanParameterInfo[] params =  new  MBeanParameterInfo[0];                Descriptor catDescCP =  new  DescriptorSupport(  new  String[] {                               "name=doCheckpoint",                               "descriptorType=operation",                               "class=TackcatalogMM",                               "role=operation" });                catOperations[0] =  new  ModelMBeanOperationInfo("doCheckpoint",                               "doCheckpoint(): saves the catalog to a file",                               params ,                               "void",                               MBeanOperationInfo.ACTION,                               catDescCP);                params =  new  MBeanParameterInfo[] {                               (  new  MBeanParameterInfo("name",                               "java.lang.String",                               "Tack Catalog name"))};                Descriptor catDescLoad =  new  DescriptorSupport(  new  String[] {                       "name=load",                       "descriptorType=operation",                       "class=TackcatalogMM",                       "role=operation" });                catOperations[1] =  new  ModelMBeanOperationInfo("load",                        "load(dbName): loads the catalog from a file",                        params ,                        "jnjmxch9.Catalog",                        MBeanOperationInfo.ACTION,                        catDescLoad);                ModelMBeanNotificationInfo[] catNotifications =  new  ModelMBeanNotificationInfo[0];                String className =  this.  getClass().getName();                info =  new  ModelMBeanInfoSupport(className,                                                catDescription,                                                catAttributes,                                                catConstructors,                                                catOperations,                                                catNotifications );  try  {                       info.setMBeanDescriptor(catDescriptor);                }  catch  (Exception e) {                       System.out.println("CreateModelMBeanInfo failed with " +                       e.getMessage());                }  return  info;        } }     // class 

Here we have created a model MBean, but it is not registered with an MBeanServer at this point. In Section 9.2.3 we will look at ways to register this MBean with an MBeanServer. Notice that we have added the get and set methods as operations that the attributes now point to. This example does not take advantage of any of the model MBean's features like caching, logging, and persistence. But it does illustrate how to organize the MBean implementation. As we saw in Chapter 4, it is much easier to create instances of ModelMBeanInfo from XML files.

9.2.3 Publish-Only MBeans

Some applications have management information that they need to expose to a management system, but they are unable or unwilling to be interrupted to get current values of attributes or invoke operations. This situation may arise for several reasons ”for example, if the application is real-time, highly distributed, performance critical, can only initiate communications and cannot receive asynchronous communications, or runs on a small device that can only send data or receive solicited data. Examples of applications that fall into the last category are browsers and many applications on pervasive devices like PDAs (personal digital assistants). Figure 9.4 shows how an application can set attributes and invoke operations on its MBean, without being affected by the changes in the MBean.

Figure 9.4. A Publish-Only Application

graphics/09fig04.gif

MBeans for these types of applications can be created and registered by another program outside the managed resource. Then the MBeans have a lifecycle that is not tied to the managed resource. The application can find and reuse the MBean while it is executing. The application would be responsible for updating the MBean whenever a configuration or metric attribute was updated. The getAttribute() methods would always return the current stored value and not go to the application to get a value. There would be no setAttribute() methods available to the management system. The only operations would be those associated with getAttribute() methods. The application would use the MBean to send notifications.

Model MBeans are well suited for this pattern of usage. They already support caching of attribute values in the MBean.

Here is an example of a publish-only application that uses a model MBean. For every attribute, the application updates the value in the model MBean. The model MBean returns the cached value and does not ever invoke any methods on the application. No get or set methods are defined for the attributes because they are not invoked:

  package  jnjmx.ch9;  import  java.io.File;  import  java.io.FileInputStream;  import  java.io.FileNotFoundException;  import  java.io.FileOutputStream;  import  java.io.IOException;  import  java.io.ObjectInputStream;  import  java.io.ObjectOutputStream;  import  java.io.Serializable;  import  java.util.ArrayList;  import  java.util.HashMap;  import  java.util.Iterator;  import  java.util.List;  import  java.util.Map;  import  java.lang.reflect.*;  import  javax.management.*;  import  javax.management.modelmbean.*; // Publish-Only Model MBean Example  public   class  TackCatalogPO  implements  Catalog, Serializable {  private   static   final  String FILEEXT = ".catalog";  private   final  String name;  private   boolean  autoCheckpoint =  false;   private  Map categories;  private  List items;  private   int  nextCatalogNo = 1;  private   int  accesses=0, sales=0, inventorySize=0;        ModelMBean catMBean =  null;  ObjectName dynTackMBean =  null;  MBeanServer mbs;  public  TackCatalogPO(String inName) {  this.  name = inName;  this.  categories =  new  HashMap();  this.  items =  new  ArrayList();  try  {                      dynTackMBean =  new  ObjectName("Chapter9:id=TackCatalogPO");  this.  catMBean =  this.  createCatalogMBean();                ArrayList mbservers = MBeanServerFactory.findMBeanServer(  null  );  if  (mbservers.size() == 1)                       mbs =  (MBeanServer)mbservers.get(0);  else  System.out.println("Creation of Management Bean " +                           "failed: too many MBeanServers");                mbs.registerMBean(catMBean, dynTackMBean);                }  catch  (Exception e) {                       System.out.println("Creation of Management Bean failed");                }        }  public   static  TackCatalogPO create(String inName)  throws  IOException {                String cfn = inName + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }                TackCatalogPO catalog =  new  TackCatalogPO(inName); //             Should write to a file here //             ObjectOutputStream db = //                      new ObjectOutputStream(new FileOutputStream(cf)); //             db.writeObject(catalog); //             db.flush(); //             db.close();  return  catalog;        }  public  Catalog load(String name)  throws  IOException, ClassNotFoundException {                String cfn = name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists() ==  false  ) {  throw   new  FileNotFoundException(cfn + " doesn't exist");                }                ObjectInputStream db =  new  ObjectInputStream(  new  FileInputStream(cf));                TackCatalogPO catalog = (TackCatalogPO) db.readObject();                db.close();  return  catalog;        }  public   synchronized  Map getCategories() {  return   this.  categories;        }  public   synchronized   void  classify(Item item, Category category) {                List items = ((TackCategory) category).getItems();  if  (items.contains(item) ==  false  ) {                       items.add(item);                }        }  public   synchronized  Category createCategory(String name)  throws  CategoryExistsException {                TackCategory result =  new  TackCategory(  this,  name);  if  (  this.  categories.get(name) !=  null  ) {  throw   new  CategoryExistsException();                }  this.  categories.put(name, result);  return  result;        }  public   synchronized  Item createItem(String name,  int  price,  int  quantity)  throws  ItemExistsException {                TackItem result =  new  TackItem(  this,  nextCatalogNo++, name,                    price, quantity);  if  (  this.  items.contains(result)) {  throw   new  ItemExistsException();                }  this.  items.add(result);                inventorySize = inventorySize + quantity;                /* Update cached value in ModelMBean model MBean */  try  {                       ModelMBeanInfo localInfo =                           (ModelMBeanInfo)catMBean.getMBeanInfo();                       Descriptor newNum =                           localInfo.getDescriptor("InventorySize","attribute");                       newNum.setField("value",  new  Integer(inventorySize));                       localInfo.setDescriptor(newNum, "attribute");                       Descriptor newNumI =                           localInfo.getDescriptor("NumberOfItems","attribute");                       newNumI.setField("value",  new  Integer(items.size()));                       localInfo.setDescriptor(newNumI, "attribute");                       catMBean.setModelMBeanInfo(localInfo);                }  catch  (Exception e) {                       System.out.println("Update of Management data failed");                }  return  result;        }  public   synchronized   void  declassify(Item item, Category category) {                List items = category.getItems();                items.remove(item);        }  public   synchronized   void  decreaseInventory(Item item,  int  amount)  throws  InventoryUnderflowException {                inventorySize = inventorySize - amount;                sales = sales + amount;  try  {                       ModelMBeanInfo localInfo =                           (ModelMBeanInfo)catMBean.getMBeanInfo();                       Descriptor newNum =                           localInfo.getDescriptor("InventorySize","attribute");                       newNum.setField("value",  new  Integer(inventorySize));                       localInfo.setDescriptor(newNum, "attribute");                       Descriptor newNumS =                           localInfo.getDescriptor("TotalSales","attribute");                       newNumS.setField("value",  new  Integer(sales));                       localInfo.setDescriptor(newNumS, "attribute");                       catMBean.setModelMBeanInfo(localInfo);                }  catch  (Exception e) {                       System.out.println("Update of Management data failed");                }  int  nq = item.getQuantity() - amount;  if  (nq < 0) {  throw   new  InventoryUnderflowException();                }                ((TackItem)item).setQuantity(nq);        }  public   void  destroy()  throws  IOException {                String cfn =  this.  name + FILEEXT;                File cf =  new  File(cfn);  if  (cf.exists()) {                       cf.delete();                }        }  public   synchronized  Category getCategory(String name) {                Category result = (Category)  this.  categories.get(name);  return  result;        }  public   synchronized   void  increaseInventory(Item item,  int  amount) {                inventorySize = inventorySize + amount;  if  (amount < 0) {  throw   new  IllegalArgumentException();                }  try  {                       ModelMBeanInfo localInfo =                           (ModelMBeanInfo)catMBean.getMBeanInfo();                       Descriptor newNum =                           localInfo.getDescriptor("InventorySize","attribute");                       newNum.setField("value",  new  Integer(inventorySize));                       localInfo.setDescriptor(newNum, "attribute");                       catMBean.setModelMBeanInfo(localInfo);                }  catch  (Exception e) {                       System.out.println("Update of Management data failed");                }  int  nq = item.getQuantity() + amount;                ((TackItem) item).setQuantity(nq);        }  public   synchronized  List getItems() {                accesses++;  try  {                       ModelMBeanInfo localInfo =                           (ModelMBeanInfo)catMBean.getMBeanInfo();                       Descriptor newNum =                           localInfo.getDescriptor("Accesses","attribute");                       newNum.setField("value",  new  Integer(accesses));                       localInfo.setDescriptor(newNum, "attribute");                       Descriptor newNumI =                           localInfo.getDescriptor("NumberOfItems","attribute");                       newNumI.setField("value",  new  Integer(  this.  items.size()));                       localInfo.setDescriptor(newNumI, "attribute");                       catMBean.setModelMBeanInfo(localInfo);                }  catch  (Exception e) {                       System.out.println("Update of Management data failed");                }  return   this.  items;        }  public   synchronized   void  removeCategory(Category category)  throws  NoSuchCategoryException {                removeCategory(category.getName());        }  public   synchronized   void  removeCategory(String name)  throws  NoSuchCategoryException {                Category category = (Category)  this.  categories.remove(name);  if  (category ==  null  ) {  throw   new  NoSuchCategoryException();                }        }  public   synchronized   void  removeItem(Item item)  throws  NoSuchItemException {  boolean  exists =  this.  items.remove(item);  if  (exists ==  false  ) {  throw   new  NoSuchItemException();                }                inventorySize = inventorySize - item.getQuantity();  try  {                       ModelMBeanInfo localInfo =                          (ModelMBeanInfo)catMBean.getMBeanInfo();                       Descriptor newNum =                           localInfo.getDescriptor("InventorySize","attribute");                       newNum.setField("value",  new  Integer(inventorySize));                       localInfo.setDescriptor(newNum, "attribute");                       Descriptor newNumI =                           localInfo.getDescriptor("NumberOfItems","attribute");                       newNumI.setField("value",  new  Integer(items.size()));                       localInfo.setDescriptor(newNumI, "attribute");                       catMBean.setModelMBeanInfo(localInfo);                }  catch  (Exception e) {                       System.out.println("Update of Management data failed");                }                removeItemFromCategories(item);        }  public   synchronized   void  removeItem(  int  catalogno)  throws  NoSuchItemException {  boolean  removed =  false;  Iterator i =  this.  items.iterator();  while  (i.hasNext()) {                       Item item = (Item) i.next();  if  (item.getCatalogNo() == catalogno) {  this.  items.remove(item);                             inventorySize = inventorySize - item.getQuantity();  try  {                                  ModelMBeanInfo localInfo =                                    (ModelMBeanInfo)catMBean.getMBeanInfo();                                  Descriptor newNum = localInfo.getDescriptor(                                    "InventorySize","attribute");                                  newNum.setField("value",  new  Integer(inventorySize));                                  localInfo.setDescriptor(newNum, "attribute");                                  Descriptor newNumI = localInfo.getDescriptor(                                    "NumberOfItems","attribute");                                  newNumI.setField("value",  new  Integer(items.size()));                                  localInfo.setDescriptor(                                    newNumI, "attribute");                                  catMBean.setModelMBeanInfo(localInfo);                               }  catch  (Exception e) {                                  System.out.println(                                    "Update of Management data failed");                               }                               removeItemFromCategories(item);                               removed =  true;   break;  }                }  if  (removed ==  false  ) {  throw   new  NoSuchItemException();                }        }  private   void  removeItemFromCategories(Item item) {                Iterator i =  this.  categories.keySet().iterator();  while  (i.hasNext()) {                       String category = (String) i.next();                       declassify(item, (Category)  this.  categories.get(category));                }        }        // TackCatalogMBean Methods  public   boolean  isAutoCheckpointing() {  try  {                       ModelMBeanInfo localInfo =                           (ModelMBeanInfo)catMBean.getMBeanInfo();                       Descriptor newNum = localInfo.getDescriptor(                           "AutoCheckpointing","attribute");                       newNum.setField("value",  new  Boolean(  this.  autoCheckpoint));                       localInfo.setDescriptor(newNum, "attribute");                       catMBean.setModelMBeanInfo(localInfo);                }  catch  (Exception e) {                       System.out.println("Update of Management data failed");                }  return   this.  autoCheckpoint;        }  public   void  setAutoCheckpoint(  boolean  autoCheckpoint) {  this.  autoCheckpoint = autoCheckpoint;  try  {                       ModelMBeanInfo localInfo =                           (ModelMBeanInfo)catMBean.getMBeanInfo();                       Descriptor newNum = localInfo.getDescriptor(                           "AutoCheckpointing","attribute");                       newNum.setField("value",  new  Boolean(  this.  autoCheckpoint));                       localInfo.setDescriptor(newNum, "attribute");                       catMBean.setModelMBeanInfo(localInfo);                }  catch  (Exception e) {                       System.out.println("Update of Management data failed");                }        }  public   void  doCheckpoint() {  if  (  this.  autoCheckpoint ==  true  ) {                       checkpoint();                }        }  public   void  checkpoint() {                // This method should save the catalog to a file        }        ModelMBean createCatalogMBean() {               ModelMBean catalogMBean =  null;   try  {                     catalogMBean =  new  RequiredModelMBean();                     catalogMBean.setModelMBeanInfo(createCatalogMBeanInfo());                     catalogMBean.setManagedResource(  this,  "ObjectReference");               }  catch  (Exception e) {                     System.out.println("Creating Tack Catalog MBean failed: " +                         e.getMessage());               }  return  catalogMBean;        }        ModelMBeanInfo createCatalogMBeanInfo() {                ModelMBeanInfo info =  null;  /* attributes: boolean AutoCheckpointing r/w,        int numberOfItems r/o,        int Accesses r/o,        int Sales r/o        int InventorySize r/o */        /* operations: none */        /* notifications: none */                String catDescription = "Tack Catalog Dynamic MBean";                Descriptor catDescriptor =  new  DescriptorSupport(  new  String[] {                                      ("name=TackCatalog"),                                      "descriptorType=mbean",                                      ("displayName=TackCatalogManager"),                                      "type=jnjmxch9.TackCatalogPO",                                      "currencyTimeLimit=-1" });                                      // return cache values                ModelMBeanAttributeInfo[] catAttributes =  new  ModelMBeanAttributeInfo[5];                Descriptor catDescAuto =  new  DescriptorSupport(  new  String[] {                                      "name=AutoCheckpointing",                                      "descriptorType=attribute",                                      "displayName=AutoCheckpointing",                                      "currencyTimeLimit=-1" });                                      // return cache values                catDescAuto.setField("value",  new  Boolean(  this.  autoCheckpoint));                // initial value                // not writable                catAttributes[0] =  new  ModelMBeanAttributeInfo("AutoCheckpointing",                               "java.lang.Boolean",                               "AutoCheckpointing: boolean value if " +                               "checkpointing of catalog is done automatically",  false,   false,   true,  catDescAuto);                Descriptor catDescItems =  new  DescriptorSupport(  new  String[] {                                      "name=NumberOfItems",                                      "descriptorType=attribute",                                      "displayName=NumberOfItems",                                      "currencyTimeLimit=-1" });                                      // return cache values                catDescItems.setField("value",  new  Integer(0));                                      // initial value                catAttributes[1] =  new  ModelMBeanAttributeInfo("NumberOfItems",                       "java.lang.Integer",                       "NumberOfItems: number of items in the catalog",  true,   false,   false,  catDescItems);                Descriptor catDescAccess =  new  DescriptorSupport(  new  String[] {                                      "name=Accesses",                                      "descriptorType=attribute",                                      "displayName=Accesses",                                      "currencyTimeLimit=-1" });                                      // return cache values                catDescAccess.setField("value",  new  Integer(0));                // initial value                catAttributes[2] =  new  ModelMBeanAttributeInfo("Accesses",                       "java.lang.Integer",                       "Accesses: number of time items in the catalog "  +                       "database have been listed",  true,   false,   false,  catDescAccess);                Descriptor catDescSales =  new  DescriptorSupport(  new  String[] {                                      "name=TotalSales",                                      "descriptorType=attribute",                                      "displayName=TotalSales",                                      "currencyTimeLimit=-1" });                                      // return cache values                catDescSales.setField("value",  new  Integer(0));                // initial value                catAttributes[3] =  new  ModelMBeanAttributeInfo("TotalSales",                       "java.lang.Integer",                       "Sales: number of items sold",  true,   false,   false,  catDescSales);                Descriptor catDescInv =  new  DescriptorSupport(  new  String[] {                                      "name=InventorySize",                                      "descriptorType=attribute",                                      "displayName=InventorySize",                                      "currencyTimeLimit=-1" });                                      // always cache                catDescInv.setField("value",  new  Integer(0));                // initial value                catAttributes[4] =  new  ModelMBeanAttributeInfo("InventorySize",                       "java.lang.Integer",                       "InventorySize: Total number of all items currently " +                       "in inventory",  true,   false,   false,  catDescInv);                ModelMBeanConstructorInfo[] catConstructors =  new  ModelMBeanConstructorInfo[1];                Constructor[] constructors =  this.  getClass().getConstructors();                Descriptor tackConstDesc =  new  DescriptorSupport(  new  String[] {                                      "name=TackCatalogPO",                                      "descriptorType=operation",                                      "role=constructor" });                catConstructors[0] =  new  ModelMBeanConstructorInfo(                        "tackCatalog(dbName): Constructs a tackCatalog" +                        "from existing database",                        constructors[0],                        tackConstDesc);                ModelMBeanOperationInfo[] catOperations =  new  ModelMBeanOperationInfo[0];                ModelMBeanNotificationInfo[] catNotifications =  new  ModelMBeanNotificationInfo[0];                String className =  this.  getClass().getName();                info =  new  ModelMBeanInfoSupport(className,                                      catDescription,                                      catAttributes,                                      catConstructors,                                      catOperations,                                      catNotifications );  try  {                       info.setMBeanDescriptor(catDescriptor);                }  catch  (Exception e) {                       System.out.println(                          "CreateModelMBeanInfo failed with " + e.getMessage());                }  return  info;        } }     // class 

In this case we are creating a model MBean that exposes attributes with no get methods, no setters, and no operations. The values for the attributes are cached in the descriptor of the attribute by the application. The model MBean is registered with the MBeanServer. Whenever one of these attributes is updated, the model MBean's cached value in the descriptor is updated as well.

9.2.4 Facades for Distributed Applications

Complex applications may have management operations scattered across many different classes. For distributed applications, the instances of the managed resources may be scattered across several JVMs or even remote JVMs. Operations may need to reach some or all of these diverse classes or distributed targets. In such cases, an MBean can be a very convenient management facade for your application. The MBean can have references (see Figure 9.5) to all of the managed objects ”local or remote ”or it can discover them at runtime. The MBean can then invoke the operations on the right objects on the right systems.

Figure 9.5. A Facade MBean

graphics/09fig05.gif

Model MBeans were designed specifically to perform this function, as the examples in Chapter 4 illustrate.



Java and JMX. Building Manageable Systems
Javaв„ў and JMX: Building Manageable Systems
ISBN: 0672324083
EAN: 2147483647
Year: 2000
Pages: 115

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net