Thứ Ba, 23 tháng 9, 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.

Thứ Hai, 22 tháng 9, 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.

Thứ Năm, 18 tháng 9, 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.

Thứ Hai, 8 tháng 9, 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
}

Thứ Sáu, 5 tháng 9, 2008

Lines of Code and Unintended Consequences

One of the most insightful articles that I have read (in terms of both general daily life and a software development career) is Steven Kerr's On the Folly of Rewarding A, While Hoping for B. In this article originally published in 1975, Kerr employs descriptive examples to demonstrate how a reward system can often lead to different and even opposite results than intended. Using lines of code to measure effort and developer productivity often feels like something that would be another good example for this article.

In this blog entry, I have no intention of delving into the details of most of the advantages and disadvantages of measuring and collecting metrics by counting lines of code. These are outlined in many online resources such as the Wikipedia entry, Lines of Code - Dispelling the Myths, Estimation Technique: Lines of Code, and You Can't Manage What You Don't Measure. As these articles point out, there are advantages (such as quantifiable nature and simplicity) and disadvantages (such as language differences, developer efficiency differences, problem complexity differences) to using lines of code as a metric.

For this blog entry, my focus is on how the use of lines of code as a metric may serve as a detrimental motivational force for the software developer. In other words, while using lines of code may be meant primarily to measure productivity and to understand how far along a particular task is, the unintended consequence may be poor decisions that favor larger code counts in shorter periods of time. If we applied Kerr's article directly to this discussion, we might rename the article "On the folly of rewarding potentially worse development practices that happen to lead to more lines of code, while hoping for a measurement and estimation approach that helps developers and managers manage their work loads more effectively." This is not nearly as succinct a title as the original title, but does make the point. To be clear, I am not saying that fewer lines of code is always and necessarily better than more lines of code. What I am writing about here are situations when fewer lines of code is preferable to more lines of code, but the motivation to keep that count up might influence the decision.

So how can using lines of code as a primary metric serve as a detrimental motivational force? I will outline some examples where a developer could make a decision to choose a less efficient software development technique because of pressures of delivering a certain expected number of lines of code per day or week or month. I admit that the realization that I am reducing my expected line of code count has occurred to me more than once as I refactored many lines of code into code that was cleaner, more maintainable, and more readable with much fewer lines of code.


Code Generation

I want to avoid the debate about generated code versus hand-written code here. As with so many other things in life, there are times when each has its advantages and may be more appropriate. However, even in cases where hand-writing code may generate superior code, knowledge that one's performance might be measured by the lines of code produced could lead to one using the generation tools.

For example, I like to only provide "set" (or "mutator") methods on my objects when they are known to be needed. I prefer immutable objects generally. However, "set" methods can add significantly to my overall line of code count if I put them in all classes blindly. This is really easy to do with all modern Java IDEs that will automatically generate these methods for me. In this example, measuring my productivity via lines of code might tempt me to deviate from my hard-earned preference for immutable or carefully crafted mutable objects.

This can also go the other way. For example, one might choose to directly write object-relational mapping code rather than use a third-party object-relational mapping (ORM) mapping tool to increase the line of code count. There might be valid reasons for writing one's own ORM code, but I hope that increasing the lines of code count is not one of them. In this example, I might be tempted to make a decision about ORM handling based on lines of code rather than on more technical criteria.

Code generation can be a highly useful tool and truly can make me more productive. However, it can also be used improperly to "hide" the real efficiency and even lead to unneeded code that still must be tested and maintained.


Use of COTS Products

Several of the blog entries and articles on using lines of code as a measurement tool point out one of the challenges of this approach being how to count libraries. For example, the Spring Framework brings much functionality to an application. This is provided not only by the Spring libraries themselves, but also by all the dependencies libraries it employs. Spring can help one avoid much boilerplate code (as discussed in Add Some Spring to Your Oracle JDBC Access). In extreme cases, one might be tempted to not take advantage of Spring or similar frameworks and libraries because rolling one's own code will lead to a higher count of lines of code. There is also a temptation among many of us to succumb to Not Invented Here Syndrome (even when it is not really appropriate) and the lines of code metric motivation does not make this any easier to avoid.


Redundant Code

Years ago, I saw a well-respected project manager for a major software vendor outline how this person's company's IDE made reuse easy. The person went on to demonstrate this feature, which really boiled down to a fancy CTRL-C, CTRL-V copy-and-paste implementation. This, of course, did not appeal to my ideas of proper reuse. However, such copy-and-paste reuse might be appealing to a developer that knows his or her productivity is being measured simply by lines of code rather than by the maintainability of the code.


Refactoring

While there can be drawbacks to inappropriately chosen refactoring (such as breaking working software or delaying delivery at when implemented with poor timing), I have found that refactoring can often provide significant long-term advantages to the software I develop. Perhaps more surprising is that sometimes it even helps in the short-term because it makes my next task easier and quicker to implement. For example, if I see that I'm doing the same thing I've done before in multiple locations, taking the time to refactor this redundant code into methods often makes even the short-term job easier. However, refactoring like this can be especially egregious in terms of performance measured against lines of code because time is being spent to actually reduce the measured lines of code. It can sometimes be difficult to explain (or difficult for that person to believe) how fewer counted lines of code this week than last actually corresponds to more functional software.


Doing Things the Easy Way Rather than the Better Way

If a developer knows that he or she will be rewarded or punished based on the number of lines of code he or she generates, it is tempting to take the easiest problems with the highest degree of boilerplate coding. Likewise, a developer may choose to implement something in the most wordy manner imaginable rather than taking advantage of polymorphism, reflection, and similar techniques to write more concise code. Note that I am not saying that polymorphism and reflection are always the best approaches. In fact, they can be overused and abused and there are times when the complications of these approaches are overkill. However, I am saying that there are times when they are elegant and the most efficient. Unfortunately, writing code that effectively uses reflection or polymorphism can often be more difficult than writing the code with many more lines. So, as a developer with some degree of motivation based on lines of code, it is tempting to write the lengthier code that is easier and quicker to write rather than take the time to carefully craft the smaller code that requires more time to initially write.


Dead Code (UPDATE: This section added on 11 September 2008)

Most of us have witnessed and understand the lurking dangers of dead code or unreachable code. However, placing too much emphasis on total lines of code without any regard for the quality of the underlying code can be a demotivating factor that influences one's decision to eliminate dead code. Because many of the most common negative side effects of dead code (such as maintainability, extra compilation time, more things to break) are spread out over the long term, the total cost of dead code may not be apparent and so it is easy to think that avoiding confrontation over reduced lines of code is easier and better than removing the dead code (and the lines of code that get credited for it). This is representative of many of the factors discussed here: the negative consequences may be spread out enough or incurred far enough in the future that the idea of reducing negative consequences of reducing code count now outweighs the idea of fixing long-term problems or problems whose negative cost will not be incurred until much later.


How Motivating is the Lines of Code Metric?

This is the most difficult question for me to answer quantitatively. While using lines of code as a quantitative metric for measuring performance theoretically can motivate developers to make poor decisions as discussed above, it is not easy to estimate the degree of motivation this metric has. This difficulty arises from the fact that all individuals are different in terms of their responses to the same motivations. I have also observed that the best software developers often place great importance on the quality of their work and their own craftsmanship. Therefore, it may require a high degree of motivation to sway them from doing what they feel is best. However, if one feels his or her job is on the line or that there might be a nasty confrontation or embarrassing review, even such a person with a strong desire to write the best code can be influenced by such a motivation. The degree that lines of code counting will motivate the developer is also highly dependent on the importance and frequency of its use in the work environment. The closer that the measurement of lines of code is seen to being related to rewards or punishments, the more likely it is that lines of code will lead to poor software development decisions.


Is There a Better Way to Measure Productivity and Progress?

It is generally easy to identify how any reward system motivates unintended and/or undesired results. Unfortunately, it is much more difficult to provide better systems because these alternatives often have their own advantages and disadvantages, including their own side effects or unintended consequences. In other words, it is easier to criticize and find fault with any proposed system than it is to come up with a better system.

I have read about many proposed alternatives to using lines of code as a metric and they all have their own problems and disadvantages. No matter what system we use to measure our progress and our efficiency, we can always keep in mind that these numbers are far from absolute or clear. Instead, focus should be on "orders of magnitude" and the realization that not all lines of code are created equal. Other factors to consider include the difficulty coding a particular problem, languages used, newness of the technology, and the other many inadequacies of this measurement approach. If we don't take lines of code as an absolute measurement and consider lines of code as only one facet in a much larger set of considerations, I believe we can reduce the degree of potential negative motivation that lines of code counting can entail. Most importantly, lines of code count should be only considered with other factors such as recent refactoring, difficulty of work involved, and the relationship of lines of code to actual implemented functionality.


Measuring Affects Performance

Just as measuring performance of software actually impacts the very performance we are measuring, it seems that measuring lines of code may actually impact the number of lines of code generated. When we measure software performance, we try to use tools and techniques that have as little impact on the actual performance of the software as possible. Likewise with measuring code productivity and progress, we should try to use whatever tools and approaches will have the least impact on the generated lines of code. We want code to be generated to best solve a particular problem rather than to meet a certain expected total number of lines of code.


Conclusion

The idiom/proverb "The road to Hell is paved with good intentions" is widely popular because of the many true, real-life instances of it that we have all witnessed. Clients, managers, and even developers themselves want and need methods for estimating project sizes and measuring their progress. Unfortunately, these types of estimations and measurements have never been particularly easy. Measuring lines of code is one method for approaching the measurement of progress of a software development project and does have some obvious advantages. However, measuring lines of code also has its disadvantages. One of the primary disadvantages of measuring progress with lines of code is the potentially adverse motivational effect it can have on developers when they must choose between coding to meet this motivation (more lines of code) or applying tried-and-true software development best practices.






UPDATE (11 October 2008): A somewhat related Geek Hero Webcomic is now available and is called Programmer's Salary Policy.


UPDATE (24 December 2008): The following are some related resources that have been published since my original post or that I have come across since my original post.

"Measuring programming progress by lines of code is like measuring aircraft building progress by weight." (quote commonly attributed to Bill Gates)
- Programming Quotes
- A Double Handful of Programming Quotes

Measuring Productivity of Individual Programmers

Lines of Code as a Measure of Progress

Measuring Programming Progress by Lines of Code

Thứ Hai, 1 tháng 9, 2008

Google Chrome Web Browser

The Google Comic Book on Google Chrome provides insight into what to expect from the Google Chrome web browser. The AppleInsider article Google Planning New Chrome Browser Based on WebKit is a thorough and useful summary of some of the highlights of this project.

The Official Google Blog entry A Fresh Take on the Browser states that Google Chrome will be launched tomorrow (2 September 2008) in over one hundred countries and will be available for Windows-based systems.

My biggest concerns about Google Chrome are:

1. What effect will it have on the success of Mozilla Firefox?

2. Will the Flash plug-in player be supported fully in Google Chrome?

Google has supported Firefox in the past and helped make Firefox an important force in the web browser space. Google Analytics reports that nearly 70 percent of visitors to my blog are using Firefox. I explain this unusually high percentage of Firefox users by rationalizing that most people visiting my blog are software developers and most software developers I know strongly prefer Firefox or some other non-IE browser over IE. As someone who uses Firefox heavily, I'd hate to see support for it dwindle. The Google Blog entry announcing Google Chrome's release specifically mentions use of Firefox components and talks of providing code and support back to the community. Apparently, I'm not the only one wondering about the impact of Google's support being focused on its own web browser instead of Firefox.

The AppleInsider article referenced previously states that plug-ins such as Flash will be supported in Google Chrome. The blog entry Google Chrome, the security tidbits points out that current plug-ins will circumvent some of Google Chrome's security mechanisms.

Because Google Chrome focus areas include standards compliance and speedy JavaScript performance, I hope to see improved browser experiences for developers and users in the future. Whether or not Google Chrome ever captures significant market share, it is likely that it will provide ideas and components that make other web browsers better. I will be curious to see if Google Chrome has success closer to the market-leading Google search engine or closer to one of the numerous web browsers that significantly lack the market share of the browsers such as Microsoft Internet Explorer and Firefox.

Other useful blogs on Google Chrome include Google OS is Actually a Browser and Google Chrome: Not Invented Here Syndrome Invades Mountain View.