Enterprise Java Technologies Tech Tips Tips, Techniques, and Sample Code Welcome to the Enterprise Java Technologies Tech Tips for February 22, 2005. Here you'll get tips on using enterprise Java technologies and APIs, such as those in Java 2 Platform, Enterprise Edition (J2EE). This issue covers: * Understanding JMX Technology * Introducing the Sun Java Streaming XML Parser These tips were developed using the Java 2, Enterprise Edition, v 1.4 SDK. You can download the SDK at http://java.sun.com/j2ee/1.4/download.html. This issue of the Tech Tips is written by Robert Eckstein, a Java technology developer, editor, and author. Robert is owner of Nexes Consulting, a Java consulting firm based in Austin, Texas. You can view this issue of the Tech Tips on the Web at http://java.sun.com/developer/EJTechTips/2005/tt0222.html. See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms. You can download the sample archives for these tips at http://java.sun.com/developer/EJTechTips/download/ttfeb2005jmx.jar and http://java.sun.com/developer/EJTechTips/download/ttfeb2005sjsxp.jar. Any use of this code and/or information below is subject to the license terms at http://developers.sun.com/dispatcher.jsp?uid=6910008. For more Java technology content, visit these sites: java.sun.com - The latest Java platform releases, tutorials, and newsletters. java.net - A web forum for collaborating and building solutions together. java.com - The marketplace for Java technology, applications and services. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UNDERSTANDING JMX TECHNOLOGY Java Management Extensions (JMX) technology offers programmers the ability to add monitoring and management to their Java applications. In effect, these APIs allow you to locally or remotely manage anything Java-enabled, from web servers to network devices to web phones. JMX technology is defined by two closely related specifications developed through the Java Community Process (JCP): Java Specification Request (JSR) 3: Java Management Extensions (JMX) Specification (http://www.jcp.org/en/jsr/detail?id=3) and JSR 160: Java Management Extensions (JMX) Remote API 1.0 (http://www.jcp.org/en/jsr/detail?id=160). This tech tip introduces you to the JMX architecture, and shows you how to create a simple Managed Bean. With JMX technology, an application, device, or service on one machine (called a resource), can be controlled remotely through the use of one or more custom JavaBean objects known as Managed Beans (MBeans). These MBeans are then registered in a core-managed object server (an MBean server). The MBean server acts as a management agent to any remote managers that want to access the resource. The JMX Environment The JMX specification defines an architecture in three distinct tiers. The first two levels shown are the instrumentation and agent tiers, which are defined by JSR 3: o Instrumentation Tier. Resources, such as applications, devices, or services, are instrumented using Java objects called Managed Beans (MBeans). MBeans expose their management interfaces, composed of attributes and operations, through a JMX agent for remote management and monitoring. o Agent Tier. The main component of a JMX agent is the MBean server. This is a core managed object server in which MBeans are registered. A JMX agent also includes a set of services for handling MBeans. JMX agents directly control resources and make them available for management. The remote management level, the third tier, is partly defined by JSR 160: o Remote Management Tier. This tier defines protocol adaptors and connectors that make a JMX agent accessible from remote management applications outside the agent's Java Virtual Machine (JVM)*. (Note that JSR 160 defines connectors only.) Connectors are used when the remote client is JMX-aware, and sees the same JMX API as a local client would. Adaptors are used when the remote client is using a generic management protocol such as Simple Network Management Protocol (SNMP) or Common Information Model and Web Based Enterprise Management (CIM/WBEM). There are generally three types of developers who use JMX, although one person might exercise all three roles: o A developer that writes MBeans to manage resources. Here, JMX technology defines the interfaces exposed for management. The developer is responsible for the "glue" between the MBean and the resource itself. o A developer that creates and deploys the agent. This person typically performs a number of tasks, including: - Creating an MBean server, or using the one supplied by the platform. - Registering the MBeans that represent the resources using MBean naming conventions. - Configuring the connectors and protocol adaptors supplied by the platform (RMI and SNMP), or adding custom connectors or adaptors if the resources are to be accessed remotely. o A developer that writes the remote manager. This person chooses the connector or protocol adaptors to interact with the JMX agent, and builds views of the resources managed remotely through the exposed MBeans. There are four types of MBeans, as defined by the JMX specification: o Standard MBeans: Standard MBeans are management interfaces that are described by their method names. The methods are then exposed through introspection of the interface. Standard MBeans are the most common type of MBean. Most developers do not need to create any other MBean types. o Dynamic MBeans: A dynamic MBean implements its management interface programmatically with the javax.management. DynamicMBean interface, instead of through introspection of method names. To do this, it relies on informational classes that represent the attributes and operations exposed for management. Dynamic MBeans are often used when the management interface of an MBean is not known at compile time -- for example, if it is determined by parsing an XML file. o Model MBeans: A model MBean is a generic, configurable MBean that applications can use to instrument any resource dynamically. Essentially, it is a dynamic MBean that has been implemented so that its management interface and its actual resource can be set programmatically. This enables any manager connected to a Java dynamic management agent to instantiate and configure a model MBean dynamically. o Open MBean: Open MBeans are dynamic MBeans, with specific constraints on their data types, that allow management applications and their human administrators to understand and use new managed objects as they are discovered at runtime. Open MBeans provide a flexible means of instrumenting resources that need to be open to a wide range of applications compliant with the Java Management Extensions (JMX) specification. The typical process used in creating a JMX-compliant management interface involves at least two steps. The first step is to create an MBean interface, as well as an agent to register that MBean with the MBean server. The next step is to manage the MBean using a remote management application. The JMX specification defines a standard set of connectors that allow you to access JMX agents from remote management applications. This is useful because JMX connectors using different protocols provide the same management interface. This enables a management application to manage resources transparently, regardless of the communication protocol used. JMX agents can also be used by systems or applications that are not compliant with the JMX specification, but which support JMX agents. Creating a Standard MBean Interface Let's create an MBean for a hypothetical thermometer device. You can follow along by looking at the source code in the sample archive that accompanies this tip (ttfeb2005jmx.jar). The first thing you must do is "instrument" the resource by creating an MBean interface. In other words, you must create an MBean to implement the access to the instrumentation of resources. Here is an example of a simple MBean: public interface ThermometerMBean { // Attributes public double getTemperature(); public double getMaximumTemperature(); public double getMinimumTemperature(); // Operations public void resetMaxAndMin(); } The interface of an MBean typically consists of named and typed attributes that can be read, written (or both), and named and typed operations that can be invoked. JMX technology also defines a generic notification model based on the Java event model. JMX agents and MBeans can use this notification model to send critical information to interested parties such as management applications or other MBeans. However, this example does not use notifications. The next step is to create an implementation of the MBean interface. This is fairly straightforward. Here, we simply create a class called Thermometer that calls a hypothetical static singleton accessor. The accessor obtains a reference to the object that holds the temperature data. For standard MBeans, the name of the implementing class must be the same as the MBean interface, except that the "MBean" at the end is dropped (for example, ThermometerMBean becomes Thermometer). public class Thermometer implements ThermometerMBean { // Attributes public double getTemperature() { return getMyStaticDeviceInterface().getTemperature(); } public double getMaximumTemperature() { return getMyStaticDeviceInterface().getMax(); } public double getMinimumTemperature() { return getMyStaticDeviceInterface().getMin(); } // Operations public void resetMaxAndMin() { getMyStaticDeviceInterface().resetMaxAndMin(); } // ... } Creating a JMX Agent After a resource has been instrumented by MBeans, the resource can then be managed through a JMX agent. MBeans do not require any knowledge of the JMX agent that they work with. This allows developers of applications, systems, and networks to make their products manageable in a standard way without having to understand or invest in complex management systems. This way, existing resources can be made manageable with minimum effort. Agents get help from services that are located on the server. Agent services are objects that can perform management operations on the MBeans registered in the MBean server. By including management intelligence in the agent, JMX technology allows for more powerful management applications. The core component of a JMX agent is the MBean server. This is a managed object server in which MBeans are registered. A JMX agent also includes a set of services to manage MBeans, and at least one communications connector to allow access by a management application. Here is the source code for a simple agent: import java.lang.management.*; import javax.management.*; public class ThermometerAgent { private MBeanServer server = null; public ThermometerAgent() { server = ManagementFactory.getPlatformMBeanServer(); Thermometer tBean = new Thermometer(); ObjectName tBeanName = null; try { tBeanName = new ObjectName( "ThermometerAgent:type=Thermometer"); server.registerMBean(tBean, tBeanName); } catch(Exception e) { e.printStackTrace(); } } public static void main(String argv[]) { ThermometerAgent agent = new ThermometerAgent(); System.out.println( "Agent is ready... Press Enter to close"); try { System.in.read(); } catch (Exception e) { e.printStackTrace(); } } } Running the Example You will need J2SE 1.5 to run the example. 1. Download the sample archive (ttfeb2005jmx.jar) for the this tech tip. 2. Change to the directory where you downloaded the sample archive. Uncompress the JAR file for the sample archive as follows: jar xvf ttfeb2005jmx.jar This will expose the source code for the example. 3. Compile the classes using the standard javac compiler. 4. Run the agent class using the following command: java -Dcom.sun.management.jmxremote ThermometerAgent 5. In a new shell or command prompt, start the jconsole tool (this is located in the J2SE bin directory). 6. Connect to the JMX agent. Click on the MBeans tab, then select the Thermometer MBean of the ThermometerAgent entry in the tree to the left. If it's not already chosen, click the Attributes tab. You should then see the thermometer attributes for maximum temperature, minimum temperature, and current temperature. 7. Click the Operations tab. You should see the resetMaxAndMin() button. This tech tip has only begun to scratch the surface of JMX technology. For more information, see the article "Getting Started with Java Management Extensions (JMX): Developing Management and Monitoring Solutions" (http://java.sun.com/developer/technicalArticles/J2SE/jmx.html). The article creates a similar example as shown in this tip, but goes into more detail on using connectors. Also see the JMX technology page (http://java.sun.com/products/JavaManagement/). And be sure to follow JSR 255 (http://www.jcp.org/en/jsr/detail?id=255) to watch the development of the JMX Specification, version 2.0. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - INTRODUCING THE SUN JAVA STREAMING XML PARSER Most Java developers that work with XML are familiar with the Simple API for XML (SAX) and the Document Object Model (DOM) libraries. SAX is an event-based API, which means that a programmer typically registers a number of listeners with the parser, and when a specific XML grammar construct is reached (for example, an element or an attribute), the listener method is called. DOM, on the other hand, has a tree-based architecture, that scans in the entire document and builds an object-tree for each grammar construct it encounters. A programmer can then access and modify the object tree after the scanning is complete. Both of these approaches have their drawbacks: event-based APIs that make use of listeners are generally harder to work with. That's because they're driven by the parser. Tree-based APIs can consume an inordinate amount of memory in comparison to the size of the document being scanned. However, now there is a third API available for Java developers to scan XML: the Streaming API for XML parser, or StAX. What is the SJSXP? The Sun Java Streaming XML Parser is a high-speed implementation of StAX. BEA Systems, working in conjunction with Sun Microsystems, Inc., as well as XML-guru James Clark, Stefan Haustein, and Aleksandr Slominski (XmlPull developers), and others in the Java Community Process developed StAX as an implementation of JSR 173 (http://www.jcp.org/en/jsr/detail?id=173). StAX is a parser independent Java API based on a set of common interfaces. The SJSXP is included with version 1.5 of the Java Web Services Developer Pack (http://java.sun.com/webservices/downloads/webservicespack.html). The first thing that you're likely to notice about SJSXP is that it is based on a streaming API, which does not need to read an entire document before a developer can access any of the nodes. It also does not adhere to the principle of starting the parser and allowing the parser to "push" data to the event listener methods. Instead, SJSXP implements a "pull" method, where the parser maintains a pointer of sorts to the currently-scanned location in the document--this is often called a cursor. You simply ask the parser for the node that the cursor currently points to. Using SJSXP to Parse XML Documents Reading in XML documents with the SJSXP is fairly easy. Most of the work is done through an object that implements the javax.xml.stream.XMLStreamReader interface. This interface represents a cursor that's moved across an XML document from beginning to end. A few things to keep in mind: the cursor always points to a single item, such as an element start-tag, a processing instruction, or a DTD declaration. Also, the cursor always moves forward (not backward), and you cannot perform any "look aheads" to see what's upcoming in the document. You can obtain an XMLStreamReader to read in XML from a file with the following snippet of code: URL url = Class.forName("MyClassName").getResource( "sample.xml"); InputStream in = url.openStream(); XMLInputFactory factory = XMLInputFactory.newInstance(); XMLStreamReader parser = factory.createXMLStreamReader(in); You can then iterate through the XML file with the following code: while(parser.hasNext()) { eventType = parser.next(); switch (eventType) { case START_ELEMENT: // Do something break; case END_ELEMENT: // Do something break; // And so on ... } } The hasNext() method in XMLStreamReader checks to see if there is another item available in the XML file. If there is one, you can use the next() method to advance the cursor to the next item. The next() method returns an integer code that indicates the type of grammatical construct (the item) that it encountered. There are a number of get methods in XMLInputStreamReader that you can use to obtain the contents of the XML item that the cursor is pointing to. The first method is getEventType(): public int getEventType() The method returns an integer code that identifies the type of item the parser found under the cursor. It's the same code returned by the next() method. The items are identified by one of the following XMLInputStream constants: XMLStreamConstants.START_DOCUMENT XMLStreamConstants.END_DOCUMENT XMLStreamConstants.START_ELEMENT XMLStreamConstants.END_ELEMENT XMLStreamConstants.ATTRIBUTE XMLStreamConstants.CHARACTERS XMLStreamConstants.CDATA XMLStreamConstants.SPACE XMLStreamConstants.COMMENT XMLStreamConstants.DTD XMLStreamConstants.START_ENTITY XMLStreamConstants.END_ENTITY XMLStreamConstants.ENTITY_DECLARATION XMLStreamConstants.ENTITY_REFERENCE XMLStreamConstants.NAMESPACE XMLStreamConstants.NOTATION_DECLARATION XMLStreamConstants.PROCESSING_INSTRUCTION If the item has a name, you can use the getName() and getLocalName() methods to obtain the name. The latter yields the raw name, without any extra information (for example, the name of the element without a qualifying namespace). public Qname getName() public String getLocalName() If you want to identify the namespace of the current item, you can use the getNamespaceURI() method: public String getNamespaceURI() If there is any accompanying text, such as the text in a DTD declaration or text inside an element, you can use the following methods to obtain them (the latter is used solely for elements): public String getText() public String getElementText() If an element has attributes associated with it, you can use the getAttributeCount() method to obtain the number of attributes the current element has. You can then retrieve information on each of them using the getAttributeName() and getAttributeValue() methods: public int getAttributeCount() public Qname getAttributeName(int index) public String getAttributeValue(int index) If you know the local name of the attribute and the namespace URI of the element, you can also obtain the attribute value using the following method: public String getAttributeValue( String elementNamespaceURI, String localAttributeName) As you might have guessed, not all of the accessors methods are applicable in a specific state. For example, if you are currently processing a DTD, you cannot call getElementText(). If you do so, you will either receive a XMLStreamException stating that the parser has identified a conflicting event type, or the method itself will return null. You can turn on a number of parser properties by using the setProperty() method of the XMLInputFactory class. For example, the following specifies that entity references encountered by the parser will be replaced: factory.setProperty( XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE); To prevent the parser from supporting external entities, use the following setting: factory.setProperty( XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); To make the parser namespace aware, use the following setting: factory.setProperty( XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.TRUE); Note that the current version of SJSXP will accept the following command, but the parser is currently non-validating. factory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.TRUE); If any of these XMLInputFactory properties are enabled, you can use the setXMLReporter() method to handle any errors faced by the parser. The easiest way to determine exactly what type of error the parser encountered is to use the following anonymous inner class in conjunction with the setXMLReporter() method: factory.setXMLReporter(new XMLReporter() { public void report(String message, String errorType, Object relatedInformation, Location location) { System.err.println("Error in " + location.getLocationURI()); System.err.println("at line " + location.getLineNumber() + ", column " + location.getColumnNumber()); System.err.println(message); } }); Using SJSXP to Write XML Documents Writing XML output is easy with SJSXP. In this case, you can use the XMLStreamWriter interface instead of the XMLStreamReader interface. The XMLStreamWriter interface provides direct methods to write elements, attributes, comments, text, and all the other parts of an XML document. The following example shows how to obtain this interface and use it to write an XML document: XMLOutputFactory xof = XMLOutputFactory.newInstance(); XMLStreamWriter xtw = xof.createXMLStreamWriter(new FileWriter("myFile")); xtw.writeComment( "all elements here are in the HTML namespace"); xtw.writeStartDocument("utf-8","1.0"); xtw.setPrefix("html", "http://www.w3.org/TR/REC-html40"); xtw.writeStartElement( "http://www.w3.org/TR/REC-html40","html"); xtw.writeNamespace( "html", "http://www.w3.org/TR/REC-html40"); xtw.writeStartElement( "http://www.w3.org/TR/REC-html40","head"); xtw.writeStartElement( "http://www.w3.org/TR/REC-html40","title"); xtw.writeCharacters("Java Information"); xtw.writeEndElement(); xtw.writeEndElement(); xtw.writeStartElement( "http://www.w3.org/TR/REC-html40","body"); xtw.writeStartElement("http://www.w3.org/TR/REC-html40","p"); xtw.writeCharacters("Java homepage is "); xtw.writeStartElement("http://www.w3.org/TR/REC-html40","a"); xtw.writeAttribute("href","http://java.sun.com"); xtw.writeCharacters("here"); xtw.writeEndElement(); xtw.writeEndElement(); xtw.writeEndElement(); xtw.writeEndElement(); xtw.writeEndDocument(); xtw.flush(); xtw.close(); When you finish writing out each of the elements, you need to flush and close the writer. The preceding code will output the following XML (formatted here with line breaks for easier reading): Java Information Java information is here Filtering XML Documents You can create a filter for an incoming XML document if you don't want to scan through each item type. To do so, create a class that implements the javax.xml.stream.StreamFilter interface. This interface consists of only one method, accept(), that accepts an XMLStreamReader object and returns a primitive boolean. A typical implementation of StreamFilter looks like the following: public class MyStreamFilter implements StreamFilter { public boolean accept(XMLStreamReader reader) { if(!reader.isStartElement() && !reader.isEndElement()) return false; else return true; } } You then create a filtered reader by calling the createFilteredReader() method of the XMLInputFactory, and pass in both the original XML stream reader and the StreamFilter implementation. This is shown below: factory.createFilteredReader( factory.createXMLStreamReader(in), new MyStreamFilter()); For more information on the SJSXP, see the Sun Java Streaming XML Parser release notes (http://java.sun.com/webservices/docs/1.5/sjsxp/ReleaseNotes.html). Running the Sample Code for the Sun Java Streaming XML Parser 1. Download the sample archive (ttfeb2005sjsxp.jar) for this tech tip. 2. Download and install Java WSDP 1.5 from the Java Web Services Developer Pack Downloads page (http://java.sun.com/webservices/downloads/webservicespack.html). 3.Change to the directory where you downloaded the sample archive. Uncompress the JAR file for the sample archive as follows: jar xvf ttfeb2005sjsxp.jar 4. Set your classpath to include the ttfeb2005sjsxp.jar and jsr173_api.jar files, which are located in the sjsxp/lib directory of the Java WSDP 1.5 installation. 5. Compile and run the SJSXPInput executable. In response, you should see an entry similar to the following for each XML item: Event Type (Code=11): DTD Without a Name With Text: ----------------------------- 6. Compile and run the SJSXPOutput executable. The output will be sent to file named XMLOutputFile and consist of the elements shown in the output example above. . . . . . . . . . . . . . . . . . . . . . . . Please read our Terms of Use and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://developers.sun.com/dispatcher.jsp?uid=6910008 PRIVACY STATEMENT: Sun respects your online time and privacy (http://sun.com/privacy). You have received this based on your e-mail preferences. If you would prefer not to receive this information, please follow the steps at the bottom of this message to unsubscribe. * FEEDBACK Comments? Send your feedback on the Enterprise Java Technologies Tech Tips to: http://developers.sun.com/contact/feedback.jsp?category=sdn * SUBSCRIBE/UNSUBSCRIBE Subscribe to other Java developer Tech Tips: - Core Java Technologies Tech Tips. Get tips on using core Java technologies and APIs, such as those in the Java 2 Platform, Standard Edition (J2SE). - Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME). To subscribe to these and other JDC publications: - Go to the Sun Developer Network - Subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to and click "Submit". - To unsubscribe, go to the Subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click "Submit". - To use our one-click unsubscribe facility, see the link at the end of this email: - ARCHIVES You'll find the Enterprise Java Technologies Tech Tips archives at: http://java.sun.com/developer/EJTechTips/index.html - COPYRIGHT Copyright 2005 Sun Microsystems, Inc. All rights reserved. 901 San Antonio Road, Palo Alto, California 94303 USA. This document is protected by copyright. For more information, see: http://java.sun.com/developer/copyright.html Enterprise Java Technologies Tech Tips February 22, 2005 Trademark Information: http://www.sun.com/suntrademarks/ Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. * As used on the web site, the terms "Java Virtual Machine" and "JVM" mean a virtual machine for the Java platform.