Monday, September 29, 2008

The Surprisingly Simple StackTraceElement

JDK 1.4 introduced so many new features to the Java landscape that it was easy to focus on a few at the expense of others. New JDK 1.4 features such as built-in Java logging, built-in XML support, Java WebStart, the Assertion Facility, the Preferences API, and the incorporation of key Java security APIs dominated the long list of new features for many of us.

One feature that I appreciated when JDK 1.4 was released, but did not focus on, was the Chained Exception Facility. This was a feature that I have taken advantage of countless times since in my application exception handling, but I did not need to and chose not to focus much on it. It was just there and I have benefited from it tremendously. As part of JDK 1.4's renovation of exception handling, an important advancement was the ability to access the exception stack trace programmatically. This blog entry looks at this feature that has been around for some time now, but is useful in multiple different circumstances.

JDK 1.4 added the getStackTrace method to Throwable and this method returns an array of StackTraceElement. The StackTraceElement class provides easy access to information about each stack frame in the stack trace.

There are two obvious uses of the StackTraceElement that come to mind. One is for parsing the contents of an exception stack trace for specific details. The other obvious use of StackTraceElement is to provide metadata when implementing simple logging messages. I'll demonstrate brief examples of each of these two primary uses of StackTraceElement in this blog entry.

There are several reasons that a Java developer may want to parse an exception's metadata. These reasons include conditional logic based on exception metadata and generation of appropriate responses to service calls from internally thrown exceptions. The following code listing demonstrates how an exception's metadata can be extracted through the StackTraceElement. [Note that all code listings in this blog entry exist in a class called StackTraceElementDemonstrator.]


/**
* Extract exception details from Exception using StackTraceElement. The
* StackTraceElement class makes it simple to extract specific details from
* a given Exception because Throwable (Exception's parent) provides a
* getStackTrace method.
*
* http://java.sun.com/javase/6/docs/api/java/lang/Throwable.html
*/
public void demonstrateExceptionExtraction()
{
try
{
intentionallyThrowException();
}
catch (Exception exception)
{
parseExceptionContents(exception, System.out);
}
}

/**
* Intentionally throw an exception that can be caught and processed with
* StackTraceElement instances.
*/
private void intentionallyThrowException()
{
throw new UnsupportedOperationException(
"You called a method that has not yet been implemented.");
}

/**
* Parse the contents of the passed-in exception.
*
* @param exception Exception whose contents are desired.
* @param out OutputStream to which to write the exception contents.
*/
private void parseExceptionContents(
final Exception exception,
final OutputStream out)
{
final StackTraceElement[] stackTrace = exception.getStackTrace();
int index = 0;
for (StackTraceElement element : stackTrace)
{
final String exceptionMsg =
"Exception thrown from " + element.getMethodName()
+ " in class " + element.getClassName() + " [on line number "
+ element.getLineNumber() + " of file " + element.getFileName() + "]";
try
{
out.write((headerLine + newLine).getBytes());
out.write((headerTitlePortion + index++ + newLine).getBytes() );
out.write((headerLine + newLine).getBytes());
out.write((exceptionMsg + newLine + newLine).getBytes());
out.write(
("Exception.toString: " + element.toString() + newLine).getBytes());
}
catch (IOException ioEx)
{
System.err.println(
"IOException encountered while trying to write "
+ "StackTraceElement data to provided OutputStream.\n"
+ ioEx.getMessage() );
}
}
}


The code listing above leads to the output shown in the following screen snapshot:



From this screen snapshot, we clearly see that the useful StackTraceElement in this particular array is the first entry (index of zero). This is not surprising when considered in light of the Javadoc documentation for StackTraceElement, which states that the top frame is normally the generation point of the exception.

A second use of StackTraceElement is to provide metadata for a simple logging implementation. This concept is demonstrated in How to Get Java Source Code Line Number and File Name in Code. While the array of StackTraceElement returned from the Throwable.getStackTrace() method helped extract exception details in the example above, the same approach can be used even for logging non-exceptional circumstances when using in conjunction with the Thread class.

The Thread class has a static method called currentThread() that can be used to statically acquire a handle on the current thread and then the method getStackTrace() can be called on that current thread. That method returns an array of StackTraceElement just as the same-named method on Throwable did in the Exception example above. However, in the case of normal thread execution, the particular StackTraceElement of interest in the array is not likely to be the top (zero index) element.

The following code demonstrates using Thread.currentThread().getStackTrace() to get a handle of the array of StackTraceElement and demonstrates using the StackTraceElement with index of 2 to get information on the method that actually called the log statement.


/**
* Demonstrate use of StackTraceElement in custom logging.
*/
public void demonstrateLogging()
{
logThisMessage("Entered the demonstrateLogging() method.", System.out);
}

/**
* Log the provided String along with log-related metadata such as file name
* and line number.
*
* @param messageToLog String message to log.
* @param out OutputStream to which to write the log message.
*/
private void logThisMessage(
final String messageToLog,
final OutputStream out)
{
final StackTraceElement[] stackTrace =
Thread.currentThread().getStackTrace();
StackTraceElement logElement = stackTrace[2];
printContentsOfStackTrace(stackTrace, out);
try
{
final String messageToOutput =
"[" + logElement.getFileName() + ":" + logElement.getLineNumber()
+ "] " + logElement.getClassName() + "." + logElement.getMethodName()
+ ": " + messageToLog;

out.write((newLine + headerLine + newLine).getBytes());
out.write(("-- Logging Message: " + messageToLog + newLine).getBytes());
out.write((headerLine + newLine).getBytes());
out.write((messageToOutput + newLine).getBytes());
}
catch (IOException ioEx)
{
System.err.println(
"IOException encountered trying to log message with "
+ "StackTraceElement:\n" + ioEx.getMessage());
}
}

/**
* Print contents of stack trace to provided OutputStream.
*
* @param stackTrace Stack Trace to write out.
* @param out OutputStream to which to write stack trace elements.
*/
public void printContentsOfStackTrace(
final StackTraceElement[] stackTrace,
final OutputStream out)
{
int index = 0;
for (StackTraceElement element : stackTrace)
{
try
{
out.write(newLine.getBytes());
out.write(("Index: " + index++ + newLine).getBytes() );
out.write(("ClassName: " + element.getClassName() + newLine).getBytes() );
out.write(("MethodName: " + element.getMethodName() + newLine).getBytes() );
out.write(("FileName: " + element.getFileName() + newLine).getBytes() );
out.write(("LineNumber: " + element.getLineNumber() + newLine).getBytes());
}
catch (IOException ioEx)
{
System.err.println(
"IOException trying to write out contents of StackTraceElement[]:\n"
+ ioEx.getMessage() );
}
}
}


The example code above leads to two different portions of output. The method in the code listing above called logThisMessage logs the stack trace element details from the one entry in the returned StackTraceElement array that represents the code invoking the logging. Its output is depicted in the screen snapshot below:



This output demonstrates that the index value of 2 (third array element) was the appropriate choice for extracting information on the source code that called the logging method. If one wonders why it is 2, the next code sample (parseExceptionContents method) answers that question.


/**
* Parse the contents of the passed-in exception.
*
* @param exception Exception whose contents are desired.
* @param out OutputStream to which to write the exception contents.
*/
private void parseExceptionContents(
final Exception exception,
final OutputStream out)
{
final StackTraceElement[] stackTrace = exception.getStackTrace();
int index = 0;
for (StackTraceElement element : stackTrace)
{
final String exceptionMsg =
"Exception thrown from " + element.getMethodName()
+ " in class " + element.getClassName() + " [on line number "
+ element.getLineNumber() + " of file " + element.getFileName() + "]";
try
{
out.write((headerLine + newLine).getBytes());
out.write((headerTitlePortion + index++ + newLine).getBytes() );
out.write((headerLine + newLine).getBytes());
out.write((exceptionMsg + newLine + newLine).getBytes());
out.write(
("Exception.toString: " + element.toString() + newLine).getBytes());
}
catch (IOException ioEx)
{
System.err.println(
"IOException encountered while trying to write "
+ "StackTraceElement data to provided OutputStream.\n"
+ ioEx.getMessage() );
}
}
}


The parseExceptionContents method produces output like that shown in the next screen snapshot.



This output demonstrates why the magical "2" was needed to get the appropriate StackTraceElement from the array returned by Thread.currentThread.getStackTrace(). The first StackTraceElement (index of 0) represents the last stack frame (the call to current thread to get the stack trace) and the second index (index of 1) represents the stack frame for the logThisMessage method that was called by the method wanting to log. The third StackTraceElement with index value of 2 actually belongs to the method wanting to log (demonstrateLogging) and the last stack frame (index of 3) is the main function that called the method that would want to log a message. This is summarized here:

0 -> Thread.getStackTrace
  1 -> StackTraceElementDemonstrator.logThisMessage
    2 -> StackTraceElementDemonstrator.demonstrateLogging
      3 -> StackTraceElementDemonstrator.main


In this blog entry, I have summarized the ease and convenience of the StackTraceElement in parsing exceptions and in simple logging mechanisms. All of the code listings shown above are shown again below in a complete class listing for the StackTraceElementDemonstrator class.


StackTraceElementDemonstrator.java


package dustin.example;

import java.io.IOException;
import java.io.OutputStream;

/**
* This class demonstrates the great utility of the StackTraceElement class
* that was introduced with JDK 1.4.
*
* @author Dustin
*/
public class StackTraceElementDemonstrator
{
final String newLine = System.getProperty("line.separator");
final String headerLine =
"-----------------------------------------------------------------------";
final String headerTitlePortion =
"-- StackTraceElement Index #";

/** No-argument constructor. **/
public StackTraceElementDemonstrator() {}

/**
* Extract exception details from Exception using StackTraceElement. The
* StackTraceElement class makes it simple to extract specific details from
* a given Exception because Throwable (Exception's parent) provides a
* getStackTrace method.
*
* http://java.sun.com/javase/6/docs/api/java/lang/Throwable.html
*/
public void demonstrateExceptionExtraction()
{
try
{
intentionallyThrowException();
}
catch (Exception exception)
{
parseExceptionContents(exception, System.out);
}
}

/**
* Intentionally throw an exception that can be caught and processed with
* StackTraceElement instances.
*/
private void intentionallyThrowException()
{
throw new UnsupportedOperationException(
"You called a method that has not yet been implemented.");
}

/**
* Parse the contents of the passed-in exception.
*
* @param exception Exception whose contents are desired.
* @param out OutputStream to which to write the exception contents.
*/
private void parseExceptionContents(
final Exception exception,
final OutputStream out)
{
final StackTraceElement[] stackTrace = exception.getStackTrace();
int index = 0;
for (StackTraceElement element : stackTrace)
{
final String exceptionMsg =
"Exception thrown from " + element.getMethodName()
+ " in class " + element.getClassName() + " [on line number "
+ element.getLineNumber() + " of file " + element.getFileName() + "]";
try
{
out.write((headerLine + newLine).getBytes());
out.write((headerTitlePortion + index++ + newLine).getBytes() );
out.write((headerLine + newLine).getBytes());
out.write((exceptionMsg + newLine + newLine).getBytes());
out.write(
("Exception.toString: " + element.toString() + newLine).getBytes());
}
catch (IOException ioEx)
{
System.err.println(
"IOException encountered while trying to write "
+ "StackTraceElement data to provided OutputStream.\n"
+ ioEx.getMessage() );
}
}
}

/**
* Demonstrate use of StackTraceElement in custom logging.
*/
public void demonstrateLogging()
{
logThisMessage("Entered the demonstrateLogging() method.", System.out);
}

/**
* Log the provided String along with log-related metadata such as file name
* and line number.
*
* @param messageToLog String message to log.
* @param out OutputStream to which to write the log message.
*/
private void logThisMessage(
final String messageToLog,
final OutputStream out)
{
final StackTraceElement[] stackTrace =
Thread.currentThread().getStackTrace();
StackTraceElement logElement = stackTrace[2];
printContentsOfStackTrace(stackTrace, out);
try
{
final String messageToOutput =
"[" + logElement.getFileName() + ":" + logElement.getLineNumber()
+ "] " + logElement.getClassName() + "." + logElement.getMethodName()
+ ": " + messageToLog;

out.write((newLine + headerLine + newLine).getBytes());
out.write(("-- Logging Message: " + messageToLog + newLine).getBytes());
out.write((headerLine + newLine).getBytes());
out.write((messageToOutput + newLine).getBytes());
}
catch (IOException ioEx)
{
System.err.println(
"IOException encountered trying to log message with "
+ "StackTraceElement:\n" + ioEx.getMessage());
}
}

/**
* Print contents of stack trace to provided OutputStream.
*
* @param stackTrace Stack Trace to write out.
* @param out OutputStream to which to write stack trace elements.
*/
public void printContentsOfStackTrace(
final StackTraceElement[] stackTrace,
final OutputStream out)
{
int index = 0;
for (StackTraceElement element : stackTrace)
{
try
{
out.write(newLine.getBytes());
out.write(("Index: " + index++ + newLine).getBytes() );
out.write(("ClassName: " + element.getClassName() + newLine).getBytes() );
out.write(("MethodName: " + element.getMethodName() + newLine).getBytes() );
out.write(("FileName: " + element.getFileName() + newLine).getBytes() );
out.write(("LineNumber: " + element.getLineNumber() + newLine).getBytes());
}
catch (IOException ioEx)
{
System.err.println(
"IOException trying to write out contents of StackTraceElement[]:\n"
+ ioEx.getMessage() );
}
}
}

/**
* @param arguments The command line arguments; none anticipated.
*/
public static void main(final String[] arguments)
{
final StackTraceElementDemonstrator me =
new StackTraceElementDemonstrator();
me.demonstrateExceptionExtraction();
me.demonstrateLogging();
}
}



While I have used and do use many of the features introduced with JDK 1.4 every day and take advantage of the chained exception handling frequently without even thinking about it, use of the StackTraceElement is something that only comes up occasionally, but is very welcome when needed.

Saturday, September 27, 2008

Standardization: The Dangerous Relationship for Open Source

There are many examples in software development of open source products introducing useful concepts and approaches that are later standardized. It is often the case that the creative developers behind the most successful open source products can deliver innovative features much quicker than a standards committee could ever hope to do. Standards are, by their nature, more difficult to create because they require consensus of so many different organizations and people with different agendas and ideas of what is best. However, standards creators can adopt tried and proven features introduced by open source (and commercial) offerings once the value of the features is proven.

Ironically, once a standardized approach provides the desirable features originally offered by the open source project, the open source product's future is often eclipsed by that of the standardized approach. In this blog entry, I'll look at some of the open source products that have provided great benefit to only be overtaken in use by the standards they inspired. I will also look at some open source products that managed to survive and even thrive against the standardized competition offering the same features. From these products, I will observe what I believe makes these products successful.


XDoclet and Apache Commons Attributes

XDoclet is one of the most obvious examples of an open source product providing a useful feature, but being "overcome by events" when standardized approaches adopted that feature. XDoclet's popularity rose rapidly as J2EE (before it was Java EE) developers recognized many benefits of being able to annotate source code to have deployment descriptors and boilerplate interfaces generated from the source code. However, once annotations were formally added to the Java programming language with J2SE 5, use of XDoclet fell precipitously because its main benefits and features could be achieved using standards. XDoclet's main page currently shows its "Last Published" date as 5 May 2005 and its most recent news item is dated 23 October 2004 (XDoclet 1.2.2 released).

Like XDoclet, Apache Commons Attributes became less necessary with the adoption of annotations in Java. As of this writing, the last news update on the Commons Attributes page is dated 3 August 2006 (release of Attributes 2.2) and the "Last Published" date is 1 August 2007. According to Jürgen Höller's presentation Spring 2.5 on the Way to 3.0, it appears that Spring 3 will "prune" Commons Attributes support.

Of course, not everyone is using J2SE 5 or Java SE 6 and many legacy Java applications running in newer JREs may not have been ported specifically to use new features, so it is not surprising that XDoclet and Commons Attributes are still used, albeit at a much lower rate than in their peak years.


Log4j

I must admit my surprise at the resiliency of Log4j despite the introduction of many Log4j-like concepts with the JDK 1.4 introduction of java.util.logging as a standard, built-in approach to logging from Java applications. The "Last Published" date for the main Log4j web page is, as of this writing, 1 September 2007, and the latest news item is dated 29 August 2007 (release of log4j 1.2.15 and log4j extras 1.0).

Although I know many developers on applications using JDK 1.4, J2SE 5, and Java SE 6 that still prefer log4j (because of features in Log4j that are not in java.util.logging), the lack of recent changes to log4j and the changes of direction involved with Log4j 1.2, Log4j 1.3 (abandoned), and Log4j 2.0 (experimental) lead me to believe that some of the steam is running out for log4j. This may be partly due to the feeling that there is not much more that needs to be added to an already useful logging tool, but it may also be due at least partially to the existence of the standardized java.util.logging package. The standard Java logging package has started to add numerous useful features such as the ability, via JMX, to set the logging level dynamically and remotely.

Another open source project, Apache Commons Logging, allows developers to use both or move between Log4j and java.util.logging. The presence of this project illustrates that log4j has not gone away.


JDOM

JDOM was wildly popular for some time because this non-standard library was so much easier to use than other available approaches. In particular, JDOM's strategy of supporting Java-specific APIs rather than the more XML-general APIs supported by alternatives made it much more approachable for a large number of Java developers.

JDOM's latest news entry is the release of JDOM 1.1 on 18 November 2007. While JDOM still seems to be used on a large number of Java-based projects, most of my anecdotal experience has been with new applications using more standard approaches such as Java API for XML Processing (JAXP) and Java API for XML Binding (JAXB). JAXB, in particular, does a nice job of abstracting XML specifics so that Java developers can focus on using Java objects bound to XML rather than on the XML. The addition of XPath support to Java with the javax.xml.xpath package has provided Java developers with yet another standard approach for querying XML from Java.


Hibernate

Hibernate is one of the best examples of a non-standard open source project becoming a de facto standard. Hibernate might be most simply described as a Java object-relational mapping (ORM) framework. Hibernate heavily inspired the newer and actual standard Java Persistence API (JPA). Hibernate has remained a major force in the ORM world partly because its developers made the decision to tweak Hibernate to provide a JPA implementation. In other words, rather than Hibernate competing against JPA, Hibernate is an implementation of JPA.


Spring Framework

Spring Framework founder Rod Johnson's book J2EE Design and Development (and its sequel J2EE Without EJB) not only laid the groundwork for the Spring Framework, but also had enormous impact on the enterprise Java community. Not only did the Spring Framework help bring inversion of control (IoC) and dependency injection (DI) into the Java mainstream, but the framework also helped reinforce the benefits of using regular Java classes rather than numerous interfaces and class inheritance hierarchies.

JSR-220 adopted many of the features that were most popular in Spring and incorporated them into EJB 3 and Java EE 5. These improvements made Java EE much easier to use, but I believe that many enterprise Java developers are still jaded by their experience with EJB 2.x and have not adopted Java EE 5/EJB3/JPA as quickly as one might have expected.

Besides the fact that negative experiences with EJB 2.x may have jaded many of us and turned us cynical about EJBs, I think there are other explanations for Spring's success despite Java EE adopting many of Spring's concepts and features. These reasons for Spring's success include its great support for reducing boilerplate code related to Java APIs such as JDBC, JMX, JMS, RMI, and many others. The Spring Framework has continued to offer cutting edge features like OSGi support to continually differentiate it from more standard alternatives. Also, once many developers had switched from EJB 2.x to Spring, it would require enough pain or dissatisfaction with Spring to return to EJB.

I am curious to see if recent SpringSource announcements regarding their new Enterprise Maintenance Policy and their new licensing models for new products and the recent blogs questioning the openness of Spring impact the number of developers and applications using Spring. While I believe that these developments undoubtedly will turn a certain number of developers away from Spring, the real question is how many that will be. It is highly possible that Spring will lose a relatively large fraction of users/developers, but that SpringSource will still be monetarily better off with the smaller user base. I believe that these recent developments in the Spring community may be enough of a reason in some developers' minds to try out alternatives, including returning to EJB.


Other Examples

There are many more examples of where an open source product has brought forth great innovation, has inspired or directly fed into a standard approach, and then has fallen by the wayside as the standard alternative satisfies the same needs in a standard way. Examples include Doug Lea's util.concurrent library (fed into and replaced by java.util.concurrent package) and MC4J (replaced largely by JConsole and VisualVM, which are delivered with Sun's Java SE 6 JVM).


Observations

From this quick tour of open source products which have had more standardized approaches adopt some of their biggest benefits and most valuable features, a few observations can be made regarding what keeps these products alive.

1. Open source frameworks and toolkits that offer more than the standardized version offers may survive because they do offer these "extras" that make using the less standard product preferable even to using the more standardized product. Examples of this include log4j providing more than java.util.logging and Spring Framework offering more than just the dependency injection and POJO-orientation now available in EJB3. Other examples include the continued popularity of Apache XMLBeans and Castor despite the availability of standardized JAXB.

2. Strength and commitment of the open source product's community also have an effect. The commitment and interest of the primary developers of the open source product are particularly important in the success of the product when more standardized approaches offer many of the same benefits. The open source products must continue to innovate and offer features the standard counterparts don't.

3. Perhaps most importantly, an non-standard open source project is most likely to survive once a standard alternative adopts it best features and concepts if the open source product is willing to adapt to complement the standard approach and add value to it. This is the strategy that Hibernate seems to have taken and it is true (even if not all that common) that the Spring Framework can be used with a Java EE application server and with EJBs.


Conclusion

Because standards bodies are, by their nature, almost always going to generate innovation at a slower pace than a few creative and like-minded individuals can, it is not surprising that standards have and will continue to adopt some of their best features from proven open source frameworks. Also, because many of these open source projects are started simply because a (usually painful) gap is identified in the existing standards, it is also not surprising that many of these projects will be less necessary once the standard alternatives do adopt the features they provide. In fact, I think there are few things that can be more satisfying to the ego of an open source product founder than having his or her ideas accepted enthusiastically and adopted into standards, even if it means a lessening importance of his or her open source product. However, anyone starting an open source product to satisfy a glaring omission in the current standards implementations must realize that his or her success will often be the very thing that eventually leads to that very product losing its significance in the software development world.


Additional Resources

* Open Standards, Open Source

* Open Source and Standards

* Open Source and Open Standards

Tuesday, September 23, 2008

gradientview: From the OpenLaszlo Incubator

I have blogged several times recently on different components in the OpenLaszlo Incubator. I have blogged on the tooltips component (OpenLaszlo ToolTips), the Validators (From the OpenLaszlo Incubator: Validator), and the ColorPicker (ColorPicker: From the OpenLaszlo Incubator). I have not written about the OpenLaszlo richtexteditor, but a good example with source code (right click on it and select "View Source") is at http://sbshine.net/proj/richtext/. In this blog entry, I'll very briefly look at how to use the gradientview from the OpenLaszlo Incubator.

The code listing for LaszloGradientViewExample.lzx is shown below. It demonstrates use of the gradientview and specification of some key attributes of that class as attributes of the XML element. When the generated gradientview is clicked on, the "onclick" event handler changes the colors of the gradientview, changes the orientation of the gradient, and changes the final text string in the gradientview. Note also that it is important, as the comments in the code suggest, to set opacity to less than its default of 1.0 so that the text inside the gradientview can be seen.

LaszloGradientViewExample.lzx

<canvas title="OpenLaszlo GradientView Example" debug="true">

<include href="incubator/gradientview.lzx" />

<script>
initialBorderColor = 0x003300;
initialFromColor = 0xCCFFCC;
initialToColor = 0xFFFF99;
borderColor = initialBorderColor;
</script>

<gradientview borderSize="2"
borderColor="${borderColor}"
borderColorRight="${borderColor}"
borderColorLeft="${borderColor}"
borderColorTop="${borderColor}"
borderColorBottom="${borderColor}"
colorFrom="${initialFromColor}"
colorTo="${initialToColor}"
opacity="0.5">
<simplelayout axis="y" spacing="25" />
<statictext name="text1"
text="This is a simple example of gradientview."
fgcolor="0x000000" fontstyle="bolditalic" />
<statictext name="text2"
fgcolor="0x000000" fontstyle="bold"
>It makes it easy to make backgrounds far more interesting.</statictext>
<text name="text3"
>You need to set opacity to something less than the default of 1.0.</text>
<text name="text4"
text="Opacity of 1.0 blocks out all text on view." />
<handler name="onclick">
this.setAttribute("borderColor", 0x222222);
this.setAttribute("colorFrom", 0xFFCCFF);
this.setAttribute("colorTo", 0xCCFF00);
this.text4.setAttribute("text", "My opacity is set to 0.5!");
this.setAttribute("gradientOrientation", "horizontal"); // Vertical by default
this.draw();
</handler>
</gradientview>

</canvas>


To demonstrate the above code being executed, the following four screen snapshots shown the SWF8 initial and final versions of the gradientview and the DHTML initial and final versions of the gradient view with all four screen snapshots taken using the Firefox web browser.


Initial State of Application as SWF on Firefox




Final State of Application as SWF on Firefox




Initial State of Application as DHTML on Firefox




Final State of Application as DHTML on Firefox




You may have noticed that the text of the DHTML version changed after the click, but that there are never any gradient effects in the DHTML version. This illustrates the point that there are some components within OpenLaszlo as well as some other features that are specific to one runtime or another. This is, of course, necessary to enjoy the full advantages of each runtime when alternative runtimes do not have that feature, but it is highly desired. In this case, the DHTML runtime supports the application well enough to still provide the information at a minimally acceptable level even if it is not as eye-catching. OpenLaszlo provides ways to determine the environment at runtime and an alternative for DHTML could have been used here.

In my recent series of blog entries on the OpenLaszlo Incubator, I have attempted to demonstrate some key components provided in the Incubator. This is my final planned blog entry dedicated to a component in the OpenLaszlo Incubator, but there are many more useful OpenLaszlo Incubator components beyond those that I have discussed.

Monday, September 22, 2008

ColorPicker: From the OpenLaszlo Incubator

I have blogged previously on components from the OpenLaszlo Incubator, including OpenLaszlo ToolTips (tooltips) and From the OpenLaszlo Incubator: Validators (layoutform and validators). In this blog entry, I'll show an extremely simple example of how to use the ColorPicker component from the OpenLaszlo Incubator and will show it in action in SWF and DHTML runtimes in the Firefox 3, Internet Explorer 7, and Google Chrome 0.2 web browsers.

My simple example application demonstrates that the ColorPicker allows a color to be selected tentatively by dragging the bars on the bottom three rows of the widget or by selecting a color box in the upper portion of that widget. The color selected tentatively with either of these approaches then appears in the vertical rectangle on the right. Clicking on that rectangle or on the top title bar of the ColorPicker window kicks off the "onclick" event and the selected color is applied to the canvas's background color. The source code for this sample is very brief. It is shown below and is called ColorPickerExample.lzx.

ColorPickerExample.lzx

<canvas title="Very Simple OpenLaszlo ColorPicker Example" debug="true">

<include href="incubator/colorpicker.lzx" />

<simplelayout axis="y" spacing="50" />

<colorpicker title="Select Your Favorite Color"
allowdrag="false" width="275">
<handler name="onclick">
canvas.setAttribute("bgcolor", this.selectedColor);
</handler>
</colorpicker>

</canvas>


This simple code example leads to a color picker that is demonstrated in a series of images in DHTML and in SWF8 on the three web browsers mentioned previously. A label above each screen snapshot indicates which runtime and web browser is being used. It is difficult to see major differences in the output no matter the browser or the SWF or DHTML runtime. That is exactly the point!


Initial ColorPicker in SWF8 on Firefox




Initial ColorPicker in DHTML in Firefox




Initial ColorPicker in SWF8 on Internet Explorer




Initial ColorPicker in DHTML on Internet Explorer




Initial ColorPicker in SWF8 on Google Chrome




Initial ColorPicker in DHTML on Google Chrome




Tentatively Selected Color in SWF8 on Firefox




Color Selected and Applied in SWF8 on Firefox




Color Selected and Applied in DHTML on Firefox




Color Selected and Applied in SWF8 on Internet Explorer




Color Selected and Applied in DHTML on Internet Explorer




Color Selected and Applied in SWF8 on Google Chrome




Color Selected and Applied in DHTML on Google Chrome




Although this example is very simple, it demonstrates how easy it is to use the ColorPicker from the OpenLaszlo Incubator to allow users to select colors that can be easily applied to the application's style. In this example, the background of the canvas was changed as a color was selected in the ColorPicker.

Thoughts on SpringSource's Enterprise Maintenance Policy

UPDATE (11 October 2008): SpringSource has tweaked the policy described below to a significant enough extent that I believe it the altered new policy will be palatable to the vast majority of developers using the Spring Framework. More information on this is available in SpringSource Regains Footing with Support Change and A Question of Balance: Tuning the Maintenance Policy.




The recent mini controversy surrounding SpringSource's Enterprise Maintenance Policy announcement provides interesting material for a look at how different people view open source differently. I have talked with professional software developers who still equate "open source" with "free software" rather than recognizing the classic differentiation between "free speech" and "free beer." In fact, I used to be one of these individuals until I attended multiple editions of the Colorado Software Summit in the early 2000s in which Simon Phipps explained each year different ways of looking at what it means to be open source and to be part of an open source community.

The Spring Framework has become wildly successful and widely adopted. There has been some discussion regarding how much "community" participation there is in Spring versus SpringSource direct involvement with Spring. In this blog entry, I intend to examine competing arguments that have been or might be made in this controversy. I can understand the motivations of the principals of both sides of the argument and I want to look at the issue from both points of view. I also intend to look at some possible results of this SpringSource announcement.

This recent announcement by SpringSource and the reactions to the announcement have caused me to reflect on my own participation in the Spring community. I think in may ways I may be representative of the "typical" Spring "community" member. I have never contributed a line of code to Spring and have not even written up a defect or enhancement request (JIRA). However, I feel like I have at least contributed a little to the Spring community by writing an article on Spring for Oracle Technology Network called Add Some Spring to Your Oracle JDBC Access (November 2005) and blogging on Spring occasionally. In addition, as some have stated in their feedback to the recent announcement, I have purchased Spring-related books and told colleagues about Spring. However, these are, in some respects, indirect support rather than direct contributions to the framework itself.

There is no question that we become accustomed to getting what we have regularly received in the past and do not like to hear that this freely given benefit will be removed or have some cost applied to it. For example, when an employer takes away a benefit we have received in the past, few employees will "welcome" that. From this very human perspective, it is natural to not "welcome" the news that SpringSource will not be publishing releases of the Spring Framework to non-Enterprise users after a major release has been out for three months. After all, if I have been enjoying the benefits of polished releases of software for years, why would I welcome extra work on my part or a subscription fee to continue to receive that same benefit?

My understanding is that bug fixes will continue to be available in the repository trunk, but that they will no longer be available in a binary form after the 3-month period following each major release. For many organizations that use Spring, this is a minor new hindrance to using the framework, but probably not enough of a hindrance to stop using the framework altogether unless something else is available for which the benefit-to-cost ratio seems better.

While the Spring Framework's popularity has grown tremendously, there are a growing number of popular alternatives. For example, Java EE 5 (including EJB3) has borrowed heavily from Spring and, in my opinion, is a now a reasonable alternative. Many of the issues that drove developers from EJB 2.x to Spring have now been resolved. There are also other non-standard but popular frameworks like Google's Guice that are available. What developers need to ask themselves is if the added cost of using Spring from here on out (either the cost of the enterprise support or the cost of building Spring releases) is still worth the benefits of using Spring and if the ratio of these benefits to the costs is still better than the ratio of using an alternative to the cost of using that alternative. For example, a developer may choose to use the simplified Java EE 5 on future projects because the extra effort associated with Java EE is so significantly reduced and the ability to write standard code regardless of providers is a desirable advantage. In fact, with a product like GlassFish, that developer can have an open source and standard Java EE 5 implementation.

Developers will likely have some options between becoming SpringSource Enterprise subscribers and ditching Spring altogether for an alternative. Many major vendors already use Spring in their products. As long as these vendors of IDEs (such as NetBeans 6.1 support and Spring Extension for JDeveloper), application servers (such as WebLogic and Oracle Containers for Java EE), other frameworks (such as AppFuse), and other products continue releasing their Spring-based and Spring-supporting products with new versions of Spring, some of the bug fixes and updates may become available to end-user developers via these third-party products. There is always the possibility that these third-party products could drop support for Spring due to this announcement, but the announcement does not seem onerous enough to me to cause that to happen. Finally, there has already been talk of starting a community effort to fill in the relatively small gap being left by this SpringSource announcement. This proposed project would, in theory, be able to take the source code on the publicly exposed repository and build it into a finished distributable like SpringSource does today. There has even been talk of a complete fork off of Spring.

Some developers are feeling trapped. They have tied their products heavily to Spring and so it is not as easy as simply switching to an alternative. Ironically, Spring creator Rod Johnson pointed out in the excellent book J2EE Design and Development that good design includes isolating non-standard and changeable code from standard and stable code. If developers followed this advice with Spring (and in many ways Spring does encourage this isolation), then the migration would be easier than it would have been from proprietary libraries or other third-party frameworks. Even so, the question to ask if if the new cost of using Spring (three year subscription or building Spring on one's own) is enough to justify the certain cost associated with switching to an alternative.

For some larger organizations, this actually might be welcome news because there are still large corporations that prefer professional support. Some clients and some organizations have been reluctant to use any open source products that did not have professional support. They are likely to already be using Spring's Enterprise support and so this won't impact them. Others may have not chosen this option, but do not mind paying the subscription fee and consider it a relatively small cost.

In his blog entry Open Source, Open Strategy: The SpringSource Manifesto, Rod Johnson covers the history of SpringSource (including its days as Interface21) and outlines reasoning behind using a different license for some of the newer products released by SpringSource. He makes valid points such as the fact that many companies sell products using the Spring Framework for significant portions of their functionality. An important and highlighted note in this blog entry is this statement: "We are not changing and will not change the license of any existing project." He goes on to add that this includes the Spring Framework and Spring Security. I think this is significant. The recent Enterprise Maintenance announcement is less of a blow if existing and future Spring Framework code remains open source under the Apache license.

Some have charged that SpringSource is only making this change in support because of their wide adoption and the dependencies people have on the framework. It seems that it is obvious that a move like this is much more likely to be successful after a framework has been evangelized and adopted rather than in its infancy, so I don't think any of us should be too surprised. SpringSource certainly does have some leverage right now and it is certainly greater than it would have been a number of years ago. In fact, in the blog entry Pumping It Dry: $200 a Barrel and $25,000 per CPU, Rod Johnson points out similar tactics in terms of revenue raising implemented by Oracle after their acquiring BEA.

My biggest concern about the recent SpringSource announcement is not the content of the announcement itself. With the assertion in the summer blog about the license for Spring Framework not changing and with Rod Johnson's assertion at TheServerSide's New Spring Maintenance Policy that "SpringSource continues to expose our open source code", I do not see any reason for immediate panic or knee-jerk reactions. Instead, my much bigger concern is worry that this might only be the first in a long series of steps that make Spring less attractive. It will definitely enter my mind when trying to decide how dependent on Spring I want my application to be. In other words, I may choose to use options where I don't need to implement a Spring interface.

It seems obvious that this decision was made to increase SpringSource revenues. I understand that and empathize with the talented individuals who have sacrificed to make Spring what it is today. They definitely deserve some payback for the sacrifice. Making money on open source development is a tricky business model and it is not surprising that it occasionally needs tweaking. That being said, what happens if this move is not enough to garner that payback? Will the license be changed in the future for new versions/releases or will more other much more drastic measures be taken to gain the desired revenue? I like to think that won't happen, but the recent announcement at least reminds one of that possibility.

The long-term implications of this announcement are interesting to ponder. The blog entry SpringSource Will Charge for Updates to Spring, What Comes Next? provides some possible next steps that SpringSource might take in this same direction. While that blog entry focuses on what SpringSource may do, I also wonder how the community will evolve as a result of this announcement.

The following are some questions whose answers we will likely not know for certain until some time has played out.

* Will there be a dramatic decrease in the number of developers and projects using SpringSource products (especially the Spring Framework)?

* Will there be fewer blog entries, articles, and other coverage of Spring Framework due to this policy change?

* Will the developers that stop using Spring be missed by SpringSource if they weren't providing any type of direct monetary benefit to SpringSource?

* Will the developers that stop using Spring be missed by the community if they weren't contributing directly or indirectly to the community?

* What percentage of the developers that stop using Spring or reduce their usage of Spring will be contributors who have performed useful testing and bug and enhancement reporting?

* Will the new policy allow SpringSource to retain and acquire the necessary talent and resources to make the products better for users?

* Will this announcement be the beginning of a bright new era or the beginning of the end of Spring's rapidly growing dominance?

I won't be surprised if this new policy is the subject of some hallway discussion and even some Q&A discussion at the Colorado Software Summit 2008 that occurs four weeks from now. With Matt Raible speaking on What's Coming in Spring 3.0, with Chris Richardson (author of POJOs in Action) speaking, and with a SpringSource employee (Filip Hanik) presenting, it seems likely this will come up.

No product is perfect and the Spring Framework is no different. However, many enterprise Java developers have found it to be extremely useful and helpful and a welcome release from the complications of EJB 2.x. When evaluating a product, I like to look at the entire package including all advantages and disadvantages. The Spring Enterprise Management policy should be considered along with Spring's advantages and disadvantages when deciding whether or not to use Spring. For some, it may be an advantage, for some it may be considered a significant disadvantage, and for many of us it will probably only be a minor disadvantage. The more interesting questions involve the effect it will have on Spring, on the Spring community, and if it is just the first of additional and even more dramatic steps in this direction.

Thursday, September 18, 2008

From the OpenLaszlo Incubator: Validator

The OpenLaszlo Incubator includes several useful components such tooltips, rich text editor, and the validators. I have blogged previously about the OpenLaszlo Incubator. The Incubator Directory opens up by providing background on the nature of the OpenLaszlo Incubator. That document explains that the incubator consists of OpenLaszlo components submitted by the community that have not been folded into the main OpenLaszlo framework because of reasons like lack of documentation, insufficient testing, or lack of code review. However, having said that, several of these components (including the tool tips and the validators) are widely used.

In this blog entry, I demonstrate using the formlayout and validators that are provided by the OpenLaszlo Incubator. The LZX source code for this simple example of validation is shown next:

LaszloValidatorDemonstrator.lzx


<canvas title="Very Simple OpenLaszlo Validator Example" debug="true">

<include href="incubator/formlayout.lzx" />
<include href="incubator/validators" />

<simplelayout axis="y" spacing="50" />

<vbox id="mainForm" width="500">
<vbox id="header">
<statictext x="10" fontsize="20" fontstyle="bold"
fgcolor="#000066">Presentation Information</statictext>
</vbox>
<vbox id="validatingFormBox">
<validatingForm id="validatingForm" width="100%">

<formlayout axis="y" align="right" spacing="20" />

<formlabel text="Presenter Name" />
<validateString id="presenterName" name="presenterName" />

<formlabel text="Presenter Organization" />
<validateString id="organization" name="organization" />

<formlabel text="Presentation Title" />
<validateString id="presentation" name="presentation" />

<handler name="onerrorcount" args="errors">
if (errors==0)
{
submitButton.setAttribute("visible", true);
}
else
{
submitButton.setAttribute("visible", false);
}
</handler>
</validatingForm>
</vbox> <!-- validatingFormBox -->

<vbox id="displayBox" visible="false">
<text x="100" id="presenterDisplay" text="Presenter" />
<text x="100" id="organizationDisplay" text="Organization" />
<text x="100" id="titleDisplay" text="Title" />
</vbox>

</vbox> <!-- mainForm -->

<button x="200" id="submitButton" text="Submit" visible="false">
<handler name="onclick">
presenterDisplay.setText("Presenter: " + presenterName.getValueText());
organizationDisplay.setText("Organization: " + organization.getValueText());
titleDisplay.setText("Presentation Title: " + presentation.getValueText());
displayBox.setAttribute("visible", true);
</handler>
</button>

<!-- Define custom validator class that is really just a pre-existing
string validator with certain attributes chosen as defaults. -->
<class name="validateString" extends="stringvalidator" width="200"
required="true" requiredErrorstring="Required Field">
<edittext name="validatableText" width="${classroot.width}" text="" />
</class>

<!-- Define custom form label that is really just a pre-existing
text element with foreground color and other characteristics
defined as defaults. -->
<class name="formlabel" extends="text" fgcolor="#0000FF"
fontstyle="bolditalic" />

</canvas>


The code listing above demonstrates the import statements to access the validator and formlayout components from the incubator. The example also uses several of OpenLaszlo's features like constraints, event handlers, methods, and overridden classes.

In the example above, the formlayout is used directly within the validatingForm. A custom class, validateString, extends the stringvalidator class and is used in the code to validate the text fields expecting Strings. The custom formlabel class extends the text class and provides some application-specific defaults.

The remainder of this blog entry consists of screen snapshots intended to provide a sense of the dynamic behavior of this OpenLaszlo application. I also attempt to demonstrate how the application using the same set of code runs without alteration or browser-specific syntax across the web browsers Firefox 3, Internet Explorer 7, and Google Chrome 0.2. However, with OpenLaszlo, it doesn't end there. I also demonstrate with a couple screen snapshots that this same application will work in DHTML rather than in Flash (the default I am using for the other screen snapshots) without any code changes.


Initial Application Startup / Flash / Firefox




Application In Progress / Flash / Firefox




Application in Progress / DHTML / Firefox




Application Submit Button Appears / Flash / Firefox




Application Completed / Flash / Firefox




Application Completed / DHTML / Firefox




Application Completed / Flash / Internet Explorer




Application Completed / Flash / Google Chrome




In this blog entry, I have demonstrated how to use the formlayout and validators from the OpenLaszlo incubator. I have also demonstrated running the OpenLaszlo application across multiple browsers and with Flash and DHTML.

Monday, September 8, 2008

A Third Look at the JMX Web Services Connector

In previous blog entries, I used the JMX Web Services Connector (JSR-262) with JConsole as the client and with WinRM as the client and, in both entries, accessed the JMX Web Services Connector sample provided with the ws-jmx-connector download. In this third look at using the JMX Web Services Connector, I will use WinRM and JConsole to access attributes and operations of a custom MBean via the JMX Web Services Connector. This will demonstrate how easy it is to apply the JMX Web Services Connector to custom MBeans and will also demonstrate some additional syntax that can be used in WinRM to use the JMX Web Services Connector.

For the examples in this blog entry, I will be using the MBean prescribed by the following interface (CalculatorMXBean) and implementation class (Calculator).


CalculatorMXBean.java

package css2008.dustin.jmx.jmxws;

import css2008.dustin.jmx.core.CalculatorOperationTypeEnum;

/**
* Interface for Calculator MXBean used with JMX Web Services Connector example.
*
* @author Dustin
*/
public interface CalculatorMXBean
{
/**
* Provide the remainder from the last invoked integer division.
*
* @return Remainder from the last invoked integer division.
*/
public int getLastRemainder();

/**
* Describe which operation type was last executed.
*
* @return Type of operation that was last executed.
*/
public CalculatorOperationTypeEnum getLastOperationType();

/**
* Same as getLastOperationType(), but will be seen by JMX client as an
* operation rather than as an attribute getter.
*
* @return Type of operation that was last executed.
*/
public CalculatorOperationTypeEnum provideLastOperation();

/**
* Calculate the sum of the augend and the addend.
*
* @param augend First integer to be added.
* @param addend Second integer to be added.
* @return Sum of augend and addend.
*/
public int add(final int augend, final int addend);

/**
* Calculate the difference between the minuend and subtrahend.
*
* @param minuend Minuend in subtraction operation.
* @param subtrahend Subtrahend in subtraction operation.
* @return Difference of minuend and subtrahend.
*/
public int subtract(final int minuend, final int subtrahend);

/**
* Calculate the product of the two provided factors.
*
* @param factor1 First integer factor.
* @param factor2 Second integer factor.
* @return Product of provided factors.
*/
public int multiply(final int factor1, final int factor2);

/**
* Calculate the quotient of the dividend divided by the divisor.
*
* @param dividend Integer dividend.
* @param divisor Integer divisor.
* @return Quotient of dividend divided by divisor.
*/
public double divide(final int dividend, final int divisor);
}



Calculator.java

package css2008.dustin.jmx.jmxws;

import css2008.dustin.jmx.core.CalculatorOperationTypeEnum;

/**
* Class being used as an illustration of an MXBean.
*
* @author Dustin Marx
*/
public class Calculator implements CalculatorMXBean
{
/** Remainder for the last executed integer division operation. */
private int lastRemainder = 0;

/** Last calculator operation executed. */
private CalculatorOperationTypeEnum lastOperationType;

/**
* Provide the remainder from the last invoked integer division.
*
* @return Remainder from the last invoked integer division.
*/
@Override
public int getLastRemainder()
{
return this.lastRemainder;
}

/**
* Describe which operation type was last executed.
*
* @return Type of operation that was last executed.
*/
@Override
public CalculatorOperationTypeEnum getLastOperationType()
{
return this.lastOperationType;
}

/**
* Same as getLastOperationType(), but will be seen by JMX client as an
* operation rather than as an attribute getter.
*
* @return Type of operation that was last executed.
*/
@Override
public CalculatorOperationTypeEnum provideLastOperation()
{
return this.lastOperationType;
}

/**
* Calculate the sum of the augend and the addend.
*
* @param augend First integer to be added.
* @param addend Second integer to be added.
* @return Sum of augend and addend.
*/
@Override
public int add(final int augend, final int addend)
{
this.lastOperationType = CalculatorOperationTypeEnum.ADDITION;
return augend + addend;
}

/**
* Calculate the difference between the minuend and subtrahend.
*
* @param minuend Minuend in subtraction operation.
* @param subtrahend Subtrahend in subtraction operation.
* @return Difference of minuend and subtrahend.
*/
@Override
public int subtract(final int minuend, final int subtrahend)
{
this.lastOperationType = CalculatorOperationTypeEnum.SUBTRACTION;
return minuend - subtrahend;
}

/**
* Calculate the product of the two provided factors.
*
* @param factor1 First integer factor.
* @param factor2 Second integer factor.
* @return Product of provided factors.
*/
@Override
public int multiply(final int factor1, final int factor2)
{
this.lastOperationType = CalculatorOperationTypeEnum.MULTIPLICATION;
return factor1 * factor2;
}

/**
* Calculate the quotient of the dividend divided by the divisor.
*
* @param dividend Integer dividend.
* @param divisor Integer divisor.
* @return Quotient of dividend divided by divisor.
*/
@Override
public double divide(final int dividend, final int divisor)
{
this.lastOperationType = CalculatorOperationTypeEnum.DIVISON;
this.lastRemainder = dividend % divisor;
return dividend / divisor;
}
}


The MBean described by the interface and implementation class listed above is relatively simple, but it does have attributes and operations that will be used by the clients (JConsole and WinRM).

In the blog entry Remote JMX: Connectors and Adaptors, I demonstrated how the same code base could be used to set up a JMX Web Services Connector (both client and server side) as would be used for any other compliant JMX connector (including the required RMI connector and the optional JMXMP connector). The only thing that differed between each type of connector used was the JMXServiceURL provided. In that blog entry, I included the code for setting up any of these compliant connectors on both the server and client side, but I will provide the relevant server-side of the JMX Web Services Connector here for convenience.


JmxConnectorServer useJmxServiceUrlBasedConnector Method

/**
* Use the platform MBean server in conjunction with the JMX connector
* specified in the provided JMXServiceURL.
*
* @param serviceUrl JMXServiceURL to be used in connector.
* @param connectorMBeanName MBean registration name for the connector.
* @param connectorServers Collection to which my instantiated JMX Connector
* Server should be added.
*/
public static boolean useJmxServiceUrlBasedConnector(
final String serviceUrl,
final String connectorMBeanName,
final List connectorServers)
{
boolean success = true;
final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
printOutputHeader(
"Setting up connector for JMXServiceURL " + serviceUrl,
System.out);
try
{
final JMXServiceURL jmxServiceUrl = new JMXServiceURL(serviceUrl);
final JMXConnectorServer connectorServer =
JMXConnectorServerFactory.newJMXConnectorServer(
jmxServiceUrl,
null,
mbs);
connectorServer.start();
registerProvidedMBean(connectorServer, connectorMBeanName);
connectorServers.add(connectorServer);
}
catch (MalformedURLException badJmxServiceUrl)
{
System.err.print(
"ERROR trying to create JMX server connector with service URL "
+ serviceUrl + ":\n" + badJmxServiceUrl.getMessage() );
success = false;
}
catch (IOException ioEx)
{
System.err.println(
"ERROR trying to access server connector.\n"
+ ioEx.getMessage() );
success = false;
}

if ( success )
{
System.out.println(
connectorMBeanName
+ " registered in MBean server as connector with JMXServiceURL of "
+ serviceUrl + ".");
}
else
{
System.out.println("\n\nERROR encountered.");
}
System.out.println("\n\n");

return success;
}



The next source code listing shown the contents of the ExampleServer class. This class registers our custom MBean (Calculator), sets up the JMX Web Services Connector server, and provides a wait method that allows the application to keep running so that it can be monitored and managed rather than simply finishing execution.

ExampleServer.java

package css2008.dustin.jmx.jmxws;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;

/**
* Simple application that uses WS-JMX Connector Server to make an example MBean
* available to potentially non-JMX or even non-Java clients.
*
* @author Dustin
*/
public class ExampleServer
{
/**
* Register an example MBean that can be monitored and managed via JMX WS
* connector.
*/
public static void registerExampleMBean()
{
final String objectNameStr = "css2008-calculator-jmxws:type=mxbean";
final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try
{
final ObjectName objectName = new ObjectName(objectNameStr);
final Calculator calculator = new Calculator();
mbs.registerMBean(calculator, objectName);
}
catch (MalformedObjectNameException badObjectNameEx)
{
System.err.println( objectNameStr + " is a bad ObjectName:\n"
+ badObjectNameEx.getMessage() );
}
catch (InstanceAlreadyExistsException redundantMBeanEx)
{
System.err.println(
"An MBean is already registered with ObjectName " + objectNameStr
+ ":\n" + redundantMBeanEx.getMessage() );
}
catch (NotCompliantMBeanException nonCompliantMBeanEx)
{
System.err.println(
objectNameStr + " is a non-compliant MBean:\n"
+ nonCompliantMBeanEx.getMessage() );
}
catch (MBeanRegistrationException regEx)
{
System.err.println(
"ERROR trying to register MBean with name " + objectNameStr + ":\n"
+ regEx.getMessage() );
}
catch (MBeanException mbeanEx)
{
System.err.println("An MBean exception occurred: \n"
+ mbeanEx.getMessage() );
}
}

/**
* Set up the JMX WS Connector Server.
*/
public static void setUpJmxWsConnectorServer()
{
List connectorServers =
new ArrayList();
JmxConnectorServer.useJmxServiceUrlBasedConnector(
"service:jmx:ws://localhost:1097/jmxws",
"connector:type=jsr262_jmxws",
connectorServers);
}

/**
* Hold application so that it can be monitored and managed.
*/
public static void waitForInput()
{
try
{
System.in.read();
}
catch (IOException ioEx)
{
System.err.println("Error trying to read input.\n" + ioEx.getMessage() );
}
}

/**
* Run JMX WS connector with registered MBean for JMX WS clients to interact
* with.
*
* @param arguments
*/
public static void main(final String[] arguments)
{
registerExampleMBean();
setUpJmxWsConnectorServer();
waitForInput();
}
}


To run the ExampleServer with its JMX Web Services Connector server, I used the following command (note that the JSR-262-provided libraries are included on the classpath and that I take advantage of the Java SE 6 support for wildcard JAR specification with the asterisk):


java -cp C:\css2008\jmx\MBeanExamples\dist\MBeanExamples.jar;C:\jmx-ws-262\jsr262-ri\lib\* css2008.dustin.jmx.jmxws.ExampleServer


When the above command is run, the application prints out its JMXServiceURL (service:jmx:ws://localhost:1097/jmxws) to make connection for clients easier. Here is how it appears in a Command Prompt console:



All of the above set up the custom MBean, registered it with the platform MBean server, and associated a JMX Web Services Connector with that MBean server. With that in mind, it is time now to look at how this MBean server and the Calculator MBean can be accessed remotely with JConsole and WinRM.

As discussed in my first blog entry on the JMX Web Services Connector, it is necessary to provide certain additional libraries on the classpath for JConsole to use to connect using JMX Web Services Connector. The next listing shows the JSR-262 RI libraries on JConsole's classpath. Note that I am once again taking advantage of the ability in Java SE 6 to specify that all JARs within a particular directory are on the classpath using the asterisk. This is much easier than spelling out all the necessary JAR files in that directory. It is also important to note that I have the environment variable JAVA_HOME set and am using that here.


java -cp "C:\jmx-ws-262\jsr262-ri\lib\*;%JAVA_HOME%\lib\jconsole.jar;%JAVA_HOME%\lib\tools.jar" sun.tools.jconsole.JConsole


The next screen snapshot shows JConsole's entry screen and shows the JMXServiceURL provided above specified in the Remote tab.



Once I have connected remotely via the JMX Web Services Connector, I can use JConsole's MBeans tab to interact with the remote JMX Web Services Connector server. The next two screen snapshots show access of an operation (add) and of an attribute (last operation type).






It is one thing to connect to the JMX Web Services connector server with JConsole, but it can be even more impressive to see a non-Java, non-JMX client communicate with our MXBean. In the remainder of this blog entry, I will use WinRM to connect remotely to the MBean server via the JMX Web Services Connector and will manage and monitor the Calculator MXBean using WinRM.

For my examples, I will be using WinRM's scripting language directly to interact with the JMX MBean server through the JMX Web Services Connector. In the first example, I will invoke the "add" operation. This is done in WinRM with the following command:


winrm invoke Invoke http://jsr262.dev.java.net/DynamicMBeanResource?ObjectName=css2008-calculator-jmxws:type=mxbean -file:C:\css2008\jmx\jmxwsinput\AddOperation.xml -r:http://127.0.0.1:1097/jmxws -a:None


I explained several of the pieces of the above WinRM command in my Second Look at JMX Web Services Connector blog entry. The important new things to focus on in this particular command are the use of Invoke and the specification of a file with the actual description of what should be invoked. In this case, the XML file (called AddOperation.xml), looks like this (note that it will be adding 13 and 4):

AddOperation.xml

<ManagedResourceOperation xmlns="http://jsr262.dev.java.net/jmxconnector"
name="add">
<Input>
<Param name="augend"><Int>13</Int></Param>
<Param name="addend"><Int>4</Int></Param>
</Input>
</ManagedResourceOperation>


The XML above is of a format prescribed by the XML Schema file jsr262.xsd that currently gets shipped with the ws-jmx-connector implementation of JSR-262 (subject to change as the JSR progresses). Note that WinRM reports an obvious error message if the provided XML is invalid ("The WS-Management service cannot process the request because the XML is invalid."). However, when the XML is valid, but does not match what is expected, the less descriptive and less helpful error message "The service cannot comply with the request due to internal processing errors." Another message that indicates things are not well is "The WS-Management service does not support format mismatch." and that is again a difficult one to track down exactly the cause of the problem. These error messages indicate problems with the XML, but it is not always intuitive what the problem really is. WinRM allows you to request more details about a particular error code with the syntax "winrm helpmsg <<errorCode>>", but I found that this provided no additional detail above what the error message itself provided.

In a Windows Command Prompt terminal, running this command with a valid XML file looks like this:



The addition operation shown above demonstrates using WinRM to invoke a JMX operation via the JMX Web Services Connector. Another example of calling an operation is shown next with the divide operation on Calculator. The WinRM command and XML file with the parameters for the operation are shown next.


winrm invoke Invoke http://jsr262.dev.java.net/DynamicMBeanResource?ObjectName=css2008-calculator-jmxws:type=mxbean -file:C:\css2008\jmx\jmxwsinput\DivideOperation.xml -r:http://127.0.0.1:1097/jmxws -a:


DivideOperation.xml

<ManagedResourceOperation xmlns="http://jsr262.dev.java.net/jmxconnector"
name="divide">
<Input>
<Param name="dividend"><Int>12</Int></Param>
<Param name="divisor"><Int>4</Int></Param>
</Input>
</ManagedResourceOperation>


The screen snapshot for invoking the division operation from WinRM is shown next:



We can also access attributes in the MXBean from WinRM. One way to do this is to expose the attribute via an operation and an example of this is requesting the value of the last operation, which should be division, using the provideLastOperation() method of the Calculator class. This is done as shown in the following WinRM command and associated XML file with the actual invocation details.


winrm invoke Invoke http://jsr262.dev.java.net/DynamicMBeanResource?ObjectName=css2008-calculator-jmxws:type=mxbean -file:C:\css2008\jmx\jmxwsinput\LastOperationTypeOperation.xml -r:http://127.0.0.1:1097/jmxws -a:None


LastOperationTypeOperation.xml

<jmx:ManagedResourceOperation name="provideLastOperation"
xmlns:jmx="http://jsr262.dev.java.net/jmxconnector">
<jmx:Input/>
</jmx:ManagedResourceOperation>


The WinRM command to invoke this operation to get this attribute and the results of that command are shown in the next screen snapshot:



While we can access attributes using operations written specifically for that purpose, it is more common to simply use the "get" method associated with an attribute. The next example will demonstrate this.

When acquiring an attribute's value directly with its "getter" method rather than as a non-get operation, the WinRM command is a little different than what we've seen so far for invoking operations. That command and the output from running that command are shown next (note there is no XML file this time).


winrm get http://jsr262.dev.java.net/DynamicMBeanResource?ObjectName=css2008-calculator-jmxws:type=mxbean -fragment://:Property[@name="LastOperationType"]/*/text() -r:http://127.0.0.1:1097/jmxws -a:None




Acquiring an attribute uses WinRM's "get" rather than operations' "invoke" and doesn't require the extra specification of input parameter data we provided on the operations examples.

In this blog entry, a custom MBean (MXBean in this case) was made available to remote clients via the JMX Web Services Connector. Both the Java-knowledgeable/JMX-knowledgeable JConsole and the Java-agnostic/JMX-agnostic WinRM clients were used to show the flexibility and openness provided by the JMX Web Services Connector. While the JMX Web Services Connector is still a work-in-progress, it looks likely that it will be available with Java SE 7. I also suspect that other WS-Management aware tools (which is how WinRM "knows" how to talk the JMX Web Services Connector) will become more popular in the future.




UPDATE (15 September 2008): Based on feedback (below) and to provide completeness, I have added the code listing for the enum CalculatorOperationTypeEnum:

CalculatorOperationTypeEnum.java

package css2008.dustin.jmx.core;

/**
* Simple enum to use with different types of JMX MBeans to demonstrate their
* support or lack of support for custom classes/enums.
*
* @author Dustin
*/
public enum CalculatorOperationTypeEnum
{
ADDITION,
DIVISION,
MULTIPLICATION,
SUBTRACTION
}