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 MBeansIn 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):
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:
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 ApplicationsSometimes 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:
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 MBeansSome 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
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 ApplicationsComplex 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
Model MBeans were designed specifically to perform this function, as the examples in Chapter 4 illustrate. |