Monday, June 30, 2008

The Value of Named Parameters in JPA QL

In my article Basic Java Persistence API Best Practices, I wrote that because JPA is the result of good ideas from many previous technologies (Hibernate, JDBC, JDO, etc.), it follows that many of the best practices associated with these inspiring technologies can and should be applied to JPA. I used named and positional parameters as an example of this.

The interesting thing about JDBC's PreparedStatement is the fact that, out of the box, the JDBC standard PreparedStatement does not support named parameters. Rather, the PreparedStatement supports placeholders in the SQL syntax that are "populated" via set methods that provide the index of the ? being replaced (counting from left to right).

In order to make code more readable and easier to maintain, many people have tried to workaround the PreparedStatement's positional parameter binding approach. For example, I have seen cases where developers have made the numeric instances Java constants with names that are more readable than the numeral itself. More elegant solutions for using a named parameter type approach have been created to more easily enable "named" parameters. For example, Adam Crume introduces a class called NamedParameterStatement in the JavaWorld article Named Parameters for PreparedStatement. Perhaps even better known is the Spring Framework's support for the NamedParameterJdbcStatement (part of Spring's extensive JDBC support). Finally, Oracle has provided an Oracle-specific JDBC extension that allows named parameters to be specified and used with the OraclePreparedStatement when using an Oracle JDBC driver.

The rising popularity of being able to used named parameters in addition to ordinal parameters is also showcased by Oracle's support for named parameters in JDBC calls to PL/SQL and in IBM's Informix Dynamic Server (IDS).

The JPA contributors recognized the usefulness and desirability of named parameters and JPA Query Language has supported them since JPA's inception. JPA Query Language supports input parameters that are specified with a colon and the parameter name. Then, as demonstrated in the article Standardizing Java Persistence with the EJB3 Java Persistence API, this input parameter can be set by calling the Query.setParameter(---) method.

The JPA 1.0 specification covers named parameters in section 3.6.3. This section indicates how to use named parameters and points out that named parameters are only portable across JPA providers for JPA Query Language statements (not for native queries).

In the case of JPA, hindsight is 20/20. JPA was able to leverage named parameter binding in JPA Query Language statements from the beginning. This allows JPA developers to take advantage of a feature that has been proven to be beneficial many times over and has led to custom implementations when named parameter support is not built into the language or framework.

Friday, June 27, 2008

OpenLaszlo and SpketIDE

SpketIDE is a useful IDE for developing OpenLaszlo applications. SpketIDE provides many useful OpenLaszlo editing functions beyond the simple color syntax support available with NetBeans 6.1 (as demonstrated in Flex and OpenLaszlo in NetBeans 6.1 Beta) or JEdit (as demonstrated in Using JEdit with OpenLaszlo and Flex) or other XML-cognizant editors.

The main SpketIDE Laszlo Editor page demonstrates many of the useful features Spket provides for OpenLaszlo development. Of course, there is color syntax highlighting support, but there is also support for more advanced features such as different types of code completion and occurrence marking. I like having the Laszlo documentation available in the editor as an added convenience because I otherwise find myself spending a lot of time flipping back and forth between my editor and the LZX Reference Manual.

According to the Spket 1.6.12 Downloads page, there is no charge for SpketIDE for non-commercial use. Currently (late June 2008), the license cost for a single license for commercial use is less than $30 USD. The SpketIDE FAQ also provides information on how to use SpketIDE with OpenLaszlo specifically.

I used SpketIDE to write the code for my example of writing an RSS Reader with OpenLaszlo and a screen snapshot of that code in SpketIDE is shown next (click on the image to see a larger version).



This snapshot demonstrates the Project Explorer, the Outline, Code Snippets (with Components opened up), the Lz Documentation window, and, of course, the main code editing window with color coded syntax.

SpketIDE is a useful editor for OpenLaszlo developers that is simple but powerful, easy to use, and has the potential to increase OpenLaszlo developer productivity.

Remote JMX Exceptions

Perhaps the most difficult aspect of getting a freshly written JSR-160 (Remote JMX) compliant application working is ensuring a proper JMXServiceURL. In this blog entry, I cover some of the most common JMXServiceURL-related exceptions one may run into when developing the client and server portions of a management and monitoring system using Remote JMX.

The JMX 1.4 Specification combines the original JMX specification with the Remote JMX specification in a single document (the JMX Remote API Specification is part III of this document). This third part covers the required RMI connector as well as the optional JMXMP connector. In this blog entry, I'll be focusing on using the RMI connector with Java SE 6 HotSpot platform MBean server. Instead of using JConsole as a client, I'll use a simple custom client to illustrate some of the exceptions one may encounter when writing one's own client.

I am using essentially the same code in this blog for examples that I previously used in the previous blog entry Remote JMX: With and Without Spring and Proxies. I will not reproduce the code here, but it is sufficient to know that the JMX Connector server code generally establishes its JMXServiceURL as service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi. Note that the protocol is RMI, the host is localhost, the port is 1099, and the URL string is "jmxrmi". Because the JMX client needs to use the same JMXServiceURL as provided by the server, I appreciate it when third-party JMX servers provide this to me. For example, both GlassFish and the JMX Web Services Connector Reference Implementation sample provide their JMXServiceURL to the console when they are executed.

When the JMXServiceURL is known, it is relatively straightforward to write a client. The most significant lines of code for doing this are extracted from the code in the previously referenced blog and displayed next:


final String objectNameStr = "dustin:type=status,name=remoteJMX";
final String jmxRmiStr =
"service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi";
try
{
final ObjectName objectName = new ObjectName(objectNameStr);
final JMXServiceURL jmxUrl = new JMXServiceURL(jmxRmiStr);
final JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxUrl);
final MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();


As the code listing above demonstrates, it is fairly easy (and mostly boilerplate) to connect clients to a JMX Connector Server with a well-known JMXServiceURL. However, it is not uncommon for new JMX developers to struggle to get the JMXServiceURL appropriately set. In the remainder of this blog entry, I now look at some of the most common JMXServiceURL-related exceptions one may run into.


ERROR: IOException trying to connect to JMX Connector Server: Failed to retrieve RMIServer stub: javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
java.net.ConnectException: Connection refused: connect]

A possible cause of this exception includes no JMX Connector Server running on the specified host and port combination.

Also, ensure that an RMI registry has been started at the specified port.



ERROR: IOException trying to connect to JMX Connector Server: Failed to retrieve RMIServer stub: javax.naming.NameNotFoundException: jmx-rmi

This NameNotFoundException was a result of inadvertently mistyping the client's String URL portion (jmx-rmi specified by the client rather than jmxrmi without the hyphen). This is different from the above error because a JMX Connector Server is at the specified host and port, but with a different String portion. Because multiple JMX Connector Servers can use the same host/port combination as long as they have unique String portions of their URLs, it is not surprising that no assumptions can be made about what String was intended.



Exception in thread "main" java.lang.ClassCastException: com.sun.jndi.rmi.registry.RegistryContext cannot be cast to javax.management.remote.rmi.RMIServer

The previous exception demonstrated the result of attempting to access a JMX Connector Server from a client with the correct host/port comibination, but with an incorrect String URL portion. This ClassCastException is the result of not specifying any String URL portion at all in the JMXServiceURL. In other words, the jmxrmi portion was left off the end of the client's JMXServiceURL.




java.io.IOException: Cannot bind to URL [rmi://localhost:1099/jmxrmi]: javax.naming.NameAlreadyBoundException: jmxrmi [Root exception is java.rmi.AlreadyBoundException: jmxrmi]

Until now, all of the previously covered exceptions resulted from a JMX Client using a mistyped or otherwise incorrect JMXServiceURL that did not match that used by the JMX Connector Server. This exception, however, is a JMX Connector Server exception that results from two different applications trying to use the same JMXServiceURL on the same host/port with the same URL String.



ERROR: Problem with JMXServiceURL based on rmi:///jndi/rmi://localhost:1099/jmxrmi: Service URL must start with service:jmx:

This error message is particularly descriptive and occurs when the JMXServiceURL does not begin with the specification-required service:jmx: portion. See Section 13.8 ("Connector Server Addresses") for additional details on all JMXServiceURLs beginning with this portion.



ERROR: Problem with JMXServiceURL based on service:jmx::///jndi/rmi://localhost:1099/jmxrmi: Missing or invalid protocol name: ""

As the error message indicates, the protocol is missing from the JMXServiceURL, which should be service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi, but the portion in red was omitted.



ERROR: Problem with JMXServiceURL based on service:jmx:nothing:///jndi/rmi://localhost:1099/jmxrmi: Unsupported protocol: nothing

Whereas the previous error message indicated a missing protocol designation, this error indicates an erroneous protocol specification. In this particular case, the incorrect JMXServiceURL used was service:jmx:nothing:///jndi/rmi://localhost:1099/jmxrmi where the "nothing" in red should have been "rmi".



ERROR: IOException trying to connect to JMX Connector Server: Failed to retrieve RMIServer stub: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial

This error message is displayed for the case where the second protocol listing in the JMXServiceURL is either omitted or has an incorrect value. In other words, if the highlighted portion of the correct JMXServiceURL (service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi)was omitted entirely or had something unexpected like "nothing," this error message would occur.


Other Remote JMX Exceptions/Errors Considerations

There are, of course, several other types of exceptions one can run into when remotely accessing a JMX Connector Server. These exceptions, which have not been covered here, are those that are thrown when the connection is successfully made, but the sought-after MBean cannot be found or some other problem occurs. The JMX Best Practices document outlines recommendations related to MBeans throwing standard exceptions and also recommends having MBeans intended for remote management/monitoring implement interfaces with operations that use the throws IOException clause (more granular for client than available as UndeclaredThrowableException).


JMX Exceptions in General

The focus of this blog entry has been on common exceptions and errors encountered when running a custom JMX Connector Client with a custom JMX Connector Server. Note that JMX spells out its own exception hierarchy related to the JMX Agent. Section 6.4 (JMX Exceptions) of the JMX 1.4 Specification is devoted to these JMX exceptions. The section talks about the JMException, which is the main class and parent of all descendant exception classes (exception runtime exceptions) thrown by a JMX agent implementation. The JMX runtime exceptions are represented by JMRuntimeException and its descendant exceptions.

Tuesday, June 24, 2008

An OpenLaszlo RSS Reader Implementation

In this blog entry, I demonstrate generation of a simple RSS Reader using OpenLaszlo. The world certainly doesn't need another RSS reader, so the purpose of this blog entry is to demonstrate some of the easy-to-use features of OpenLaszlo.

The RSS reader shown in this blog entry will focus on blogs hosted on Blogger (blogspot.com). There are two URLs one can use to access RSS feeds from blogs with the blogspot.com domain. These are listed next with my own blog URL (marxsoftware.blogspot.com) used as the example.

1. http://marxsoftware.blogspot.com/feeds/posts/default?alt=rss (see Blogger Feed URLs for additional details)

2. http://marxsoftware.blogspot.com/rss.xml

This example will use the first type of URL to access blogspot.com-based RSS feeds and display the blog entries titles, URLs, and summaries if available. This simple reader allows the user to enter the main part of the blogspot.com blog name and the remainder of the appropriate RSS URL is constructed automatically. By default, this blog's RSS feed is initially displayed, but it can be changed to any other blog on the blogspot.com domain.

The next three screen snapshots show the OpenLaszlo-based RSS Reader displaying RSS feed information from the blogs http://marxsoftware.blogspot.com/, http://jacksonsoftware.blogspot.com/, and http://mmartinsoftware.blogspot.com/. Click on the screen snapshots shown here to see larger version.

OpenLaszlo-based RSS Reader Showing My Blog RSS Feed




OpenLaszlo-based RSS Reader Showing Bill's RSS Feed




OpenLaszlo-based RSS Reader Showing Michael's RSS Feed




With an idea of how the RSS Reader built on OpenLaszlo works, it is time to turn to the code. I'll first list the entire listing for this OpenLaszlo application and then will focus on some pieces of it.


<canvas title="Blog RSS Reader">
<!-- Dataset bound to blog's RSS feed. -->
<dataset name="blogRssFeed"
request="true"
src="http://marxsoftware.blogspot.com/feeds/posts/default?alt=rss" />

<!-- Alternative RSS Feed URL: http://marxsoftware.blogspot.com/rss.xml -->

<!-- Allow for BlogSpot/Blogger URL input. -->
<window name="urlEntryWindow"
title="Provide BlogSpot/Blogger URL"
width="${parent.width}" height="15%">
<view name="blogUrlSelectorView" width="${parent.width}">
<simplelayout axis="x" spacing="5" />
<statictext name="entryLabel">Enter blog name:</statictext>
<inputtext id="urlInputText"
width="${parent.width*.35-parent.entryLabel.width}" />
<text id="urlDisplayText"
width="${parent.width*0.35-5}"
bgcolor="0xFFFF99">
<handler name="ontext" reference="urlInputText">
/*
this.setText( "http://" + urlInputText.getText()
+ ".blogspot.com/rss.xml");
*/
this.setText( "http://" + urlInputText.getText()
+ ".blogspot.com/feeds/posts/default?alt=rss");
</handler>
</text>
<button>
<font color="0xFF00FF">Process this RSS Feed</font>
<handler name="onclick">
blogRssFeed.setSrc(urlDisplayText.getText());
blogRssFeed.doRequest();
</handler>
</button>
</view>
</window>

<!-- Presentation of blog RSS feeds. -->
<window title="Blog RSS Reader"
y="${parent.urlEntryWindow.height}"
width="100%" height="85%">
<view width="${parent.width}">
<view datapath="blogRssFeed:/rss/channel/item"
width="${parent.width}">
<simplelayout axis="y" spacing="5" />
<view name="feedTitleView" width="${parent.width}">
<simplelayout axis="x" spacing="10" />
<text name="rssFeedTitleLabel"
text="TITLE: "
fontsize="12"
fontstyle="bold"
resize="true"
width="${parent.width*0.1}" />
<text name="rssFeedTitle"
text="$path{'title/text()'}"
fontsize="12"
fontstyle="italic"
resize="true"
width="${parent.width*0.9}" />
</view>
<view name="feedLinkView" width="${parent.width}">
<simplelayout axis="x" spacing="10" />
<text name="rssFeedLinkLabel"
text="URL: "
fontsize="12"
fontstyle="bold"
resize="true"
width="${parent.width*0.1}" />
<richinputtext name="rssFeedLink"
text="$path{'link/text()'}"
fontsize="12"
resize="true"
width="${parent.width*0.9}"
selectable="true">
<handler name="ontext">
htmlLink = new textformat(); // LzTextFormat is deprecated
htmlLink.setAttribute("url", this.getText());
htmlLink.setAttribute("target", "blank");
this.setHTML(htmlLink);
this.setTextFormat(htmlLink);
</handler>
</richinputtext>
</view>
<view name="summaryView" width="${parent.width-10}">
<text name="summaryText"
text="$path{'summary/text()'}"
fontsize="10"
resize="true"
width="${parent.width}"
multiline="true"
x="${parent.x+5}" />
</view>
</view>
<simplelayout axis="y" spacing="25" />
</view>
<!-- Laszlo scrollbar is along y-axis by default. -->
<scrollbar />
</window>
</canvas>


This OpenLaszlo example provide several observations about OpenLaszlo development. First, this example shows how easy it is to have OpenLaszlo access server-side XML resources using the <dataset> tag:


<dataset name="blogRssFeed"
request="true"
src="http://marxsoftware.blogspot.com/feeds/posts/default?alt=rss" />


The dataset tag example shown above uses only three lines to associate a handle/name with the dataset called "blogRssFeed", instructs OpenLaszlo to initialize the dataset immediately, and provides the URL from which to access the data. I could have added the attribute type="http" to explicitly instruct OpenLaszlo that this data is obtained from an HTTP connection (rather than from a file), but using "http://" in the src attribute did essentially the same thing.

This sample blog RSS reader also provides examples of one of OpenLaszlo's most powerful features, the ability to use constraints. Constraints are easily identifiable by the ${---} syntax (similar to Ant properties syntax). In this example, I did not declare a constraint compiler directive, so it is implicitly assumed the compiler directive is always. Used this way, these constraints instruct OpenLaszlo to update attribute values immediately when the value to which they are constrained is updated.

A third interesting observation from this simplistic OpenLaszlo-based RSS Reader is the use of richinputtext with textformat:


<richinputtext name="rssFeedLink"
text="$path{'link/text()'}"
fontsize="12"
resize="true"
width="${parent.width*0.9}"
selectable="true">
<handler name="ontext">
htmlLink = new textformat(); // LzTextFormat is deprecated
htmlLink.setAttribute("url", this.getText());
htmlLink.setAttribute("target", "blank");
this.setHTML(htmlLink);
this.setTextFormat(htmlLink);
</handler>
</richinputtext>


In the snippet above, a handler is specified for the richinputtext element by nesting the handler element within the richinputtext element and providing the type of event it should handle (ontext) via the name attribute of handler.

The snippet above also demonstrates use of textformat (replaces the deprecated LzTextFormat and intended for script [not LZX XML markup] only). It also demonstrates using textformat in conjunction with the richinputtext element to create HTML text. In this case, it creates an HTML link to the designated RSS feed URL and does so in a new browser window.

Although this example provides several additional interesting things to highlight, I'll end this entry with one more thing of personal interest. Several pieces of the OpenLaszlo application take advantage of OpenLaszlo's XPath syntax support to query XML from the dataset populated via the RSS feed URL. The ability to use XPath syntax is advantageous because of its familiarity to those who have used XPath in other things (such as XQuery or XSLT), because of the standard nature of XPath, and because of the expressive but concise nature of XPath syntax.

In this blog entry, I've attempted to demonstrate by example that OpenLaszlo supports several standards including XML, XPath, HTTP, and ECMAScript. OpenLaszlo also supports an implementation of the de facto "standard" XMLHttpRequest for non-XML data handling. With OpenLaszlo's basis in and support of common web development standards, it is a relatively easy technology to learn and apply.




UPDATE (14 July 2008): There are a couple of updates to note related to this OpenLaszlo-based RSS Reader. They are listed next with hyperlinks to more details.


* OpenLaszlo 4.1 no longer supports textformat as used above, but it must be used as lz.textformat() instead -- see textformat NOT AWOL in OpenLaszlo 4.1 for more details.

* Tooltips can be added to the RSS Reader to make it more user-friendly as described in OpenLaszlo ToolTips.

Sunday, June 22, 2008

Why ActionScript is in the Tiobe Programming Community Index Top Twenty

The June 2008 edition of the Tiobe Programming Community Index highlights the fact (in its introductory text) that ActionScript has entered the index's top twenty. This places ActionScript higher on the index than such venerable languages as Fortran and Smalltalk and above trendy languages such as Groovy, Erlang, and Scala.

I have written about things I like about ActionScript 3.0 in other blog entries, but I am using this blog entry to articulate in one place some of the things I most like about ActionScript 3.0 in honor of this Top 20 June 2008 ranking.




UPDATE (15 August 2008): The announcement this week that the ECMAScript committee has decided to move on a smaller incremental version of ECMAScript (3.1) rather than the complete overhaul originally envisioned for ECMAScript 4 can be disheartening for many of us. However, if the browser vendors are able to fully and completely meet the reduced ECMAScript 3.1 standard, that is a positive. Also, it sounds like ActionScript will continue to be a forward-leaning web language despite this decision.





What I Like Most About ActionScript (at least today!)

1. Browser Differences Are Just a Terrible Memory

Although there are things I really don't like about JavaScript, by far my biggest problem with JavaScript is the need to write different code for different browsers. If ActionScript provided no other advantage than the fact that I could write it once for any browser, it would be worth using. Interestingly, this is essentially what OpenLaszlo provides -- a subset of JavaScript that allows the developer to write to the same API regardless of browser. ActionScript goes well beyond JavaScript as most of us know it and as described in the remaining advantages listed next.

2. Static Typing

At home or when working on small projects, there are many things I enjoy about a dynamically typed language such as Ruby. However, when working on large enterprise applications with large groups of developers, I have found static languages to be preferable. It is a matter of personal taste, but my preference is to have the static support (default for ActionScript 3.0 in Flex 2 and Flex 3). For those who prefer dynamic typing, Flex allows the static typing to be turned off.

3. Class-Based Object-Oriented Features

JavaScript's prototype-based object-orientation has always been difficult for me to apply effectively, primarily because I keep trying to force my thinking about JavaScript into my experience with C++, Java, and C#. I'm not alone in this as evidenced by the fact that so much JavaScript I encounter simply doesn't take advantage of the prototype features.

4. ActionScript is Standards-Oriented

Because the major web browsers do not collectively fully support the ECMAScript (ECMA-262) specification, this standard is less valuable than it otherwise might be. Having said that, the announcement that Microsoft Internet Explorer 8 will be standards compliant is heartening and gives us hope that web developers will one day be able to write significant ECMAScript-based applications that work across browsers. ActionScript is Adobe's implementation of what they predict ECMAScript Edition 4 to evolve to. ActionScript also provides support for ECMAScript for XML (E4X).

5. ActionScript and Mozilla Tamarin

In my opinion, it bodes well for ActionScript and developers learning ActionScript that Mozilla has accepted Adobe's contribution of ActionScript Virtual Machine to the Mozilla Tamarin project. Adobe has contributed many members of the Tamarin project and the current plans are to use the same ECMAScript Edition 4 implementation in Mozilla 2 (Firefox 4) and Adobe's ActionScript implementations.

6. ActionScript is Friendly to Java Developers

Experience with Java is usually of very little benefit when learning JavaScript and can even be a detriment. ActionScript, however, uses many features, syntax, and concepts that are very similar to Java. As discussed above, ActionScript supports static typing and class-based object-oriented features similar to Java. Another example is that ActionScript also provides a Javadoc-like tool called ASDoc.

7. ActionScript Complements Other Programming Languages

I like to be comfortable with multiple languages because different languages are best suited for different types of problems. ActionScript is one of those languages that a developer is likely to use in conjunction with one or more other languages. Especially within in Flex applications, ActionScript will often be used on the client side and a server-side language such as Java/Java EE, Ruby, Python, or Perl will be used with the Flex front-end. What this means is that ActionScript does not necessarily replace anyone's favorite language, but complements developers' favorite languages. Even the language that ActionScript might be mostly likely to replace (JavaScript) can be used with ActionScript. Because of this nature of ActionScript's common use case, ActionScript may sometimes avoid or be less affected by the defensiveness of developers who might think their favorite language is threatened by ActionScript's growing popularity.

Conclusion

With so many developers discovering the ease and power of using Flex, it is not surprising that ActionScript is gaining in popularity. In this blog entry, I've attempted to describe some reasons why I believe ActionScript is so popular for web development (and, with AIR, for desktop development). The rising popularity of Flex necessarily brings ActionScript along for the ride (much in the same way the Ruby on Rails framework brought Ruby newfound fame), but ActionScript has many positive features of its own (just as Ruby has many positive features of its own) to keep developers interested and happy.


SIDE NOTE:

By the way, I'm not the only one who has run into significant issues with JavaScript. Evidence of this is the title of the recently released book JavaScript: The Good Parts. Not only does the title imply that there are significant "bad parts," but the book's description goes beyond implication and explicitly spells out that JavaScript has some more than its fair share of issues.

Saturday, June 21, 2008

The JMX MXBean

I'll demonstrate the tremendous utility of the JMX MXBean in this blog entry. While doing so, I'll also demonstrate the usefulness of a J2SE 5 enum for reporting a set of discrete status values and will demonstrate the highly useful JMX (introduced with Java SE 6) class's newMBeanProxy and newMXBeanProxy methods.

The MXBean was introduced in J2SE 5, but was only available for predefined system MXBeans (platform MXBeans). The ability for JMX developers to write their own custom MXBeans was provided with Java SE 6. With MXBeans, the "standard" set of JMX MBeans is now Standard MBean, Dynamic MBean, Model MBean (used heavily in Spring JMX), Open MBean, and MXBean. In many ways, the MXBean is like a souped-up standard MBean. It follows similar conventions as the standard MBean, but provides much greater support for representing custom data types in remote JMX clients.

The MXBean is so similar in usage to the Standard MBean that it is not surprising that the thorough MXBean introduction MXBeans in Java SE 6: Bundling Values without Special JMX Client Configurations recommends using MXBeans in any Java SE 6 application where Standard MBeans may have been used before moving to Java SE 6.

The standardized MXBean should not be confused with the JBoss-specific XMBean. The JBoss XMBean is not an MXBean, but is actually a JBoss Model MBean approach similar to how the Spring Framework approaches Model MBeans.

For my MXBean example in this blog entry, I will be taking advantage of one of my favorite features added to Java since the ubiquitous JDK 1.4.2: the Java enum. I wrote about the advantages of the Java enum for representing finite sets of related values in the OTN article Basic Java Persistence API Best Practices and in a previous blog entry The Power and Flexibility of the Java Enum. Because JMX is often used to access an application's state or status and because such a state or status can often be defined in a set of finite choices, the enum can be a highly useful construct in JMX applications.

For my demonstration, I will build the same functionality using both a Standard MBean and an MXBean. The next two listings show the Standard MBean interface (StatusMBean) and the very similar looking MXBean (StatusMXBean) interface. Note how they are nearly identical. The MXBean interface is only one byte larger than the MBean interface and that is due to the extra "X" added to the interface's name. They are identical other than that extra X. Therefore, it is easy to port a Standard MBean to an MXBean in terms of the MBean itself.

StatusMBean.java (Standard MBean interface)

package server;

public interface StatusMBean
{
public StatusEnum getStatus();

public void setStatus(final StatusEnum status);

public enum StatusEnum{ SUCCESSFUL, FAILURE, UNSPECIFIED };
}


StatusMXBean.java (MXBean Interface)

package server;

public interface StatusMXBean
{
public StatusEnum getStatus();

public void setStatus(final StatusEnum status);

public enum StatusEnum{ SUCCESSFUL, FAILURE, UNSPECIFIED };
}


With the Standard MBean and MXBean interfaces defined, it is time to look at their implementations. These are shown in the next two listings.

Status.java (Standard MBean implementation)

package server;

public class Status implements StatusMBean
{
private StatusEnum statusEnum = StatusEnum.UNSPECIFIED;

public Status() {}

public StatusEnum getStatus()
{
return this.statusEnum;
}

public void setStatus(final StatusEnum status)
{
this.statusEnum = status;
}
}


Status2.java (MXBean implementation)

package server;

public class Status2 implements StatusMXBean
{
private StatusEnum statusEnum = StatusEnum.UNSPECIFIED;

public Status2() {}

public StatusEnum getStatus()
{
return this.statusEnum;
}

public void setStatus(final StatusEnum status)
{
this.statusEnum = status;
}
}


The rules (or required conventions) for MXBeans are less strict than those for Standard MBeans. For example, you may have noticed that the MXBean implementation class shown above is actually Status2. I had to use simply Status.java for the implementation of the Standard MBean interface StatusMBean, but did not need to do so for StatusMXBean. If I was to try to do the same thing with the Standard MBean, say by adding a "3" on the end of the implementation class so that it is Status3.java, I would see a runtime error similar to that shown in the next screen snapshot when I tried to register that Standard MBean.



As the screen snapshot above indicates, not following the Standard MBean convention of naming the implementation identically to the interface sans the "MBean" suffix leads to a NotCompliantMBeanException with the message that the particular non-conforming implementation class "does not implement DynamicMBean, neither follows the Standard MBean conventions" and "is not a JMX compliant Standard MBean." The fact that we don't see this same error message for the Status2 implementation of StatusMXBean demonstrates that the MXBean naming conventions are looser than those for Standard MBeans.

Related to the last point, another potential advantage of the MXBean over the Standard MBean is that an MXBean implementation class can be in a different package than its defining MXBean interface. I'll not demonstrate it here, but Eamonn McManus explains in this JMX forum thread that MXBean does not have some of these restrictions that the Standard MBean has.

The looser naming convention and packaging structure of the MXBean can be nice advantages for certain situations, but I still have not demonstrated what is likely to be the biggest advantage most JMX developers enjoy with an MXBean. To see this advantage, it is now time to access the Standard MBean and MXBean shown above remotely.

The following class shows the registration of the Standard MBean and the MXBean with the MBeanServer. This class looks much more difficult than it really is. Most of the lines in the class are the handling of the checked exceptions thrown by the various calls and by the import statements for those exceptions. This class (SimpleMBeanServer) instantiates the Standard MBean and the MXBean and registers both with the MBean server.

SimpleMBeanServer.java

package server;

import java.lang.management.ManagementFactory;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MalformedObjectNameException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;

public class SimpleMBeanServer
{
public static void registerMBean(
final MBeanServer mbs,
final String mBeanObjectName,
final Class mBeanClass)
{
try
{
final ObjectName name = new ObjectName(mBeanObjectName);
final Object mBean = mBeanClass.newInstance();
mbs.registerMBean(mBean, name);
}
catch (InstantiationException badInstance) // Class.newInstance()
{
System.err.println( "Unable to instantiate provided class with class "
+ "name " + mBeanClass.getName() + ":\n"
+ badInstance.getMessage() );
}
catch (IllegalAccessException illegalAccess) // Class.newInstance()
{
System.err.println( "Illegal Access trying to instantiate "
+ mBeanClass.getName() + ":\n"
+ illegalAccess.getMessage() );
}
catch (MalformedObjectNameException badObjectName)
{
System.err.println( mBeanObjectName + " is a bad ObjectName:\n"
+ badObjectName.getMessage() );
}
catch (InstanceAlreadyExistsException duplicateMBeanInstance)
{
System.err.println( mBeanObjectName + " already existed as an MBean:\n"
+ duplicateMBeanInstance.getMessage() );
}
catch (MBeanRegistrationException mbeanRegistrationProblem)
{
System.err.println( "ERROR trying to register " + mBeanObjectName + ":\n"
+ mbeanRegistrationProblem.getMessage() );
}
catch (NotCompliantMBeanException badMBean)
{
System.err.println( "ERROR: " + mBeanObjectName + " is not compliant:\n"
+ badMBean.getMessage() );
}
}

public static void main(final String[] arguments)
{
final String mbeanObjectNameStr = "example:type=Status";
final String mxbeanObjectNameStr = "example:type=Status2";
//final String mbean3ObjectNameStr = "example:type=Status3";
final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
registerMBean(mbs, mbeanObjectNameStr, Status.class);
registerMBean(mbs, mxbeanObjectNameStr, Status2.class);
//registerMBean(mbs, mbean3ObjectNameStr, Status3.class);
System.console().printf("Press ENTER to exit.");
final String dummyValue = System.console().readLine();
}
}


When running the MBean server code shown above, I have used the three JMX-related system properties to specify that it is remotely accessible and does not require any authentication:

-Dcom.sun.management.jmxremote.port=1199
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

With the server code written and running so that it is exposed to remote clients, it is time to turn to the client. We'll start with the readily available JConsole and then use our own client. The next couple of screen snapshots demonstrate how the Standard MBean's status (StatusEnum) is handled differently than the MXBean's status (also a StatusEnum). The first screen snapshot shows the JConsole display of the Standard MBean status attribute and the second screen snapshot demonstrates the JConsole display for the same status via the MXBean.

Standard MBean Enum Attribute Not Available


MXBean Enum Attribute Displayed Correctly


The most important difference in the above two screen snapshots is that the MXBean's attribute's value is correctly displayed, but the Standard MBean's enum attribute value is "Unavailable." Also note that JConsole provides additional details regarding the MXBean attribute type in the Descriptor section. This demonstrates another advantage of the MXBean.

We'll now move on to a custom client and call it SimpleClient. As with the server code, the checked exception handling and associated import statements make the functionality appear at first glance to be more complicated than it really is.

SimpleClient.java

package client;

import server.StatusMBean;
import server.StatusMXBean;

import java.io.IOException;
import java.net.MalformedURLException;

import javax.management.JMX;
import javax.management.MalformedObjectNameException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;

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

/**
* Simple remote JMX client intended to show differences seen from client
* perspective between a standard MBean and an MXBean.
*/
public class SimpleClient
{
public static String getServerStatusViaMBean(
final MBeanServerConnection mbsc )
{
String statusToReturn = "";
final String mbeanObjectNameStr = "example:type=Status";
try
{
final ObjectName objectName = new ObjectName(mbeanObjectNameStr);
final StatusMBean statusBeanProxy =
JMX.newMBeanProxy(mbsc, objectName, StatusMBean.class);
statusToReturn = statusBeanProxy.getStatus().toString();
}
catch (MalformedObjectNameException badObjectName)
{
System.err.println( mbeanObjectNameStr + " is a bad object name.:\n"
+ badObjectName.getMessage() );
}

return statusToReturn;
}

public static String getServerStatusViaMXBean(
final MBeanServerConnection mbsc )
{
String statusToReturn = "";
final String mbeanObjectNameStr = "example:type=Status2";
try
{
final ObjectName objectName = new ObjectName(mbeanObjectNameStr);
final StatusMXBean statusBeanProxy =
JMX.newMXBeanProxy(mbsc, objectName, StatusMXBean.class);
statusToReturn = statusBeanProxy.getStatus().toString();
}
catch (MalformedObjectNameException badObjectName)
{
System.err.println( mbeanObjectNameStr + " is a bad object name.:\n"
+ badObjectName.getMessage() );
}

return statusToReturn;
}

public static void main(final String[] arguments)
{
final String jmxUrlString =
"service:jmx:rmi:///jndi/rmi://127.0.0.1:1199/jmxrmi";
try
{
final JMXServiceURL jmxUrl = new JMXServiceURL(jmxUrlString);
final JMXConnector connector = JMXConnectorFactory.connect(jmxUrl);
final MBeanServerConnection mbsc = connector.getMBeanServerConnection();
System.out.println( "MBean reports status: "
+ getServerStatusViaMBean(mbsc) );
System.out.println( "MXBean reports status: "
+ getServerStatusViaMXBean(mbsc) );
}
catch (MalformedURLException badJmxUrl)
{
System.err.println( "ERROR trying to build JMXServiceURL with "
+ jmxUrlString + ":\n" + badJmxUrl.getMessage() );
}
catch (IOException ioEx)
{
System.err.println( "ERROR trying to connect.\n" + ioEx.getMessage() );
}
}
}


One of the more interesting highlights of the custom client is the use of the methods JMX.newMBeanProxy and JMX.newMXBeanProxy. As these names imply, there are versions of these methods for creating proxies on the client side for both standard MBeans and MXBeans. Underneath the covers, these methods seem to turn around and call the MBeanServerInvocationHandler.newProxyInstance method that I used for client-side proxy support in a previous blog entry. While the MBeanServerInvocationHandler.newProxyInstance approach works just fine, the JMX.newMBeanProxy approach tends to be a little easier to use and is a nice Java SE 6 addition to JMX.

With some knowledge of the MBean interface available on the client side, both the Standard MBean and the MXBean work well. This is demonstrated in the next screen snapshot which shows the results of running the custom client and seeing the correct returned values for both the Standard MBean and the MXBean.



The JMX Best Practices document recommends using the Standard MBean interface where possible because of its increased usability (including easy Javadoc support and client-side proxies). While this JMX Best Practices document does reference MXBeans, it was written before they were available for custom MXBeans and it is probably safe to say that MXBeans could be substituted for Standard MBeans in any recommendation regarding use of Standard MBeans. In other words, because MXBeans provide the same usability advantages as Standard MBeans with the additional client-size data type flexibility, developers using Java SE 6 or later will likely want to consider the MXBean as their first option when writing new JMX MBeans.

Java SE 6 built on J2SE 5's JMX support and added several nice new features. Some of these that were highlighted in this blog entry include support for custom MXBeans and the introduction of JMX.newMBeanProxy. Other Java SE 6 advantageous additions include the much-improved JConsole and removal of the need to specify -Dcom.sun.management.jmxremote for local JMX access (thanks to the JMX Attach API). Mandy Chung's blog provides a comprehensive list of Java SE 6 JMX enhancements. MXBeans and other Java SE 6 enhancements to JMX make Java Management Extensions easier than ever to use.

Monday, June 16, 2008

Firefox 3 Download Day

Tomorrow (Tuesday, June 17, 2008) is the day in which there will be an attempt to set a Guinness World Record for the most software downloads in 24 hours (downloading of Firefox 3). While it can be fun to be part of such an event, I am looking forward to the many new features of Firefox 3. I'll be doing my part to contribute to Firefox 3 Download Day 2008 by downloading Firefox 3 on all of my computers during the 24-hour period. This short blog entry is my small contribution to spreading the word.

I am similarly looking forward to the release of Microsoft Internet Explorer 8, which is supposed to be significantly more standards compliant than previous versions of MSIE.

Tuesday, June 10, 2008

A Second Look at JMX Web Services Connector: winrm

In A First Look at JMX Web Services Connector (also available as a previous blog entry), I demonstrated using JConsole to access a simple sample server using the reference implementation of the JMX Web Services Connector (JSR-262). In this blog entry, I am going to scratch the surface of using Vista's winrm (a WS-Management implementation) to access an MBean server using the JMX Web Services Connector.

Although I primarily use a Vista-based laptop for my blogging and other "outside of my day job" work, I have not used winrm previously and so much of the challenge for me in working out examples of accessing the JMX Web Services Connector without a Java client was trying to understand how winrm works. In this blog entry, I'll use examples from the thorough Java Management Extensions (JMX) Interoperation With Non Java Technologies document (written by Jean-Francois Denise and Daniel Fuchs) and add some descriptive text to describe how select commands from that document work. Before moving on, I'll reiterate that the this JMX Interoperation with Non Java Technologies PDF includes significantly more background and examples than the two I'm focusing on here.

For my example, I'm running the simple sample server that is bundled with the JSR-262 reference implementation. There is an Ant build.xml file included in the jsr262-ri\samples\simple\server directory that can be run without options to start the server portion. The next screen snapshot shows this:



As I did for running the JSR-262 reference implementation simple sample server, I'm going to use the Command Shell for running the winrm client side portions. It is vital in Vista that you start this shell as an administrator. Running Command Shell as an administrator is demonstrated in the next screen snapshot and is done by right-clicking on a shortcut for the Command Shell and selecting "Run as administrator."



For the client side, it is necessary to configure winrm by using the winrm qc (or winrm quickconfig) command. This and three other commands that should be run to configure winrm properly for the example are spelled out in the "Windows Vista WinRM Set up" section of the JMX Interoperation with Non Java Technologies document.

While in the JMX Interoperation with Non Java Technologies document, refer also to the section "Extras - WinRM Command Line Tool Examples" (toward the end). This section contains many direct winrm (no VBScript required) commands that can be run against the simple server started earlier. This is an easier way to see winrm work as a WS-Management implementation with the JMX Web Services Connector than trying to use VBScript (though the document contains VBScript code snippets as well).

One of the highlighted "extra WinRM Command Line Tool Examples" is the command to obtain MBean Server domain names. It is as follows:


winrm get http://jsr262.dev.java.net/MBeanServerResource -fragment://:Domain -r:http://127.0.0.1:9998/jmxws -a:None


This can appear a little cryptic when not used to winrm syntax. A good starting point for learning to use winrm is its usage information. You can get usage/help information by entering winrm -? (or winrm help) at the command-line. This shows, for example, that the get command used above could also have been expressed as simply “g” and is used for getting or retrieving management information. Likewise, the invoke or “i” is used to execute an operation on the managed resource.

To get more information on the get command, one can enter winrm help get and the usage displayed shows that a RESOURCE_URI should be passed to the get command.

In the example above, there is a clause –fragment://:Domain. The command winrm help switches provides more details on this. The help for –fragment:VALUE tells us that this option is used to specify a portion of the instance XML file. In this case, the value is //:Domain which corresponds to any Domain element in the XML structure (the // is XPath syntax for recursive search and : is XPath syntax for namespace separator).

The winrm help remoting command tells us what the –r option (or –remote) option does. The –r:VALUE command specifies the remote endpoint. As used in the example above it is a URL consisting of four pieces: transport, host, protocol, and prefix.

The final option in the command above is –a:None. The –a (or –authentication) option is used to specify the authentication mechanism used and is None in this example, but could have been Basic, Digest, Negotiate, or Kerberos. More information on the authentication mechanism can be found using the command winrm help auth.

Another useful winrm command that can be used against the JMX Web Services implementation allows us to retrieve the ObjectNames of all registered MBeans:


winrm enumerate http://jsr262.dev.java.net/DynamicMBeanResource -returnType:EPR -r:http://127.0.0.1:9998/jmxws -a:None


Some of this is the same as the previous command analyzed here. The –r (remote) option takes the same URL as before and the –a:None indicates that we don’t want any authentication employed for this example.

A difference in this example from the last one is the use of enumerate instead of get. Using winrm help enumerate provides information on its usage and explains that this option allows application of a filter to the returned results. In this case, one of enumerate’s options (-returnType=VALUE) is set to EPR which means that only end point references (applicable Resource URI and selectors) are displayed. The other two objects are object form (hierarchical presentation of object values) or ObjectAndEPR (a combination of the other two).

I'll show the full output of the most concise of these return types (EPR only) here:


EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=Memory

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=GarbageCollector,name=Copy

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = DefaultDomain:type=Sample

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=MemoryPool,name=Code Cache

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = com.sun.xml.ws.util:type=RuntimeVersion

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = com.sun.xml.ws.transport.http:type=HttpDump

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=Runtime

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=ClassLoading

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=MemoryPool,name=Perm Gen [shared-rw]

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=Threading

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.util.logging:type=Logging

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=MemoryPool,name=Perm Gen [shared-ro]

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=Compilation

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=MemoryPool,name=Eden Space

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = com.sun.management:type=HotSpotDiagnostic

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=GarbageCollector,name=MarkSweepCompact

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=MemoryPool,name=Survivor Space

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=MemoryPool,name=Tenured Gen

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=MemoryPool,name=Perm Gen

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=OperatingSystem

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = JMImplementation:type=MBeanServerDelegate

EndpointReference
Address = http://127.0.0.1:9998/jmxws
ReferenceParameters
ResourceURI = http://jsr262.dev.java.net/DynamicMBeanResource
SelectorSet
Selector: ObjectName = java.lang:type=MemoryManager,name=CodeCacheManager


The object version and object+EPR versions are significantly longer than this and I won't show them here.

So what's the big deal about all of this? The big deal is that this demonstrates the use of a non-Java client (in this case Windows Vista winrm) to access Java Management Extensions on the server. With the JMX Web Services Connector, you no longer need a Java-based client to monitor and manage JMX-instrumented resources (including the JVM itself)!

Saturday, June 7, 2008

Remote JMX: With and Without Spring and Proxies

In this blog entry, I'll cover a simple set of remote JMX examples that demonstrate the use of remote JMX with focus on the client side. I'll show how use of the Spring Framework's JMX support and especially the use of proxies (with or without Spring) can make the client-side portion of remote JMX much easier.

For my example, I am only demonstrating one "server side" of the remote JMX example. I am using Spring to implement this server-side portion of the JMX RMI Connector because Spring makes it much easier. However, everything implemented here for the server side can be implemented without Spring. There is not as much difference in implementation details for the client side, so I will show examples both using and not using Spring on the client side.

The common JMX server-side code for my example is contained in three Java class files (a main executable class, a Spring-exposed bean class, and an interface that the Spring bean implements) along with a Spring XML configuration file. These four files constituting the server-side of the application are shown next.

spring-server.xml [Spring Configuration for Server]

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">

<bean id="jmxStatusService"
class="jmx.spring.rmi.example.StatusBean"
p:status="Initializing" />

<util:map id="exposedMBeans">
<entry key="dustin:type=status,name=remoteJMX" value-ref="jmxStatusService" />
</util:map>

<bean class="org.springframework.jmx.export.MBeanExporter"
p:beans-ref="exposedMBeans"
p:assembler-ref="assembler" />

<util:list id="manageableInterfaces">
<value>jmx.spring.rmi.example.StatusBeanIF</value>
</util:list>

<bean id="assembler"
class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler"
p:managedInterfaces-ref="manageableInterfaces" />

<bean id="registry"
class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"
p:port="1099" />

<bean id="serverConnector"
class="org.springframework.jmx.support.ConnectorServerFactoryBean"
depends-on="registry"
p:objectName="connector:name=rmi"
p:serviceUrl="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi" />

</beans>


StatusBeanIF.java (Server-side Spring-bean Interface)

package jmx.spring.rmi.example;

/**
* Interface to be used by Spring and by clients to access hosted MBean.
*
* @author Dustin
*/
public interface StatusBeanIF
{
public void setStatus(final String status);

public String getStatus();
}


StatusBean.java (Bean class exposed by Spring as MBean)

package jmx.spring.rmi.example;

/**
* Bean that will be served up by Spring as an MBean.
*
* @author Dustin
*/
public class StatusBean implements StatusBeanIF
{
private String status;

public StatusBean(){}

public void setStatus(final String status)
{
this.status = status;
}

public String getStatus()
{
return this.status;
}
}


ServerMain.java (Main Executable Class to Bootstrap Spring)

package jmx.spring.rmi.example;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* ServerMain Spring application executable.
*
* @author Dustin
*/
public class ServerMain
{
/**
* Run a simple long-running Spring-based application that can be managed via
* JMX. This class requires Java SE 6 or later because it uses the
* System.console().
*
* @param args The command line arguments
* @see java.lang.System
*/
public static void main(String[] args)
{
final ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("jmx\\spring\\rmi\\example\\spring-server.xml");
StatusBeanIF statusBean;
String status;
do
{
statusBean = (StatusBeanIF) context.getBean("jmxStatusService");
System.console().printf("Enter a status for this server: ");
status = System.console().readLine();
statusBean.setStatus(status);
}
while ( !status.toUpperCase().equals("EXIT") );
context.close();
}
}


As you can see in the code above, most of the work for exposing a simple bean as a JMX MBean is done in Spring's XML configuration (the first file listed). In that XML configuration file, the JMXServiceURL is highlighted in blue because this same URL will be needed for the client-side code, which is shown next.

The first type of client I'll demonstrate is a "straight JMX" client that does not use the Spring Framework. It does not matter that the server, as shown above, was ultimately implemented via the Spring Framework. The client can be run with or without Spring and I'll show both here.

ClientMain.java (Only Class Needed for JMX Client without Spring)

Because Spring is not involved, there is no XML file for configuration. To simplify things, I stuffed all of the client side functionality into a single class called ClientMain whose code listing is shown next.


package jmx.spring.rmi.example.client;

import java.io.IOException;
import java.net.MalformedURLException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import jmx.spring.rmi.example.StatusBeanIF;

/**
* This simple example demonstrates a non-Spring remote JMX client.
*
* @author Dustin
*/
public class ClientMain
{
/**
* Demonstrate accessing a JMX agent remotely using RMI and without using
* a client-side proxy.
*
* @param mbsc MBeanServerConnection used to connect to server.
* @param objectName ObjectName pointing to MBeanServer-hosted MBean.
*/
public static void demonstrateClientJmxWithoutProxy(
final MBeanServerConnection mbsc,
final ObjectName objectName)
{
final String operationName = "getStatus";
try
{
final String status = (String) mbsc.invoke(objectName,
operationName,
null, // no parameter
null );
System.out.println("Status is: " + status);
}
catch (InstanceNotFoundException noMBeanInstanceFound) // checked
{
System.err.println( "ERROR: Could not find MBean with name "
+ objectName.toString() + " in MBeanServer:\n"
+ noMBeanInstanceFound.getMessage() );
}
catch (MBeanException mbeanEx) // checked
{
System.err.println( "ERROR: Exception encountered on invoked MBean:\n"
+ mbeanEx.getMessage() );
}
catch (ReflectionException reflectionEx) // checked
{
System.err.println( "ERROR trying to reflectively invoke remote MBean:\n"
+ reflectionEx.getMessage() );
}
catch (IOException ioEx) // checked
{
System.err.println( "ERROR trying to communicate with remote MBeanServer:\n"
+ ioEx.getMessage() );
}
}

/**
* Demonstrate accessing remote JMX agent via RMI and via a client-side proxy.
* This method uses MBeanServerInvocationHandler.newProxyInstance, but Java SE 6
* adds the easier-to-use JMX.newProxyInstance approach for obtaining a
* a client-side proxy.
*
* @param mbsc MBeanServerConnection used to connect to server.
* @param objectName ObjectName pointing to MBeanServer-hosted MBean.
*/
public static void demonstrateClientJmxWithProxy(
final MBeanServerConnection mbsc,
final ObjectName objectName)
{
final StatusBeanIF status = (StatusBeanIF)
MBeanServerInvocationHandler.newProxyInstance(
mbsc,
objectName,
StatusBeanIF.class,
false);
System.out.println("PROXY Says: The Status is " + status.getStatus());
}

/**
* The functionality here is available directly through the provided
* MBeanServerConnection with or without a proxy or reflection. Because it
* is information only at the MBeanServer level (not at each individually
* hosted MBean's level), no ObjectName is required.
*
* @param mbsc MBeanServerConnection for connecting to remote JMX agent.
*/
public static void demonstrateCommonMBeanServerInfo(
final MBeanServerConnection mbsc)
{
try
{
System.out.println( "MBean Count: " + mbsc.getMBeanCount() );
System.out.println( "MBean Default Domain: " + mbsc.getDefaultDomain() );
}
catch (IOException ioEx)
{
System.err.println( "ERROR encountered trying to get MBeanCount and "
+ "Default Domain for provided MBeanServer:\n"
+ ioEx.getMessage() );
}
}

public static void main(String[] aArguments)
{
final String objectNameStr = "dustin:type=status,name=remoteJMX";
final String jmxRmiStr =
"service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi";
try
{
final ObjectName objectName = new ObjectName(objectNameStr);
final JMXServiceURL jmxUrl = new JMXServiceURL(jmxRmiStr);
final JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxUrl);
final MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();

demonstrateCommonMBeanServerInfo(mbsc);
demonstrateClientJmxWithoutProxy(mbsc, objectName);
demonstrateClientJmxWithProxy(mbsc, objectName);
}
catch (MalformedObjectNameException malformedObjectNameEx)
{
System.err.println( "ERROR: Problem with object name ["
+ objectNameStr + "] instantiation:\n"
+ malformedObjectNameEx.getMessage() );
}
catch (MalformedURLException badUrl)
{
System.err.println( "ERROR: Problem with JMXServiceURL based on "
+ jmxRmiStr + ": " + badUrl.getMessage() );
}
catch (IOException ioEx)
{
System.err.println( "ERROR: IOException trying to connect to JMX "
+ "Connector Server: " + ioEx.getMessage() );
}
}
}


There is no Spring used in the code above. However, the code above does show how with regular JMX client-side code, one can connect to a JMX agent remotely either using direct reflection (ClientMain.demonstrateClientJmxWithoutProxy(MBeanServerConnection,ObjectName) method) or using a proxy (ClientMain.demonstrateClientJmxWithProxy(MBeanServerConnection,ObjectName) method). The main(String[]) function of this class performs all of the functionality required to connect to the JMX Server without Spring's help. This includes instantiating the JMXServiceURL and acquiring the JMXConnector to ultimately acquire an MBeanServerConnection. The MBeanServerConnection is required to connect to the JMX agent remotely whether direct reflection or proxies are used. To obtain this MBeanServerConnection, three checked exceptions must be handled.

To see how much easier it is to use a proxy as compared to direct reflection, one need only compare the lines of code in the method ClientMain.demonstrateClientJmxWithProxy (proxy) to the lines of code in the method ClientMain.demonstrateClientJmxWithoutProxy (direct reflection). The direct reflection approach requires handling of four checked exceptions compared to none required for the proxy approach. Also, as the number of operations desired to be run against a particular remote MBean increases, the additional complexity of the reflection approach becomes greater compared to the proxy approach.

The above single class demonstrated how one can access a remote JMX agent from a client. I'll now move onto how to accomplish the same functionality, but will take advantage of the Spring Framework on the client side this time.

My JMX Client assisted by Spring example has three files. The three files are the main Java class that bootstraps the Spring container, the Spring bean that interacts with the MBeanServer with direct reflection and with proxies, and the XML configuration file for the client application's Spring context.

spring-client.xml (XML Configuration for Client-side Spring Container)
Note that this XML file is much simpler than that used for the JMX server side.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">

<bean id="mBeanServerClient"
class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean"
p:serviceUrl="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi" />

<bean id="jmxSpringClient"
class="jmx.spring.rmi.example.springClient.SpringJmxClientBean"
p:mbeanServerConnection-ref="mBeanServerClient" />

</beans>


SpringClientMain.java

package jmx.spring.rmi.example.springClient;

import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* Executable to run sample JMX remote client based on Spring framework.
*
* @author Dustin
*/
public class SpringClientMain
{
public static void main(final String[] arguments)
{
final ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext(
"jmx\\spring\\rmi\\example\\springClient\\spring-client.xml");
SpringJmxClientBean jmxClient =
(SpringJmxClientBean) context.getBean("jmxSpringClient");
final String objectNameStr = "dustin:type=status,name=remoteJMX";
try
{
final ObjectName objectName = new ObjectName(objectNameStr);
jmxClient.demonstrateCommonMBeanServerInfo();
jmxClient.demonstrateClientJmxWithoutProxy(objectName);
jmxClient.demonstrateClientJmxWithProxy(objectName);
}
catch (MalformedObjectNameException malformedObjectNameEx)
{
System.err.println( "ERROR: Problem with object name ["
+ objectNameStr + "] instantiation:\n"
+ malformedObjectNameEx.getMessage() );
}
context.close();
}
}


SpringJmxClientBean.java

package jmx.spring.rmi.example.springClient;

import java.io.IOException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import jmx.spring.rmi.example.StatusBeanIF;

/**
* Simple example of using Spring with JMX on the client side of a remote JMX
* connection.
*
* @author Dustin
*/
public class SpringJmxClientBean
{
private MBeanServerConnection mbsc;

public SpringJmxClientBean() {}

/**
* Allows Spring to inject MBeanServerConnection.
*
* @param mbsc MBeanServerConnection to be injected.
*/
public void setMbeanServerConnection(final MBeanServerConnection mbsc)
{
this.mbsc = mbsc;
}

/**
* Demonstrate accessing a JMX agent remotely using RMI and without using
* a client-side proxy.
*
* @param objectName ObjectName pointing to MBeanServer-hosted MBean.
*/
public void demonstrateClientJmxWithoutProxy(final ObjectName objectName)
{
final String operationName = "getStatus";
try
{
final String status = (String) mbsc.invoke(objectName,
operationName,
null, // no parameter
null );
System.out.println("Status (Spring) is: " + status);
}
catch (InstanceNotFoundException noMBeanInstanceFound) // checked
{
System.err.println( "ERROR: Could not find MBean with name "
+ objectName.toString() + " in MBeanServer:\n"
+ noMBeanInstanceFound.getMessage() );
}
catch (MBeanException mbeanEx) // checked
{
System.err.println( "ERROR: Exception encountered on invoked MBean:\n"
+ mbeanEx.getMessage() );
}
catch (ReflectionException reflectionEx) // checked
{
System.err.println( "ERROR trying to reflectively invoke remote MBean:\n"
+ reflectionEx.getMessage() );
}
catch (IOException ioEx) // checked
{
System.err.println( "ERROR trying to communicate with remote MBeanServer:\n"
+ ioEx.getMessage() );
}
}

/**
* Demonstrate accessing remote JMX agent via RMI and via a client-side proxy.
* This method uses MBeanServerInvocationHandler.newProxyInstance, but Java SE 6
* adds the easier-to-use JMX.newProxyInstance approach for obtaining a
* a client-side proxy.
*
* @param objectName ObjectName pointing to MBeanServer-hosted MBean.
*/
public void demonstrateClientJmxWithProxy(final ObjectName objectName)
{
final StatusBeanIF status = (StatusBeanIF)
MBeanServerInvocationHandler.newProxyInstance(
mbsc,
objectName,
StatusBeanIF.class,
false);
System.out.println("PROXY (Spring) Says: The Status is " + status.getStatus());
}

/**
* The functionality here is available directly through the provided
* MBeanServerConnection with or without a proxy or reflection. Because it
* is information only at the MBeanServer level (not at each individually
* hosted MBean's level), no ObjectName is required.
*/
public void demonstrateCommonMBeanServerInfo()
{
try
{
System.out.println( "MBean Count: " + mbsc.getMBeanCount() );
System.out.println( "MBean Default Domain: " + mbsc.getDefaultDomain() );
}
catch (IOException ioEx)
{
System.err.println( "ERROR encountered trying to get MBeanCount and "
+ "Default Domain for provided MBeanServer:\n"
+ ioEx.getMessage() );
}

}
}


Finally, note that the "common" features of the JMX MBeanServer (default domain, hosted MBean count, etc.) can be obtained directly from the MBeanServerConnection and therefore the decision whether to use proxy or direct reflection does not apply to those calls. Rather, one must decide between proxy or direct reflection when operating on remotely hosted MBeans themselves (not the host).

The above example of a JMX client with Spring's assistance demonstrates that one thing that Spring provides on the JMX client side is the ability to inject an MBeanServerConnection into the code via Spring's container and wiring. Once the MBeanServerConnection is obtained, everything else associated with accessing a remote JMX agent is essentially the same in the above example. Like the non-Spring client example, the Spring-assisted client accesses the server side both using reflection and using proxies. The proxy approach is essentially the same as for the non-Spring client shown earlier just as the reflection approach is also essentially the same for a Spring-assisted approach as for a non-Spring JMX client.

However, Spring can do even more for us on the client side using a proxy. Instead of using the MBeanServerInvocationHandler.newProxyInstance, we can configure Spring in its XML configuration file to inject the proxy into the code. This allows us to make the client side access of the remote JMX MBean even easier. The method for accessing the remote object now simplifies to this:


/**
* Demonstrate accessing remote JMX agent via RMI and via a client-side proxy
* with Spring injecting the proxy so that it does not need to be provided as
* a parameter.
*/
public void demonstrateClientJmxWithProxyInjected()
{
System.out.println( "PROXY (Spring-injected) Says: The Status is "
+ this.status.getStatus() );
}


As the simple code listing above demonstrates, it is really easy now to access the remote MBean. In fact, this code shows no obvious signs that an MBean is being accessed remotely. To make this work, a data member was added to the class to store the status and it was injected via Spring using the additional XML snippet shown next:


<bean id="remoteMBean"
class="org.springframework.jmx.access.MBeanProxyFactoryBean"
p:objectName="dustin:type=status,name=remoteJMX"
p:server-ref="mBeanServerClient"
p:proxyInterface="jmx.spring.rmi.example.StatusBeanIF" />


I then need to wire this remotely accessible MBean to the bean that will be using it. The XML for that is shown next with the new line added for this wiring highlighted.


<bean id="jmxSpringClient"
class="jmx.spring.rmi.example.springClient.SpringJmxClientBean"
p:mbeanServerConnection-ref="mBeanServerClient"
p:remoteMbean-ref="remoteMBean" />


When used to its full extent, Spring allows for the configuration and injection of both the MBeanServerConnection and the proxy to the remote MBean to be implemented via XML and removed from the code. This greatly simplifies the client-side code.

The conclusion from all of this is that whether using Spring for your JMX client or not using Spring for your JMX client, the use of proxies is generally simpler and safer than using direct reflection. It is not surprising, therefore, that the JMX Best Practices recommend using proxies for the client-side access of remote JMX MBeans.