Thứ Hai, 11 tháng 8, 2008

NetBeans Code Folding and the Case for Code Folding

In this blog entry, I will briefly cover how to use code folding in NetBeans 6.1 while attempting to make a case for code folding in the correct situations.

I often agree with Jeff Atwood and what he writes in his Coding Horror blog, but I thought I was going to disagree strongly with him when I saw one of his posts titled The Problem with Code Folding. After reading it, it turns out that we don't disagree as much as we each have a different focus on code folding. He warns against using it for many things related to actual executable code (which I agree with), but I have found code folding to be highly useful in cases on non-executable code that I don't need to be burdened with when reviewing and maintaining code.

I agree that we all need to give and take a little in our software development teams. However, there are situations when the team is split roughly 50/50 on what is the best approach. Rather than irritate half the team every time they look at the code just because one approach is arbitrarily chosen, code folding can often help everyone be happier. In this blog entry, I will attempt to outline some of these cases and use NetBeans 6.1 code folding to illustrate. I'm going to try to not state which side of these issues I prefer and instead focus on how code folding allows both sides to be happier. This does not mean I don't have a preference.




One area of disagreement that can be easily handled with code folding is the specification of Java classes and interfaces to be imported. One school of thought holds that all imported classes and interfaces should be individually and explicitly imported while the other school of thought maintains that the entire package should be imported using the asterisk. The Java Tutorials section Using Package Members suggests that explicit imports are okay if only a small number of classes and/or interfaces in a given package are needed, but that the asterisk should be used to include the entire package if several members of that package are needed.

NetBeans provides automatic code folding for import statements. The advantage of automatic IDE-provided code folding is that the code is not touched in anyway and the code has nothing in it specific to code folding. The following three screen snapshots (taken from my recent EasyMBean blog example) demonstrate NetBeans 6.1's automatic import statement code folding in action. The first image shows an example piece of code with the import statements expanded as one would normally see them in any text editor. The second image shows these same import statements collapsed (by clicking on the minus sign to the left of the first import) and the third image shows them still collapsed, but shows the preview that is shown if the mouse cursor is moved over the collapsed marking (. . . ).

Import Statements Shown Normally (or Expanded)



Import Statements Collapsed



Import Statements Collapsed with Preview of Collapsed Content




The above code folding of import statements is obtained in NetBeans without any additional effort from the developer and can allow the team to have explicit and individually specified classes and interfaces without irritating those who see those as extraneous or cumbersome for their normal daily needs. Because it is automatic in NetBeans, there is no trace of any special treatment of important statements in the source code. Anyone looking at the source code in an alternate editor that doesn't itself fold import statements will just see the lines of code like shown next:


package dustin.jmx.modelmbeans;

import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.jmanage.easymbean.EasyMBean;





Another area of controversy among experienced and highly skilled software developers is the appropriate level of commenting. In truth, this is almost as much art (and personal opinion) as science, but I think most of us can agree that the more readable the code is, the less commenting is often needed. As is often cited, comments are sometimes used as the deodorant for smelly code. Note also that there are different types of comments. For example, Javadoc comments are often very different animals from inline comments within methods.

NetBeans offers automatic code folding for Javadoc comments similar to what it provides for import statements. I will again use the example code from my EasyMBean blog example to illustrate NetBeans's code folding of Javadoc comments. The next two screen snapshots show a class-level Javadoc comment and a method-level Javadoc comment expanded (normal/default) in the first image and collapsed in the second image. Note that control of the Javadoc comments is separate; I just happened to have both open for one snapshot and both closed for the other.

Javadoc Comments Expanded



Javadoc Comments Collapsed



I did not show a preview screen snapshot with collapsed Javadoc comments. This works just as I showed it working for import statements. Without expanding the collapsed Javadoc comments, I can move my mouse cursor over the collapsed Javadoc comments section in NetBeans and see a preview of what is being collapsed.

You may have noticed that even in the screen snapshot in which the Javadoc comments were closed, there was a minus sign icon for collapsing (code folding) the method itself. Use of code folding to hide a method as can be done with that icon is definitely not something I'd choose to do often. This is where I agree with many of Jeff Atwood's assertions in The Problem with Code Folding. I don't mind it as provided by NetBeans automatically because it doesn't change the code at all (NetBeans implements these code folding sections without any special demarcation in the code) and the code is expanded by default. I might occasionally find it helpful to collapse a method to better focus on what's going on, but I generally favor having executable code viewable by default. Because of this opinion, I won't be demonstrating method collapsing. However, it works similarly to the collapsing already shown of import statement sections and Javadoc comment sections.




So far, the NetBeans code folding examples have demonstrated the type of code folding that I prefer and that is my first choice when available and sufficient. This automatic and implicit code folding does not require any effort by the developer and do not in any way adulterate the source code.

The next example of NetBeans code folding that I will demonstrate is the NetBeans wizard-generated servlet code folding. This code folding is automatically added by NetBeans, but the servlet code folding differs from the previous code folding examples because it does rely on explicit code folding comments in the source code. The next screen snapshot shows what the servlet looks like immediately after being generated with NetBeans's servlet wizard.



If the developer clicks on the + icon to expand the collapsed servlet code, he or she sees something like this:


// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
/**
* Handles the HTTP <code>GET</code> method.
* @param request servlet request
* @param response servlet response
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

/**
* Handles the HTTP <code>POST</code> method.
* @param request servlet request
* @param response servlet response
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

/**
* Returns a short description of the servlet.
*/
public String getServletInfo() {
return "Short description";
}// </editor-fold>


The code that was collapsed by default after the servlet wizard was run are the three standard methods expected of servlets. The doGet and doPost methods are not all that interesting because all they do is turn around and call the processRequest method. The collapsed getServletInfo method is slightly more interesting, but would also not be too interesting once a useful string was set to be returned from the method.

The servlet wizard example provides a taste of another class of NetBeans code folding where NetBeans automatically generates the code folding, but it requires explicit comments to be placed in the code to perform the code folding. These commented-out lines will be ignored by other editors, but will be respected by NetBeans. Another example of this was present in my blog entry on Using Orson Charts to Extend JConsole, in which I used the NetBeans GUI editor to create code and that code was placed (by NetBeans) within code folding comments.




All of the previous code folding examples were automatically provided by NetBeans where code had to be changed or not to perform the code folding. The third class of NetBeans code folding is customized code folding where the developer marks sections of code that he or she wants to support NetBeans code folding. The last example of the servlet generation showed the syntax that developers can use to implement custom folding. As shown in the servlet example and as described in Custom Code Folding in NetBeans, one can add custom code folding in NetBeans simply be enclosing the code to be folded within // <editor-fold> and // </editor-fold> comments.

Custom code folding allows the developer to be more selective in which types of code are folder. This can be a nice advantage, but can also be a problem if used indiscriminately. The situation in which I think that custom code folding is most desirable is when things not directly related to the successful execution of the code is included within the code. The primary way this can happen is via comments and via annotations.

There are times when developers may choose to or be forced to include comments and/or annotations in their code that are not required to successfully execute the code. Examples of such situations include the decision to include requirements in the code, to include contact or other personnel information in the code, or to include architectural or other high-level software characteristics in the code. Often, a client or management wants this additional non-executing code included for various reasons that may not be at all related to the successful execution of the code itself. Code folding can be used to keep such metadata in the code, but remove the non-executing metadata from the developer's view when using NetBeans. In other words, code folding allows this non-executing code to be removed as a distraction without actually removing it from the code.

In the previous screen snapshot, I showed Javadoc commented out. However, there were regular (not Javadoc) comments within the method processRequest that were still visible. These are actually working servlet code lines generated by NetBeans servlet generation wizard, but they are commented out. It is easy to fold or collapse these lines by adding the comments before and after that indicate code folding as shown in the next code listing.


try {
//<editor-fold defaultstate="collapsed" desc="Generated code">
/* TODO output your page here
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet FakeServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Servlet FakeServlet at " + request.getContextPath () + "</h1>");
out.println("</body>");
out.println("</html>");
*/
//</editor-fold>
} finally {
out.close();
}


The code now looks like this in NetBeans:



This shows that any types of comments could be easily folded out of the current NetBeans view. This might be useful for verbose comments providing background, history, requirement, or details that are often not desirable when editing code. The one major issue with this, however, is the increased tendency for comments to become obsolete or even outright wrong compared to the actual code. This is because it is difficult enough remembering to keep comments current with code changes even when they're right there in front of you and hiding them only makes that maintenance that much more difficult. This is one of the arguments for "less commenting is better," but that is not always a choice.

Annotations are sometimes used where they arguably should not be used, including being used as "processable comments" (not much more than comments except that a third-party piece of software can process them easily and mine them for comment information that would be more difficult to parse out of actual comments). In such cases, the annotations might have nothing to do with the execution of the code and so most developers will benefit from folding these out of their normal NetBeans view. For example if some custom annotations were used to associate methods with certain business requirements, the developer might enclose the requirements annotations within commented lines //<editor-fold defaultstate="collapsed" desc="Requirements Annotations"> and </editor-fold>.

Code folding can lead to problems when applied indiscriminately. However, there are many advantages to be gained from carefully chosen, effective code folding. Code folding has the least downside when it is automatically generated on-the-fly by the IDE without any impact to the code. Code folding is arguably most dangerous when it is custom code folding, but this can also sometimes provides its greatest value when used carefully.

Developers are not always fortunate enough to work with code as clean as they'd like. Code folding can be used to hide away some of the most distracting non-executing code to make the executable code a little easier to read and maintain.

Thứ Năm, 7 tháng 8, 2008

JMX Model MBeans with EasyMBean

I wrote previous blog entries on the advantages of using Model MBeans and showed examples of using Model MBeans directly, using Model MBeans with the Spring Framework, and using Model MBeans with Apache Commons Modeler. In this blog entry, I will look at using Model MBeans with EasyMBean.

A JMX developer might choose to use EasyMBeans for his or her Model MBean needs if he or she was not using the Spring Framework anywhere else in a given Java application, but wanted Spring-like JMX ModelMBean annotations. Other choices include direct Model MBean coding and using Apache Commons Modeler, but EasyMBean is most like Spring in terms of JMX Model MBean exposure.

EasyMBean has a close relationship with JManage. Not only is EasyMBean discussed heavily at the jManage site, but "jmanage" appears in the package names for EasyMBeans classes as shown in this blog entry's code examples. However, it is important to note that jManage is not required and that a standalone version of EasyMBean.jar can be downloaded from the EasyMBean project site.

For my example of using EasyMBean in this blog entry, I have adapted the class SimpleCalculatorAnnotated that I demonstrated with Spring and Model MBeans in my previous blog entry. The name of this adapted class is SimpleCalculatorAnnotatedEasyMBean and its code lsiting is shown next:

SimpleCalculatorAnnotatedEasyMBean.java

package dustin.jmx.modelmbeans;

import org.jmanage.easymbean.annotations.ManagedConstructor;
import org.jmanage.easymbean.annotations.ManagedOperation;
import org.jmanage.easymbean.annotations.ManagedResource;


/**
* Simple calculator class intended to demonstrate how a class with no knowledge
* of JMX or management can be annotated to be exposed as Model MBean by EasyMBean.
*
* @author Dustin
*/
@ManagedResource(
name="modelmbean:type=spring-annotated",
description="A simple integer calculator." )
public class SimpleCalculatorAnnotatedEasyMBean implements SimpleCalculatorIf
{
@ManagedConstructor(
name="SimpleCalculatorAnnotatedEasyMBean",
description="No argument constructor")
public SimpleCalculatorAnnotatedEasyMBean()
{
}

/**
* 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.
*/
@ManagedOperation(
name="add",
displayName="Integer Addition",
presentationString="Integer Addition",
targetClass="SimpleCalculatorAnnotatedEasyMBean",
description="Integer Addition")
public int add(final int augend, final int addend)
{
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.
*/
@ManagedOperation(
name="subtract",
displayName="Integer Subtraction",
presentationString="Integer Subtraction",
targetClass="SimpleCalculatorAnnotatedEasyMBean",
description="Integer Subtraction")
public int subtract(final int minuend, final int subtrahend)
{
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.
*/
@ManagedOperation(
name="multiply",
displayName="Integer Multiplication",
presentationString="Integer Multiplication",
targetClass="SimpleCalculatorAnnotatedEasyMBean",
description="Integer Multiplication")
public int multiply(final int factor1, final int factor2)
{
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.
*/
@ManagedOperation(
name="divide",
displayName="Integer Division",
presentationString="Integer Division",
targetClass="SimpleCalculatorAnnotatedEasyMBean",
description="Integer Division")
public double divide(final int dividend, final int divisor)
{
return dividend / divisor;
}
}


The code listing for SimpleCalculatorAnnotatedEasyMBean shares many similarities with the Spring version, but there are significant differences. The most obvious and expected difference is the different set of import statements. However, there are deeper differences as well. In this case, the focus is on describing Model MBean operations (methods) and EasyMBean does not allow the parameters to the operations to be explicitly described like Spring does. While this does make it simpler for the developer (as stated in EasyMBean documentation), it is also a limitation because the parameters show up in JConsole and other generic JMX consoles as something like "p1" and "p2" rather than with their descriptive names.

One other thing worthy of special note here is the use of an EasyMBean-annotated no-argument constructor in the bean class. I found this necessary to allow my sample application to run without a NullPointerException.

The next code listing, for class EasyMBeanDemonstrator, shows the code to get access to the platform MBean server and then to use the EasyMBean static call to EasyMBean.getMBean(Object,ObjectName,MBeanServer) method.

EasyMBeanDemonstrator.java

package dustin.jmx.modelmbeans;

import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.jmanage.easymbean.EasyMBean;

/**
* This class demonstrates use of EasyMBean to annotate classes for exposure
* as Model MBeans.
*
* For more information on EasyMBeans, see the JManage documentation at
* http://www.jmanage.org/wiki/index.php/EasyMBean along with the main project
* page at http://sourceforge.net/projects/easymbean/.
*
* @author Dustin
*/
public class EasyMBeanDemonstrator
{
/**
* Register the ModelMBean in the MBeanServer using the provided
* ObjectName String.
*
* @param objectNameString ObjectName of the registered ModelMBean.
*/
private void registerModelMBean(
final String objectNameString)
{
final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
SimpleCalculatorAnnotatedEasyMBean mbean =
new SimpleCalculatorAnnotatedEasyMBean();
try
{
final ObjectName objectName =
new ObjectName(objectNameString);
final ObjectInstance objInstance =
EasyMBean.getMBean(mbean, objectName, mbs);
/*
this.mbeanServer.registerMBean( modelMBean,
objectName );*/
}
catch (MalformedObjectNameException badObjectName)
{
System.err.println( "ERROR trying to generate ObjectName based on "
+ objectNameString + ":\n"
+ badObjectName.getMessage() );
}
}

/**
* Create a ModelMBean in the raw and register it with the MBeanServer.
*/
public void createAndRegisterEasyMBean()
{
final String easyModelMBeanObjectNameString = "modelmbean:type=easyMBean";
registerModelMBean(easyModelMBeanObjectNameString);
}

/**
* Pause for the specified number of milliseconds.
*
* @param millisecondsToPause Milliseconds to pause execution.
*/
public static void pause(final int millisecondsToPause)
{
try
{
Thread.sleep(millisecondsToPause);
}
catch (InterruptedException threadAwakened)
{
System.err.println("Don't wake me up!\n" + threadAwakened.getMessage());
}
}

/**
* Demonstrate use of ModelMBean via EasyMBean..
*
* @param args the command line arguments
*/
public static void main(String[] args)
{
final EasyMBeanDemonstrator me = new EasyMBeanDemonstrator();
me.createAndRegisterEasyMBean();
pause(1000000);
}
}


With the above code in place, I will now show two screen snapshots that show the results of this simple work in JConsole. There is more detail in JConsole than one would see in a non-annotated Spring Model MBean, but less than one would see when Spring's annotation-based support is used. The most noticeable feature missing is the lack of names for the parameters to the operations.





I've attempted to demonstrate the simplicity of EasyMBean in employing Model MBeans in JMX development. When I am using Spring in my Java-based applications, I'd almost certainly stick with Spring's support, but EasyMBean is worth consideration when not otherwise using Spring.

Perhaps the most approachable resource available online that describes how to use EasyMBean is provided as part of the jManage site. The EasyMBean Wiki entry at this site provides a "Quick Start for the impatient" section and a more descriptive (but still pretty brief) "Understanding EasyMBean Annotations for the Cool Headed" section. The first section demonstrates code examples of using EasyMBean similar to those shown here and the second section explains each major EasyMBean annotation and its attributes in detail. My blog entry focused on operations, but this resource shows how EasyMBean annotations can also be used with JMX Notifications and attributes.

Finally, JMX 2.0 appears likely to provide JMX developers with the standard support for JMX annotations, which will likely be the favorite choice of most JMX developers. For more details on the forthcoming JMX 2.0 support for JMX annotations, see Eamonn McManus's blog entries Playing with the JMX 2.0 API and Defining MBeans with Annotations.

Thứ Tư, 6 tháng 8, 2008

Free "Getting Started with Flex 3" in PDF

You can download a free copy of the PDF version of the book Getting Started with Flex 3 by taking either of two quizzes available at SitePoint. Details regarding the free PDF are available here. The gist of it is that you can download the PDF without registration by simply taking a very short quiz at the end of either of two Flex-related articles by Jack Herrington. I believe that this promotion runs through 22 September 2008.

The first article is called Build a Web 2.0 Voting Widget with Flex, Part 1 and the second article is called Building a Web 2.0 Voting Widget with Flex, Part 2. Both articles provide nice background and information on using Flex and the quizzes are short and painless and help to ascertain your knowledge of Flex.

While at the SitePoint site, other Flex/AIR-related resources you may want to check out include An Absolute Beginner's Tutorial of Flex 3, Walk on AIR: Create a To-do List in Five Minutes, and Create a 3D Product Viewer in Flex 3.

Thứ Hai, 4 tháng 8, 2008

Using Orson Chart Beans with JConsole Custom Plugin

In this blog entry, I will be looking at using Orson Chart Beans to generate charts in a custom tab added to JConsole. Orson Charts (also sometimes called JFreeBeans in some of the documentation) are backed by JFreeChart and present an easier approach to including JFreeChart-generated charts in Swing applications than using JFreeChart directly.

I could have generated the charts shown in this blog entry's custom JConsole tab using JFreeChart directly (see Example 3/Listing 4 in the article Visualize Your Oracle Database Data with JFreeChart article for an example of using JFreeChart directly in Swing). However, I am using Orson Charts so that I can demonstrate their use and so that more emphasis can stay on implementing the JConsole custom tab.

Java SE 6 provides new features (service provider lookup and SwingWorker) that enable custom tabs in JConsole. The Sun Java SE 6 JDK distribution includes an example custom tab implementation called JTop that demonstrates how to take advantage of the service provider lookup and SwingWorker to create custom tabs for JConsole.

For my example, I'm not dynamically changing the data (for simplicity) and so I don't need to implement as many methods as does the JTop example. Instead, I am building the Orson charts with static, hard-coded data to simplify the examples. However, in a real implementation, one would likely use dynamic data gathered from various sources to build the charts. Data might be retrieved related to databases, networks, file systems, etc., to complement the JVM data already made available in JConsole.

The next class, OrsonBasedJConsolePlugin, shows the simple code needed to create a JConsole Plugin. Most of the interesting functionality is actually in a class this class references.

OrsonBasedJConsolePlugin

package dustin.jconsole.orson;

import com.sun.tools.jconsole.JConsolePlugin;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.swing.JPanel;
import javax.swing.SwingWorker;

/**
* This is a simplistic example of extending JConsole with a custom plugin
* that takes advantage of the Orson (JFreeBeans).
*/
public class OrsonBasedJConsolePlugin
extends JConsolePlugin implements PropertyChangeListener
{
private OrsonPanel orsonPanel = null;
private Map<String, JPanel> customTabs = null;

public OrsonBasedJConsolePlugin()
{
addContextPropertyChangeListener(this);
}

@Override
public Map<String, JPanel> getTabs()
{
if ( customTabs == null )
{
// Using LinkedHashMap because it is a Map implementation that
// maintains its predictable order (usually the order of insertion
// into the LinkedHashMap. Using the LinkedHashMap ensures that the
// new custom tabs appear in the desired order in JConsole.
customTabs = new LinkedHashMap();
orsonPanel = new OrsonPanel();
}

customTabs.put("Orson-Based Chart", new OrsonPanel());
return customTabs;
}

@Override
public SwingWorker<?, ?> newSwingWorker()
{
return orsonPanel.newSwingWorker();
}

@Override
public void propertyChange(PropertyChangeEvent evt)
{
// Implement behavior upon property change.
}
}


The next class is the OrsonPanel class that actually does most of the work related to Orson Charts generation. This panel was created in NetBeans 6.1 GUI builder and some of the "fold" comments indicate this. See the Java SE 6-provided JTop demonstration for a more complete demonstration of implementation of a SwingWorker. The image before the code listing shows how it appears in NetBeans. Note that the generic images for Orson bar chart and Orson pie chart help see what will go where on the final HMI.



OrsonPanel

package dustin.jconsole.orson;

//import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;

/**
* Simple JPanel using a chart generated JFreeChart-based Orson library.
*/
public class OrsonPanel extends javax.swing.JPanel
{
/** Creates new form OrsonPanel */
public OrsonPanel()
{
initComponents();
populatePieChart();
populateUsersMachinesBarChart();
}

/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {

orsonUsersMachineBarChartPanel = new javax.swing.JLabel();
orsonBarChart = new org.jfree.beans.JBarChart();
orsonPieChart = new org.jfree.beans.JPieChart();

orsonUsersMachineBarChartPanel.setText("Displaying Charts in JConsole with JFreeChart-based Orson is Easy!");

javax.swing.GroupLayout orsonBarChartLayout = new javax.swing.GroupLayout(orsonBarChart);
orsonBarChart.setLayout(orsonBarChartLayout);
orsonBarChartLayout.setHorizontalGroup(
orsonBarChartLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 360, Short.MAX_VALUE)
);
orsonBarChartLayout.setVerticalGroup(
orsonBarChartLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 230, Short.MAX_VALUE)
);

javax.swing.GroupLayout orsonPieChartLayout = new javax.swing.GroupLayout(orsonPieChart);
orsonPieChart.setLayout(orsonPieChartLayout);
orsonPieChartLayout.setHorizontalGroup(
orsonPieChartLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 360, Short.MAX_VALUE)
);
orsonPieChartLayout.setVerticalGroup(
orsonPieChartLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 230, Short.MAX_VALUE)
);

javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(orsonBarChart, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(orsonPieChart, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(orsonUsersMachineBarChartPanel)
.addGap(197, 197, 197))))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(orsonUsersMachineBarChartPanel)
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(orsonBarChart, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(orsonPieChart, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(27, Short.MAX_VALUE))
);
}// </editor-fold>


/**
* Populate Orson/JFreeChart-based pie chart.
*/
private void populatePieChart()
{
final DefaultPieDataset data = new DefaultPieDataset();
data.setValue("Opened", 23);
data.setValue("Closed", 10);
this.orsonPieChart.setDataset(data);
this.orsonPieChart.setTitle("Database Connections");
this.orsonPieChart.setSubtitle("Opened and Closed Database Connections");
}

/**
* Populate Orson/JFreeChart-based bar chart that displays numbers of types
* of users on each machine.
*/
private void populateUsersMachinesBarChart()
{
final DefaultCategoryDataset data = new DefaultCategoryDataset();
final String PRIVILEGED_USERS = "Privileged Users";
final String REGULAR_USERS = "Regular Users";
data.setValue(7, PRIVILEGED_USERS, "host1" );
data.setValue(5, PRIVILEGED_USERS, "host2" );
data.setValue(50, REGULAR_USERS, "host1" );
data.setValue(38, REGULAR_USERS, "host2");
this.orsonBarChart.setDataset(data);
this.orsonBarChart.setTitle("Types of Users Using Machines");
this.orsonBarChart.setSubtitle("Privileged and Regular Users");
this.orsonBarChart.setCategoryAxisLabel("Machine Hosts");
this.orsonBarChart.setValueAxisLabel("Number of Users");
}


/** SwingWorker used exclusively for JConsolePlugin. */
class OrsonWorker extends SwingWorker
{
OrsonWorker() {}

public CategoryDataset doInBackground()
{
final DefaultCategoryDataset data = new DefaultCategoryDataset();
return data;
}

@Override
protected void done()
{
}
}

// Return a new SwingWorker for UI update
public SwingWorker<?,?> newSwingWorker()
{
return new OrsonWorker();
}


// Variables declaration - do not modify
private org.jfree.beans.JBarChart orsonBarChart;
private org.jfree.beans.JPieChart orsonPieChart;
private javax.swing.JLabel orsonUsersMachineBarChartPanel;
// End of variables declaration
}


To use the service provider mechanism to allow JConsole to detect the custom tab implemented above, a special file must be included in the JAR with the two above classes. This special file must have the name >com.sun.tools.jconsole.JConsolePlugin and must contain the fully qualified name of the plugin class. In other words, for our example with only a single plugin, it should contain the following line:

com.sun.tools.jconsole.JConsolePlugin

dustin.jconsole.orson.OrsonBasedJConsolePlugin


A single JAR with the compiled versions of the above classes and the just-mentioned file should be built. This will be provided to JConsole when it is run. The JAR contents look like this in my example:

Contents of JConsoleWithOrson.jar

0 Sun Aug 03 21:39:48 MDT 2008 META-INF/
162 Sun Aug 03 21:39:46 MDT 2008 META-INF/MANIFEST.MF
0 Sun Aug 03 21:17:56 MDT 2008 META-INF/services/
0 Sun Aug 03 21:17:14 MDT 2008 dustin/
0 Sun Aug 03 21:17:14 MDT 2008 dustin/jconsole/
0 Sun Aug 03 21:17:14 MDT 2008 dustin/jconsole/orson/
46 Sun Aug 03 21:17:56 MDT 2008 META-INF/services/com.sun.tools.jconsole.JConsolePlugin
1458 Sun Aug 03 21:17:14 MDT 2008 dustin/jconsole/orson/OrsonBasedJConsolePlugin.class
1095 Sun Aug 03 21:39:48 MDT 2008 dustin/jconsole/orson/OrsonPanel$OrsonWorker.class
4863 Sun Aug 03 21:39:48 MDT 2008 dustin/jconsole/orson/OrsonPanel.class


While JConsole can normally be run by simply typing "jconsole" at the command-line, we now need to provide this JAR file, JConsoleWithOrson.jar, to the "jconsole" command. In addition, we also need to include Orson and JFreeChart libraries used to JConsole as well to avoid exceptions related to not finding these classes. The run command to run JConsole with this custom plugin JAR and associated Orson and JFreeChart libraries is:


jconsole -pluginpath JConsoleWithOrson.jar -J-Djava.class.path="C:\Program Files\Java\jdk1.6.0_07\lib\jconsole.jar";"C:\Program Files\Java\jdk1.6.0_07\lib\tools.jar";C:\orson-0.5.0\orson-0.5.0.jar;C:\orson-0.5.0\lib\jfreechart-1.0.6.jar;C:\orson-0.5.0\lib\jcommon-1.0.10.jar


You can see from the command for running JConsole above that I am using the latest version of Orson Charts (0.5). I am also using the version of JFreeChart and JCommon that come with the Orson distribution. There are newer versions of JFreeChart (1.0.10) available, but I wanted to use the version that came with the Orson download. Note also that if you did use JDBC database connections in a real situation, you'd need JDBC drivers and other database accessing classes as well.

When the JAR is compiled as described above and run as shown above, a new tab appears on JConsole as shown here:



Even with all the detail I tried to include in this blog entry, I hope that it is evident how easy it is to use Orson to add charts to your custom JConsole plug-in tabs.

Thứ Năm, 31 tháng 7, 2008

Flex/Flash Critcisms Addressed

In this blog entry, I intend to outline some of the most common criticisms and concerns regarding Flex that I hear or read from other developers. Because Flex necessarily compiles to Flash, any Flash criticisms or disadvantages are also Flex criticisms or disadvantages.

I will first list some of the most common criticisms and disadvantages correctly or incorrectly associated with Flex (and Flash) and then provide brief detail on things that address these issues. I welcome feedback in the comments section on any criticisms or disadvantages that I have missed and any events or methods that reduce the disadvantage or criticism.

Commonly Cited Flex/Flash Criticisms and Disadvantages
1. License Costs
2. Not Open Source
3. Perception: For Movies and Animation Only
4. Application Load Time
5. Development/Productivity Tools
6. Flash Player Dependence
7. No 64-bit Flash Player
8. No URLs, Histories, or Bookmarks
9. Searchability / Search Engine Optimization
10. Adobe Commitment to Flex
11. Flash Player is Single Threaded
12. Flex is Not a Standard

1. License Costs

For a long-time, one of the most significant criticisms of Flex that prevented developers from adopting it was its licensing costs. In fact, in his now well-known article Hybridizing Java, Bruce Eckel (a well-known Flex evangelist featured in Adobe advertisements for a while) even states that Flex's former licensing costs affected how much he used and wrote about Flex. This movement started with Flex being released for free use outside of commercial products and then moved to a fully free Flex SDK. I still occasionally see developers stating in blogs or on comments on blogs that they won't use Flex because of its licensing costs, but for much of Flex this is simply not the case anymore. There are some "extra" Flex features that still do require license fees for commercial use, but Flex applications can be written without these.


2. Not Open Source

While there are advantages to a product, library, or framework being open source, calling something open source does not necessarily make it better than competing alternatives (witness all the dead open source projects out there) and does not necessarily make it free of cost (free speech versus free beer). Still, there are a growing number of developers who desire "open source" as one of the most significant characteristics of the products and frameworks they choose. For them and even for those of us who prefer open source when it is competitive in other aspects as well, it was good news when Adobe announced the open sourcing of Flex. Major components of Flex 3 (SDK, debugger, etc.) were released as open source. Shortly before Flex 3's release, Adobe also released BlazeDS (especially useful for Flex-to-Java communication) as open source.

There are optional portions of Flex that are still not open source and/or still require licensing fees, but these are not necessary to create fully functioning Flex-based applications.


3. Perception: For Movies and Animation Only

With the issues of licensing costs and not being open source mostly resolved, a common concern (or really more of a question) that I hear from many developers is something along the lines of, "Isn't Flash really only for games, movies, and animation?" and "Can you create an HTML-like web form with Flash?" The answer is that Flex really is "Flash brought to the Java (or other programming language) developer." By this, I mean that the Flash applications many people are familiar with are more likely designed by a specific Adobe product by designers rather than by developers or programmers. Flex provides a rich component set out-of-the-box with the free and open source SDK, adds a couple more components with the for-a-fee FlexBuilder, and there are numerous third-party Flex components sprouting up all over the web. These components include many HTML-like components along with many richer components that rival or even exceed the component sets provided by the many fine JavaScript libraries. If one wants to, one can make a Flex-based application look nearly identical to any HTML/JavaScript application. However, Flex is usually desired for "richer" applications and hence is often used with Rich Internet Applications rather than more traditional HTML/web applications.


4. Application Load Time

As part of the RIA experience, Flex-based applications tend to load more than DHTML applications initially, but then often have reduced reloading and server communication after the initial load. However, this means that a Flex-based RIA can often take longer to load initially than a DHTML-based RIA.

Adobe continues to work on improving the performance of the Flash Player to reduce this disadvantage. The relatively recent announcement of the new Flash Player Caching is very welcome in this area.

There are several tactics Flex developers can take as well to reduce the initial load time. For example, the Flex TabNavigator has a creationPolicy attribute that allows all tabs in the navigator to be loaded at load time (ContainerCreationPolicy.ALL) or have only the initially visible tab loaded (deferred instantiation with ContainerCreationPolicy.AUTO).

Links to other ideas for improved Flex performance are available in the article Flex RIA Performance Considerations Part 1: Getting Started. Part 2 in this same series focuses on application startup.


5. Development/Productivity Tools

Another early criticism of Flex was the lack of IDE and tooling support that Java enjoys. This, of course, is just as deserved and just as common complaint for many other highly useful languages as well. However, Adobe has released FlexBuilder, an Eclipse-based Flex IDE. While Adobe does charge a licensing fee for FlexBuilder and while FlexBuilder (like most IDEs) does have room for improvement, it is also true that FlexBuilder comes with many nice features for making Flex development more productive. Because it is Eclipse-based, it is fairly easy for Java developers familiar with Eclipse to learn and use.

The author of the entry Flex 3 Vs Silverlight and JavaFX - Is Adobe Listening? maintains that Flex "may die on the vine" because it charges for FlexBuilder. As outlined above, the making Flex itself available for free did undoubtedly help increase its adoption, but the cost difference is significant. Flex formerly had license fees costing thousands of dollars while FlexBuilder fees are measured in hundreds of dollars. Still, many IDE vendors do offer free IDEs already (Eclipse, NetBeans, and JDeveloper for example). While I agree the licensing fee required for FlexBuilder must have some impact on Flex's adoption rate, I'm not convinced that it has been or will be as great on the long-term adopters of Flex as maintained in this entry.

For those who don't want to purchase FlexBuilder, there are some alternatives. Because both MXML and ActionScript are text-based, even a simple text editor will work at a minimum level of functionality. Because Flex MXML is an XML grammar, any XML-aware tool can be used to provide some advantages to working with MXML. For example, I have blogged previously about using NetBeans XML support and JEdit's XML support with Flex. One can go even beyond simple XML handling with more specific editors like Spket IDE and Aptana.


6. Flash Player Dependence

Some developers have had such negative experiences with Flash-based applications that they have a hard time even considering Flash (and hence Flex) for their web applications. There are many reasons for this resistance to the Flash Player, including wanting to avoid a proprietary runtime and not liking Flash application experiences in others' applications. The latter point can be partially addressed by considering that just because someone else abuses a language or framework doesn't many I need to. If that was the case, no language or framework would be acceptable because they all get abused by someone.

Related to the proprietary nature of Flash, there are some mitigating conditions to consider. The OpenScreen Project was huge news for Flash running on devices because it is intended to remove restrictions on two major Flash-related specifications (SWF and FLV/F4V) and to remove license fees for both AIR and Flash Player. Another interesting development occurred when Adobe and Mozilla partnered in the Tamarin Project to include the ActionScript Virtual Machine (AVM).


7. No 64-bit Flash Player

While the Flash Player is nearly universally available in 32-bit architectures, it is not available for 64-bit architectures. This has been a particularly loud complaint from Linux users, though Microsoft Vista developers are starting to request a 64-bit Flash Player more actively as well.

This concern can be partially addressed by running the 32-bit Flash Player on the platform, such as on a 32-bit browser running on the platform. In Flash 9 on 64-bit Linux in 2 Commands, James Ward covers how to use the Flash Player on 64-bit Linux. Likewise, Darron Schall has written about running Flash Player on 64-bit Linux platform in Installing Flash Player 9 on 64-bit Linux. Interestingly, James Ward points out in Where is 64-bit Linux Support for Flash Player? that one of the major hold-ups on a 64-bit Linux Flash Player is the need to upgrade the Tamarin project discussed briefly in the criticism above.

UPDATE (24 November 2008): Adobe has announced the release of an alpha version of Flash Player 10 for 64-bit Linux. An excellent review of life in Linux without this release is available in Adobe Starts 64-bit Flash Testing with Linux Alpha. The excitement in the Linux community over this is documented in One More Reason for Linux Lovers to Give Thanks.


8. No URLs, Histories, or Bookmarks

A common complaint about Flex-based RIAs that is really common to even many Ajax-based RIAs is the inability or difficultly involved in letting web users use meaningful URLs in histories and bookmarks. This was easy and expected in static web pages and even in more dynamic web sites that completely reloaded the page after each server request. With Ajax and Flash-based RIAs, these things are not quite so easy now. Just as many of the Ajax/DHTML frameworks have begun to provide support to ease the developer's ability to allow users to use URLs for bookmarking and histories, similar approaches have sprung up for Flex. However, the really good news was Flex 3's support for BrowserManager and deep linking. Approaches of use in this area (including the Flex 3 BrowserManager to perform deep linking) are covered in Flex 3 Features: Deep Linking in Flex 3, Flex 3 BrowserManager, and Flex 2 Applications and Bookmarking 101. Deep linking only works on select browsers (the most significantly used ones though) and takes a little extra effort, but does provide benefits associated with changing URLs in a web application.


9. Searchability / Search Engine Optimization

Another commonly discussed disadvantage of the Flash Player is the inability of search engines to index the binary content of a Flash application. A command work-around has been to developer HTML "wrapper" pages for the SWF applications that includes terms that the search engines can find. This tried and proven method works, but there are even more advancements coming. Google, Yahoo!, and Adobe have announced their combined effort to better index the text content in Flash applications. Considering the dominance of Google and Yahoo! in the search engine business, this is a big deal. In addition, it sounds like this approach will be made available to other search engines in the future if it works out well.


10. Adobe Commitment to Flex

Some of have questioned Adobe's commitment to Flex, especially in light of Adobe's early interest in SVG (Scalable Vector Graphics) followed by seemingly significantly reduced interest in SVG. While I don't work for Adobe and have no knowledge of what they may or may not do, I don't worry much about this particular criticism because Adobe's products still continue to support SVG to a large degree. More importantly, however, it appears to me as an outsider that Adobe has placed significantly more emphasis, time, and resources into Flex. Also, it is only anecdotal observations, but it seems to me that Flex is gaining popularity much more rapidly than SVG did. This is not too surprising because, as much as I like SVG, Flex is in many ways appealing to a wider audience of developers than SVG. The fact that Microsoft and Sun are trying to match Flex with their Silverlight and JavaFX products respectively demonstrates to me that all three of these major players believe there is a future in the RIA application development business.

For a series of articles spelling out obstacles and competitive forces that Adobe faces with Flex, see the three-part series Flash Wars.


11. Flash Player is Single Threaded

I won't write much about this here because Matthew Fabb's blog entry Multi-threading is Coming (Sort of) to Flash Player 10 does such a great job of articulating (based on his attendance at Jim Corbett's Flash Player Internals presentation at FITC) the issue and good news in that area. He describes how Flash's single-threaded architecture can be a limitation in Flash-based (and hence Flex-based) applications and how Flash Player 10 will be providing some multi-threading support.


12. Flex is Not a Standard

I saved the common criticism that I think is most overblown for last. Flex is based on the W3C standard XML specification (for Flex's MXML) and the envisioned ECMAScript standard [ECMA-262 Edition 4] (for Flex's ActionScript 3.0), including support for E4X (ECMAScript for XML).

While DHTML/Ajax can be argued to be based on standards [(X)HTML, CSS, JavaScript or JScript (ECMAScript implementations)], practical implementations are seldom if ever truly completely specification compliant because the most popular web browser does not allow them to be! The respective browser DOMs are a huge source of differences. I have stated before that standards and specifications are only as good as their implementations. As long as the dominant web browser requires developers to write non-standard code, it is difficult for me to understand how Flex with its standards-based MXML and ActionScript is so much less standard than writing DHTML/Ajax implementations. In fact, the heart of many Ajax applications is XMLHttpRequest, which itself has not been a standard (though it is now in-work at the W3C).


It Doesn't Need to Be One or the Other

Before concluding, I think it is important to point out that there is no need to necessarily use Flex/Flash instead of DHTML/Ajax or to use DHTML/Ajax instead of Flex/Flash. They both actually complement one another nicely and provide benefits to each other. An example is the discussion above about putting text in HTML wrappers of Flash applications for searchability benefits. On the other side, Flash can provide much easier approaches to rich interaction than via JavaScript. The combination of Flex and DHTML is so powerful and expected to be so popular that Flex 3 folded in the Flex-Ajax Bridge into the standard Flex SDK.


Conclusion and Request for Feedback

In this blog entry, I've attempted to outline common criticisms and cited disadvantages of Flex (and Flash). Some of these were valid but have been overcome by events. Others are still valid, but most of them have been at least partially addressed with modern advances in Flex and Flash. I like forward to additional concerns being addressed in future releases of Flex and Flash.

I also encourage you to add any other drawbacks or criticism you have from using Flex and Flash in the feedback section along with any approaches you may have used to overcome one of the drawbacks listed here or in the feedback section. Thanks in advance for your input.


Other Resources with Flex/Flash Criticisms

* Flex 3 Versus Silverlight and JavaFX: Is Adobe Listening? [FlexBuilder not free] (24 July 2008)

* Flash Wars: Adobe Fights for AIR with Open Screen Project [Part 3 of 3] (7 May 2008)

* Flash Wars: The Many Enemies and Obstacles of Flash [Part 2 of 3] (6 May 2008)

* Flash Wars: Adobe in the History and Future of Flash [Part 1 of 3] (5 May 2008)

* Flex Versus Ajax (9 July 2006)

* Why Flex 2 Isn't (8 March 2006)

* A Look at Rich Internet Applications (July/August 2004)

* Is Flash Good or Bad?

Thứ Bảy, 26 tháng 7, 2008

JMX Model MBeans with Apache Commons Modeler

In previous blog entries, I demonstrated coding Model MBeans directly (“in the raw”) and using the Spring Framework. In both cases, two primary advantages of Model MBeans (highly descriptive nature and ability to present non-JMX objects as managed JMX resources) were realized. While writing the Java code to construct Model MBean metadata information is not difficult, it is tedious. The Spring Framework significantly eases this effort, but requires a dependency on Spring if the full descriptive nature of the Model MBean is to be realized via Spring’s JMX metadata annotations. For those who want rich descriptions of their Model MBeans, who want to reduce the Java code required to implement Model MBean metadata, and cannot or do not want a Spring dependency, the Apache Commons Modeler may be the appropriate answer.

As its name implies, the Apache Commons Modeler is part of the highly useful and varied Apache Commons project. For this blog entry, I am using Modeler 2.0.1. The Modeler 2.0.1 download includes a JAR file (commons-modeler-2.0.1.jar) and the XML DTD for Modeler XML metadata configuration files (mbeans-descriptors.dtd). The contents of this DTD can be viewed without downloading Modeler at the URL http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd.

I’ll be demonstrating how to register the same SimpleCalculator class as a Model MBean that was used in the previous blog entries with direct Model MBean creation and Spring-based Model MBean creation. For convenience, the SimpleCalculator class is reproduced here.

SimpleCalculator.java

package dustin.jmx.modelmbeans;

/**
* Simple calculator class intended to demonstrate how a class with no knowledge
* of JMX or management can be "wrapped" with ModelMBeans.
*
* @author Dustin
*/
public class SimpleCalculator implements SimpleCalculatorIf
{
/**
* 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)
{
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.
*/
public int subtract(final int minuend, final int subtrahend)
{
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.
*/
public int multiply(final int factor1, final int factor2)
{
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.
*/
public double divide(final int dividend, final int divisor)
{
return dividend / divisor;
}
}


When using Apache Commons Modeler, most of the work is done in an XML metadata configuration file conforming to the DTD specified above. For this example, that XML file is shown next. The XML element and attribute names are largely self-explanatory and additional information on these can be found within the DTD definition at http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd.

simple-calculator-modeler.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mbeans-descriptors PUBLIC
"-//Apache Software Foundation//DTD Model MBeans Configuration File"
"http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
<mbeans-descriptors>
<mbean id="managedSimpleCalculatorBean"
name="ManagedSimpleCalculatorBean"
description="Applying JMX Model MBean with Apache Commons Modeler"
type="dustin.jmx.modelmbeans.SimpleCalculator">

<descriptor>
<field id="nameField" name="name" value="ModelMBeanInCommonsModeler" />
<field id="descriptorTypeField" name="descriptorType" value="mbean" />
</descriptor>

<operation id="addOperation"
name="add"
description="Integer Addition"
impact="INFO"
returnType="int">
<parameter id="augendParameter"
name="augend"
description="The first parameter in the addition (augend)."
type="int" />
<parameter id="addendParameter"
name="addend"
description="The second parameter in the addition (addend)."
type="int" />
</operation>

<operation id="subtractOperation"
name="subtract"
description="Integer Subtraction"
impact="INFO"
returnType="int">
<parameter id="minuendParameter"
name="minuend"
description="The first parameter in the substraction (minuend)."
type="int" />
<parameter id="subtrahendParameter"
name="subtrahend"
description="The second parameter in the subtraction (subtrahend)."
type="int" />
</operation>

<operation id="multiplyOperation"
name="multiply"
description="Integer Multiplication"
impact="INFO"
returnType="int">
<parameter id="factor1Parameter"
name="factor1"
description="The first factor in the multiplication."
type="int" />
<parameter id="factor2Parameter"
name="factor2"
description="The second factor in the multiplication."
type="int" />
</operation>

<operation id="divideOperation"
name="divide"
description="Integer Division"
impact="INFO"
returnType="double">
<parameter id="dividendParameter"
name="dividend"
description="The dividend in the division."
type="int" />
<parameter id="divisorParameter"
name="divisor"
description="The divisor in the division."
type="int" />
</operation>

</mbean>
</mbeans-descriptors>


My SimpleCalculator-based JMX Model MBeans examples (direct, Spring-based, and now Commons Modeler-based) have all focused on specifying MBean operations and their parameters. To see how to specify other portions of a Model MBean with Commons Modeler, see the "Modeler" section of the article Using Jakarta Commons, Part 2. Note that there have been a few changes since that article was written. The promotion of the Commons project from a sub-project of Apache Jakarta to its own Apache Commons project brought the name change and is a relatively minor change. However, several changes to the API have led to some of the methods used in previous examples being deprecated.

The next class is the main class that reads in the XML metadata configuration file and uses Modeler to register an MBean with the descriptive information provided in the XML file shown earlier. Note that this code explicitly looks up the XML file by name. I could have had Modeler pick up the metadata configuration implicitly by appropriately naming the XML file and including it in the same package as the code, but I chose to use the explicit loading for this example.


package dustin.jmx.modelmbeans;

import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import org.apache.commons.modeler.Registry;

/**
* The purpose of this class is to demonstrate use of ModelMBeans with Apache
* Commons Modeler.
*/
public class CommonsModelMBeanDemonstrator
{
/**
* Main functionality for using Modeler XML metadata to instantiate and
* register a Model MBean with the Platform MBean Server.
*/
public void applyCommonsModeler()
{
final String modelerMetadataFile = "simple-calculator-modeler.xml";
final SimpleCalculator calculator = new SimpleCalculator();
Registry registry = null;
final InputStream modelerXmlInputStream =
CommonsModelMBeanDemonstrator.class.getResourceAsStream(
modelerMetadataFile);

// Use getRegistry(Object,Object) rather than deprecated getRegistry()
registry = Registry.getRegistry(null, null);

// Use instance setMBeanServer method rather than class/static setServer
registry.setMBeanServer(ManagementFactory.getPlatformMBeanServer());
try
{
// The following two methods on Registry (loadMetadata and
// registerComponent) throw the checked and very general Exception,
// which must be captured here..
registry.loadMetadata(modelerXmlInputStream);
registry.registerComponent(
calculator,
"modelmbean:type=commons-modeler", // mbean registered object name
"dustin.jmx.modelmbeans.SimpleCalculator");
}
catch (IOException ioEx)
{
System.err.println(
"ERROR trying to load metadata into Commons Modeler Registry "
+ "from configuration file " + modelerMetadataFile + ":\n"
+ ioEx.getMessage() );
}
catch (Exception ex)
{
System.err.print( "ERROR trying to access metadata file "
+ modelerMetadataFile + ":\n" + ex.getMessage() );
}
}

/**
* Pause for the specified number of milliseconds.
*
* @param millisecondsToPause Milliseconds to pause execution.
*/
public static void pause(final int millisecondsToPause)
{
try
{
Thread.sleep(millisecondsToPause);
}
catch (InterruptedException threadAwakened)
{
System.err.println("Don't wake me up!\n" + threadAwakened.getMessage());
}
}

/**
* Main driver to demonstrate Apache Commons Modeler used with JMX
* Model MBeans.
*
* @param arguments Command-line arguments.
*/
public static void main(final String[] arguments)
{
CommonsModelMBeanDemonstrator me = new CommonsModelMBeanDemonstrator();
me.applyCommonsModeler();
pause(1000000);
}
}


The following three screen snapshots demonstrate how the results look in JConsole.







As the above example has demonstrated, Apache Commons Modeler allows JMX Model MBean metadata to be shifted from hard-coded Java into external XML configuration files. Whether this is an advantage or not is dependent on the situation and developer preferences (some prefer Java coding over XML even for configuration like this). However, it is nice to have an alternative available for those who prefer their Model MBean metadata to be externally configurable.

ADDITIONAL REFERENCES

For additional information on Apache Commons Modeler, see the project’s home page and the previously referenced article Using Jakarta Commons, Part 2. One of the most useful resources for learning to apply Apache Commons Modeler is the Javadoc API documentation for the package org.apache.commons.modeler. This Javadoc-generated documentation is included with the Modeler download and the description for package org.apache.commons.modeler is highly useful in explaining how to use Modeler. Among other interesting pointers, this description specifies how to have Modeler XML configuration found implicitly, how to use manual Java MBean metadata application rather than XML configuration, and which methods of the Modeler API are now preferred and which are now deprecated ("strongly" in some cases).

JMX Model MBeans with Spring Framework

In the creatively named blog entry The JMX Model MBean, I demonstrated with a simple example how the JMX Model MBean enables greater descriptive ability than is available with other JMX MBean types. The simple example also demonstrated that a Model MBean can be applied to an existing class with no knowledge of JMX and without any awareness that it is being exposed via a JMX management and monitoring interface. The one major drawback of Model MBeans that even this simple example demonstrated is the tedious and verbose code needed to build a Model MBean without aid of other tools or frameworks.

In that blog entry, I called out some approaches that can aid in the development of Model MBeans. These include Spring Framework’s JMX support, The Apache Commons Modeler approach, JBoss’s XMBeans, and the use of reflection. In this current blog entry, I will delve into Spring Framework’s support for Model MBeans in greater detail and demonstrate how easy Spring makes it to apply JMX Model MBeans.

By default, Spring’s support for JMX relies upon Model MBeans (though the developer has no need to deal with Model MBeans directly). The reason for this seems clear – the Model MBean allows normal Java classes with no JMX knowledge or awareness to be exposed via a JMX interface. This approach fits Spring’s behavior in other areas. In other words, it seems to be "the Spring way" to use plain Java classes with little or no knowledge of their surroundings and then "wire" them up to their surroundings via external configuration. In this blog entry, I’ll demonstrate how Spring enables Java classes with absolutely no Spring or JMX awareness to be exposed via a JMX interface and how just a little tinkering with the plain Java class via annotations enables the full descriptive power of Model MBeans to be deployed.

Although the JMX Specification (Chapter 4) talks about other potentially useful features of the Model MBean (such as persistence described in section 4.3.4), the two options of the ModelMBean that I most often take advantage of are the Model MBean’s descriptive support and the ability of the Model MBean to expose a non-JMX class in a JMX agent. In Spring, it is almost trivial to obtain this first benefit and expose a class with no awareness of JMX (or even of Spring) as a manageable and monitorable resource. In fact, the "code" needed to do this lies completely in Spring configuration. The following Java code listing contains the class (SimpleCalculator) that has no JMX awareness, but will be exposed via Spring as JMX-accessible. The XML code listing following SimpleCalculator.java shows how Spring is instructed to expose SimpleCalculator as a JMX-accessible resource.

SimpleCalculator.java


package dustin.jmx.modelmbeans;

/**
* Simple calculator class intended to demonstrate how a class with no knowledge
* of JMX or management can be "wrapped" with ModelMBeans.
*
* @author Dustin
*/
public class SimpleCalculator implements SimpleCalculatorIf
{
/**
* 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)
{
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.
*/
public int subtract(final int minuend, final int subtrahend)
{
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.
*/
public int multiply(final int factor1, final int factor2)
{
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.
*/
public double divide(final int dividend, final int divisor)
{
return dividend / divisor;
}
}


spring-mbean-simple-context.xml


<?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="exposedModelMBean"
class="dustin.jmx.modelmbeans.SimpleCalculator" />

<util:map id="exposedMBeans">
<entry key="modelmbean:type=spring-simple" value-ref="exposedModelMBean" />
</util:map>

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

<util:list id="manageableInterfaces">
<value>dustin.jmx.modelmbeans.SimpleCalculatorIf</value>
</util:list>

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

</beans>



The SimpleCalculator class shown above is essentially the same class used in my previous blog entry on Model MBeans done without benefit of Spring. This is far less code than needed in my example of employing Model MBeans without the Spring Framework, but the one disadvantage is that the greater descriptive ability of the Model MBean is not enjoyed in this simple example. This is demonstrated in the following three screen snapshots of JConsole viewing this simple Spring-exposed Model MBean.








The Spring Framework provides a mechanism to provide greater descriptive information to our Spring-exposed Model MBeans. This is done via Spring-specific annotations. The downside of this approach is that the otherwise JMX-unaware and Spring-unaware classes now dependent on Spring-specific JMX-related annotations. However, the benefit of this is great descriptive ability with relative coding ease. The next code listing shows the adapted SimpleCalculatorAnnotated class that is the same class as SimpleCalculator, but with the Spring JMX annotations. The XML needed to configure this class to be exposed via Spring is then shown as well.

SpringCalculatorAnnotated.java


package dustin.jmx.modelmbeans;

import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
import org.springframework.jmx.export.annotation.ManagedResource;

/**
* Simple calculator class annotated with Spring JMX annotations to demonstrate
* using Spring JMX metadata approach. This class uses J2SE 5 annotations, but
* Spring also supports use of Apache Commons Attributes for metadata specification.
*
* @author Dustin
*/
@ManagedResource(
objectName="modelmbean:type=spring-annotated",
description="A simple integer calculator." )
public class SimpleCalculatorAnnotated implements SimpleCalculatorIf
{
/**
* 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.
*/
@ManagedOperation(description="Integer Addition")
@ManagedOperationParameters({
@ManagedOperationParameter(
name="augend",
description="The first parameter in the addition (augend)."),
@ManagedOperationParameter(
name="addend",
description="The second parameter in the addition (addend).")})
public int add(final int augend, final int addend)
{
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.
*/
@ManagedOperation(description="Integer Subtraction")
@ManagedOperationParameters({
@ManagedOperationParameter(
name="minuend",
description="The first parameter in the substraction (minuend)."),
@ManagedOperationParameter(
name="subtrahend",
description="The second parameter in the subtraction (subtrahend).")})
public int subtract(final int minuend, final int subtrahend)
{
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.
*/
@ManagedOperation(description="Integer Multiplication")
@ManagedOperationParameters({
@ManagedOperationParameter(
name="factor1",
description="The first factor in the multiplication."),
@ManagedOperationParameter(
name="factor2",
description="The second factor in the multiplication.")})
public int multiply(final int factor1, final int factor2)
{
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.
*/
@ManagedOperation(description="Integer Division")
@ManagedOperationParameters({
@ManagedOperationParameter(
name="dividend",
description="The dividend in the division."),
@ManagedOperationParameter(
name="divisor",
description="The divisor in the division.")})
public double divide(final int dividend, final int divisor)
{
return dividend / divisor;
}
}


spring-mbean-metadata-context.xml


<?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="exposedModelMBean"
class="dustin.jmx.modelmbeans.SimpleCalculatorAnnotated" />

<bean id="exporter"
class="org.springframework.jmx.export.MBeanExporter"
p:autodetect="true"
p:assembler-ref="assembler"
p:namingStrategy-ref="namingStrategy" />

<bean id="namingStrategy"
class="org.springframework.jmx.export.naming.MetadataNamingStrategy"
p:attributeSource-ref="attributeSource" />

<bean id="attributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />

<bean id="assembler"
class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler"
p:attributeSource-ref="attributeSource" />

</beans>


The next three screen snapshots show via screen captures of JConsole how the Spring annotations have allowed the annotated class to provide greater descriptive details to the JMX client. The annotations are far less verbose and are arguably easier to write and maintain than the code I showed in the Model MBean blog entry in which I did not use Spring.







If it is overly burdensome or undesirable to add these annotations to the source class, one can "wrap" the source class with a class that has these annotations. An example of this is shown next. The SimpleCalculatorWrapper class "wraps" the SimpleCalculator class. In this approach, SimpleCalculatorWrapper must know about Spring and Spring’s JMX annotations, but the original SimpleCalculator does not need to be specific to Spring or to JMX.

SimpleCalculatorWrapper


package dustin.jmx.modelmbeans;

import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
import org.springframework.jmx.export.annotation.ManagedResource;

/**
* "Wraps" a SimpleCalculator class so that this wrapper class can be annotated
* with Spring JMX annotations rather than needing to modify the business logic
* Simple Calculator class.
*/
@ManagedResource(
objectName="modelmbean:type=spring-wrapper",
description="A simple integer calculator." )
public class SimpleCalculatorWrapper implements SimpleCalculatorIf
{
private SimpleCalculator simpleCalculator;

/**
* Setter method to set the object that I "wrap."
*
* @param calculator SimpleCalculator that I should wrap.
*/
public void setWrappedObject(final SimpleCalculator calculator)
{
this.simpleCalculator = calculator;
}

/**
* 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.
*/
@ManagedOperation(description="Integer Addition")
@ManagedOperationParameters({
@ManagedOperationParameter(
name="augend",
description="The first parameter in the addition (augend)."),
@ManagedOperationParameter(
name="addend",
description="The second parameter in the addition (addend).")})
public int add(int augend, int addend)
{
return simpleCalculator.add(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.
*/
@ManagedOperation(description="Integer Subtraction")
@ManagedOperationParameters({
@ManagedOperationParameter(
name="minuend",
description="The first parameter in the substraction (minuend)."),
@ManagedOperationParameter(
name="subtrahend",
description="The second parameter in the subtraction (subtrahend).")})
public int subtract(int minuend, int subtrahend)
{
return simpleCalculator.subtract(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.
*/
@ManagedOperation(description="Integer Multiplication")
@ManagedOperationParameters({
@ManagedOperationParameter(
name="factor1",
description="The first factor in the multiplication."),
@ManagedOperationParameter(
name="factor2",
description="The second factor in the multiplication.")})
public int multiply(int factor1, int factor2)
{
return simpleCalculator.multiply(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.
*/
@ManagedOperation(description="Integer Division")
@ManagedOperationParameters({
@ManagedOperationParameter(
name="dividend",
description="The dividend in the division."),
@ManagedOperationParameter(
name="divisor",
description="The divisor in the division.")})
public double divide(int dividend, int divisor)
{
return simpleCalculator.divide(dividend, divisor);
}
}


spring-mbean-wrapper-context.xml

<?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="wrappedClass"
class="dustin.jmx.modelmbeans.SimpleCalculator" />

<bean id="exposedModelMBean"
class="dustin.jmx.modelmbeans.SimpleCalculatorWrapper"
p:wrappedObject-ref="wrappedClass" />

<bean id="exporter"
class="org.springframework.jmx.export.MBeanExporter"
p:autodetect="true"
p:assembler-ref="assembler"
p:namingStrategy-ref="namingStrategy" />

<bean id="namingStrategy"
class="org.springframework.jmx.export.naming.MetadataNamingStrategy"
p:attributeSource-ref="attributeSource" />

<bean id="attributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />

<bean id="assembler"
class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler"
p:attributeSource-ref="attributeSource" />

</beans>


I won’t show the JConsole view of the wrapper because it appears nearly the same as what was shown for the directly annotated class. The only difference is that the original SimpleCalculator is left untouched by Spring-specific JMX annotations because the wrapper class shields these details from the SimpleCalculator class.

In the annotations examples above, I used the standard Java SE type annotations to provide the JMX Model MBean metadata. Spring also supports using Apache Commons Attributes for this. The Spring approach of specifying JMX ModelMBean metadata via annotations has proven so popular that it now sounds like JMX 2 is very likely to include similar, or at least Spring-inspired, annotation support. The obvious benefit of this would be the ability to enjoy highly descriptive Model MBeans without all the code even when not using Spring.

For completeness, I am including the main Java class that instantiated my three Spring-related examples above: SimpleCalculator without annotations, SimpleCalculator with annotations, and a "wrapped" SimpleCalculator.


package dustin.jmx.modelmbeans;

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


/**
* The purpose of this class is to demonstrate use of ModelMBeans with Spring.
*/
public class SpringModelMBeanDemonstrator
{
/**
* Pause for the specified number of milliseconds.
*
* @param millisecondsToPause Milliseconds to pause execution.
*/
public static void pause(final int millisecondsToPause)
{
try
{
Thread.sleep(millisecondsToPause);
}
catch (InterruptedException threadAwakened)
{
System.err.println("Don't wake me up!\n" + threadAwakened.getMessage());
}
}

public static void main(final String[] arguments)
{
String option;
if ( arguments.length < 1 )
{
option = "simple";
}
else
{
option = arguments[0].toLowerCase();
}

ApplicationContext context;
// assume simple example unless metadata or wrapper explicitly requested
if ( option.equalsIgnoreCase("wrapper") )
{
System.out.println("Spring Wrapper Approach");
context = new ClassPathXmlApplicationContext(
"/dustin/jmx/modelmbeans/spring-mbean-wrapper-context.xml");
}
else if ( option.equalsIgnoreCase("metadata")) // metadata example
{
System.out.println("Spring Metadata Approach");
context = new ClassPathXmlApplicationContext(
"/dustin/jmx/modelmbeans/spring-mbean-metadata-context.xml");
}
else // use simple if neither wrapper nor metadata specified
{
System.out.println("Spring Interface Approach");
context = new ClassPathXmlApplicationContext(
"/dustin/jmx/modelmbeans/spring-mbean-simple-context.xml");
}

final SimpleCalculatorIf calculator =
(SimpleCalculatorIf) context.getBean("exposedModelMBean");
pause(1000000);
}
}


I also added an interface, SimpleCalculatorIf, to these examples to make them easily accessible via the same code for any of the three approaches covered in this blog entry. That simple interface listing is shown next.

SimpleCalculatorIf.java

package dustin.jmx.modelmbeans;

/**
* Interface to expose Model MBean via Spring.
*/
public interface SimpleCalculatorIf
{
public int add(final int augend, final int addend);

public int subtract(final int minuend, final int subtrahend);

public int multiply(final int factor1, final int factor2);

public double divide(final int dividend, final int divisor);
}


Spring makes use of JMX easier in general. Because Spring uses Model MBeans anyway for its JMX support, it is easy to apply the descriptive power of the Model MBean to one’s Spring-exposed JMX MBeans using annotations.

See the blog entry Adding Information to a Standard MBean Interface Using Annotations for a method of using annotations processing to provide descriptive detail via Standard MBeans (as opposed to Model MBeans).




UPDATE (7 February 2009): The following section has been added in response to a request in the feedback section. This section demonstrates how to build and run the code examples shown earlier in the blog post.

The Java source files and XML configuration files for Spring shown above can be placed in different locations depending on how the build file is set up. I will provide an Ant build.xml file that generates a JAR file and a script for running the examples. This build file depends on the Java source code files and Spring Framework configuration files being in a directory structure as shown in the next two screen snapshots.

Placement of Java Source Code Files



Placement of Spring Framework XML Configuration Files



As the screen snapshots above demonstrate, I placed the directories and files under a root directory called C:\dustinjmx. The src and config subdirectories hold the Java source code files and Spring XML configuration files respectively. With this structure in place, the following Ant build.xml file can be used to compile the Java source code and assemble a JAR file. This build file also optionally generates a script that can be used to run the Java/Spring examples.

build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="SpringModelMBeansExamples" default="all" basedir=".">

<property name="spring.home.dir"
value="C:\\spring-framework-2.5.6" />

<property name="config.dir" value="config" />
<property name="src.dir" value="src" />
<property name="build.dir" value="build" />
<property name="classes.dir" value="${build.dir}/classes" />
<property name="dist.dir" value="dist" />
<property name="jar.name" value="springjmx.jar" />

<property name="command.string"
value="java -cp ${spring.home.dir}/dist/spring.jar${path.separator}${spring.home.dir}/lib/jakarta-commons/commons-logging.jar${path.separator}${dist.dir}/${jar.name} dustin.jmx.modelmbeans.SpringModelMBeanDemonstrator" />
<property name="script.name" value="runIt" />

<target name="-init">
<mkdir dir="${dist.dir}" />
<mkdir dir="${classes.dir}" />
</target>

<condition property="isWindows" value="true">
<os family="windows" />
</condition>

<target name="compile"
depends="-init"
description="Compile Java classes.">
<javac srcdir="${src.dir}"
destdir="${classes.dir}"
classpath="${spring.home.dir}/dist/spring.jar:${spring.home.dir}/lib/jakarta-commons/commons-logging.jar" />
</target>

<target name="jar" depends="compile"
description="Assemble class files and context XML files into JAR.">
<jar destfile="${dist.dir}/${jar.name}"
basedir="${classes.dir}"
includes="**/*.*"
filesonly="true">
<fileset dir="${config.dir}" includes="**/*.*" />
</jar>
</target>

<target name="clean"
description="Clean generated artifacts.">
<delete dir="${dist.dir}" />
<delete dir="${build.dir}" />
</target>

<target name="display-run-command"
description="Shows how to run these examples.">
<echo>
<![CDATA[
Run the assembled executable Spring JMX Demonstration code
with a command like the following:

${command.string} <<<demonstrator_type>>>

where <<<demonstrator_type>>> is one of the following:

simple
wrapper
metadata
]]>
</echo>
</target>

<target name="generate-run-scripts"
description="Generate script to run JMX examples"
depends="generate-windows-run-script, generate-linux-run-script" />

<target name="generate-windows-run-script"
description="Generate run script for Windows"
if="isWindows">
<echo file="runIt.cmd">${command.string} %1</echo>
</target>

<target name="generate-linux-run-script"
description="Generate run script for Linux."
unless="isWindows">
<echo file="runIt.sh">#!/bin/bash${line.separator}${command.string} $1</echo>
</target>

<target name="all" depends="jar, display-run-command"
description="Default target: Compiles Java source and assembles JAR" />

</project>



The location of the Spring Framework should be changed in the build file to point to wherever it is located on the individual machine. The generate-run-scripts target can be called with ant generate-run-scripts to build a script that can then be run to start the examples covered earlier in this blog posting. When the script is run, it should be run with a command-line option of simple, wrapper, or metadata like this: runIt simple.



Once this script has been started, JConsole can be started in a different window by typing "jconsole" on the command line. If JConsole is run on the same machine as the Java application just started, the it should be a local connection that can be selected as shown in the following screen snapshot. Use the MBeans tab to interact with the Java application as shown earlier.