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

Using Collections Methods emptyList(), emptyMap(), and emptySet()

Early this year (2008), a reasonable question was asked on one of the Sun forums (the Core APIs - Collections: Lists, Sets, and Maps forum): When should I use emptyList(), emptySet() or emptyMap()?

Some responders replied with obvious answers that probably provided no new information to the person asking the question. What the person was really asking is why would one ever want a method that returns a Java List, a Java Set, or a Java Map that is empty and immutable. The Javadoc API documentation for the three methods Collections.emptyList(), Collections.emptyMap(), and Collections.emptySet() make it clear that these methods are for returning immutable, empty List, Map, and Set respectively. So, the question really is why would one want an empty immutable List, Map, or Set?

Item #27 in Joshua Bloch's first edition of Effective Java is called "Return zero-length arrays, not nulls" and this item describes some of the advantages of returning an empty array rather than a null from a method call. Although I have not gotten my hands on a copy of the recently released second edition of Effective Java, I suspect that its altered item on not returning null (now Item #43 - "Return empty arrays or collections, not null") demonstrates returning an empty collection and perhaps even demonstrates using these emptyList, emptySet, and emptyMap methods on the Collections class to get an empty collection that also has the additional benefit of being immutable (the new edition's Item #15 is "Minimize Mutability").

Why is returning an empty and immutable Collection often preferable to returning a null or even to returning a mutable collection? The most obvious disadvantage of returning a null is forcing the client of the method to deal with that null. The most obvious advantage to an immutable collection is the advantages associated with immutable objects and collections in concurrent programming. Discussion of why immutable objects and collections are highly desirable in multi-threaded environments can be found in Effective Concurrency for the Java Platform and in the Java Tutorials Concurrency Trail.

The example code below provides an example of using one of these methods:


import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class FunWithEmptyImmutableCollections
{
private Set<String> states;

/**
* Prepare the states data member with some sample names of states.
*/
private void prepareStates()
{
states = new HashSet<String>();
states.add("Alabama");
states.add("Alaska");
states.add("Arizona");
states.add("Arkansas");
states.add("California");
states.add("Colorado");
states.add("Connecticut");
states.add("Delaware");
states.add("Florida");
}

/**
* Provide names of all states that begin with provided alphabet letter.
*
* @param firstLetter Letter for which matching state names are desired.
* @return Set of names of states that begin with provided firstLetter.
*/
private Set<String> getStatesStartingWithDesignatedLetter(
final String firstLetter)
{
if ( (firstLetter == null)
|| (firstLetter.isEmpty())
|| (firstLetter.length() > 1) )
{
//return null;
return Collections.emptySet();
}

final Set<String> matchingStates = new HashSet<String>();
for ( final String stateName : states )
{
if ( stateName.startsWith(firstLetter.toUpperCase()) )
{
matchingStates.add(stateName);
}
}

return Collections.unmodifiableSet(matchingStates);
//return matchingStates;
}

/**
* Print out contents of provided Set.
*
* @param printTitle Title to print with set contents.
* @param setToPrint Set whose contents should be printed.
*/
public static void printSetContents(
final String printTitle,
final Set<String> setToPrint)
{
System.out.println("----- " + printTitle + "-----");
for ( final String setItem : setToPrint )
{
System.out.println( setItem );
}
System.out.println("--------------------");
}

/**
* Add provided stateName to provided Set of states.
*
* @param states Set of states to which state name should be added.
* @param stateName Name of state to be added.
*/
public static void addArbitraryState(
final Set<String> states,
final String stateName )
{
states.add(stateName);
}

/**
* Main executable method for running example.
*
* @param arguments Command-line arguments; none expected.
*/
public static void main(final String[] arguments)
{
final FunWithEmptyImmutableCollections me =
new FunWithEmptyImmutableCollections();
me.prepareStates();
Set<String> states = me.getStatesStartingWithDesignatedLetter("C");
printSetContents( "Happy Path: Designated Letter Matches", states);
addArbitraryState(states, "Georgia");
states = me.getStatesStartingWithDesignatedLetter("B");
printSetContents( "Not-So-Happy Path: Designated Letter Not Found",
states );
addArbitraryState(states, "Georgia");
states = me.getStatesStartingWithDesignatedLetter("");
printSetContents( "Unhappy Path: null Returned", states);
addArbitraryState(states, "Georgia");
}
}


When used, these methods do not allow the client calling these methods to change the returned set. The commented-out lines show alternatives to these methods. Instead of returning an empty set, a null could be returned. Also, instead of returning an unmodifiable set, a normal, modifiable set could be returned. The following screen snapshots show the differences that occur when different combinations of these methods are used.

Trying to Modify Results of Collections.emptySet

The next screen snapshot demonstrates what happens when the code tries to modify a returned empty set. There is no NullPointerException in this case because the returned empty set is NOT null, but there is an UnsupportedOperationException.



Trying to Access Null

The next screen snapshot shows the all-too-familiar error message (NullPointerException) when null is returned rather than an empty Set and code tries to do something on that null.



Trying to Modify Collections.unmodifiableSet Returned Set

In this last screen snapshot, the results of trying to modify a Set returned using the Collections.unmodifiableSet method is demonstrated. Like when trying to modify the Set returned by Collections.emptySet(), an UnsupportedOperationException is encountered when the code attempts to modify the Set. However, there is slightly more detail in this exception stack trace, including reference to the Collections.unmodifibleSet.



The Collections class provides many useful static methods for working with Java Collections. In this blog entry, I covered the value of returning an empty, immutable Collection and demonstrated how easy this is to do with the appropriate Collections methods. As part of doing this, I also demonstrated the use of the similar methods for turning a regular collection into an unmodifiable collection.

One thing to keep in mind is the principle of Fail Fast. There may be times where it is simply better to fail than to return empty collections or null.

Thứ Năm, 15 tháng 5, 2008

Presenting at Colorado Software Summit 2008

I am excited about presenting and attending Colorado Software Summit 2008 during October (19-24). The preliminary agenda for the 17th annual edition of this conference is available here and abstracts of my presentations are also available.

As is typical for most speakers at Colorado Software Summit, I will be presenting two presentations three times each. My two presentations are "Applying Flash to Java: Flex and OpenLaszlo" and "Java Management Extensions (JMX) Circa 2008." I am excited to talk about Flex, OpenLaszlo, and JMX, all of which are technologies that I enjoy and consider highly useful.

While presenting two presentations three times each obviously requires significant effort of the speakers, it is one of my favorite aspects of attending this software conference. All too often, I find that two, three, or even four presentations on my "top ten must-see presentations" at conferences are offered during the same time block or in overlapping blocks. By having each presentation offered three times each at Colorado Software Summit, I have been able to see my most anticipated presentations. The other nice benefit of this is that I've had other attendees tell me about sessions they particularly enjoyed and I could change plans to catch the second or third offering of that presentation.

Thứ Bảy, 10 tháng 5, 2008

OpenLaszlo: SWF9 or DHTML?

I am a fan of both OpenLaszlo and Flex. They share many similarities including XML grammar-based layout/presentation languages (LZX for OpenLaszlo and MXML for Flex) and ECMAScript implementations for logic scripting (JavaScript subset for OpenLaszlo and ActionScript for Flex). Both also provide Java developers easy and intuitive approaches to the Flash Player.

While there are many similarities between OpenLaszlo and Flex, a key difference between OpenLaszlo and Flex is the environment that applications written with each of these Rich Internet Application (RIA) are deployed to. Flex requires Flash Player 9 (SWF9) while OpenLaszlo currently supports (outside of beta support) Flash Players 7 and 8, but not Flash Player 9. Also, OpenLaszlo is probably best known for its in-progress support of DHTML starting with OpenLaszlo 4.1. Note that while OpenLaszlo applications are currently compiled to Flash Player 7 or Flash Player 8, they will still run on Flash Player 9. The disadvantage for OpenLaszlo applications compiled to Flash Player 7 or Flash Player 8 format is not that they won't run on Flash Player 9, but instead is that they won't be able to take advantage of performance and other benefits associated with Flash Player 9.

Because work is still underway to make OpenLaszlo compile to Flash Player 9 and to DHTML, an understandable question is which one is getting more attention and why is that deployment environment getting more attention. The blog entry Progress with Flash 9 Runtime indicates that the OpenLaszlo development team is making progress on targeting the Flash Player 9 runtime and the blog entry SWF9 Components Progress provides visual evidence of this progress with an attractive slider component. While the component is nice itself, the real value of this latter blog entry is that it shows off the "substrate" or infrastructure that is available behind the component for Flash Player 9.

In the last blog entry mentioned (SWF9 Components Progress), the feedback comments are almost as informative as the blog entry itself. A user going by "Tim" asks why the OpenLaszlo team is focusing on SWF9 support rather than on DHTML support (which is planned to be delivered with OpenLaszlo 4.1). The answer, as documented in another feedback comment, is that there is a partner helping pay for additional labor to enable OpenLaszlo to support Flash Player 9. While this is a sufficient and understandable reason to emphasize the Flash Player 9 support over DHTML support, I think it is a good decision anyway because of the advantages of the Flash Player 9. One of the primary complaints about Flash Player-based applications and RIAs in general is that they are too slow to load up. Much has been done to improve Flash Player 9's performance and I believe that OpenLaszlo should take advantage of this.

Of course, one of the most compelling features of OpenLaszlo for many of us is its ability to have applications run on DHTML. So, while I understand and even agree with the decision to focus on OpenLaszlo support for SWF9, I still look forward to OpenLaszlo 4.1 when DHTML support will be fully supported. One of the most tantalizing possibilities with OpenLaszlo is that all of the effort to rearchitect the product to support alternative runtimes may make it possible in the future to support other runtime environments as well. As of today, Silverlight and JavaFX seem like possible candidates for future runtime support.

Note that as of this writing (Saturday, 10 May 2008), OpenLaszlo is at version 4.0.12. This version includes significant DHTML support, though some functions are still not supported in DHTML until 4.1 is released. For SWF9 support (also a work in progress), a separate download (from a development branch) of OpenLaszlo is required.

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

More Evidence of Flex's Popularity with the Java Crowd

JSR-296 Specification ("Swing Application Framework") Lead Hans Muller has announced that he is leaving Sun Microsystems to work for Adobe on Flex.

With this announcement and the recent announcement of Chet Haase going to work on Flex, there is obviously some consternation in the Java Swing community. I really like Java, especially on the server-side, but Flex does provide an almost irresistible allure. As I have stated in previous posts, if my application must be web-based, it is very difficult to think of a more compelling combination than Flex front end + Java EE backend. Trying to be an optimist, I see the movement of two major players in the Java Swing world to the Flex world as evidence that there is much for Java-centric developers to appreciate about Flex. However, it is not surprising that the "glass is half empty" viewpoint might be more negative.

Bare Bones BlazeDS HTTPService Example

This blog entry is intended to demonstrate an extremely basic example of the BlazeDS HTTPService. I have discussed the BlazeDS HTTPService at a high level in a previous blog entry. In this entry, I'll show it in action with code and screen snapshots. If you're looking for a similarly simple example of using BlazeDS's JMS Messaging support, see Michael Martin's blog entry Simplified BlazeDS and JMS.

Perhaps the easiest way to start working with BlazeDS is to have the BlazeDS Developer Guide handy for reference and to modify the sample files included with the BlazeDS download to one's own application. In this blog entry, I'll show BlazeDS configuration files that were modified from files available in the samples. I have removed much of the extraneous details that are not relevant to HTTPService to make the files clearer to understand.

The following steps are required to build a first BlazeDS-powered example.

1. Download Flex 3 (or Flex 2 will work).

2. Unzip/expand the downloaded Flex file to a directory. For example, I have Flex 3 installed at C:\flex_sdk_3 for this example.

3. Download BlazeDS (it is still separate even from Flex 3). There are multiple versions of BlazeDS (nightly build or release builds, turnkey or binary or source, etc.). For my example here, I'm using the binary download. I didn't need Tomcat (which comes with the Turnkey version) because I already had it installed. However, I downloaded the Turnkey edition because it has the same blazeds.war file needed for my example (or for any BlazeDS-powered application). In addition, it has a ds-console.war and the sample files to learn from and adapt. The ds-console.war file is useful because it provides Flash-based view into the BlazeDS server once we have it up and running.

4. Unzip/expand the downloaded BlazeDS file into a directory. I expanded the download file into a temporary directory and then expanded the blazeds.war into a directory of its own called C:\blazeds-expanded. I expanded this so that I could have my own web application (WAR file) be built with the necessary pieces of this included. For example, the expanded contents include important JAR files in the C:\blazeds-expanded\WEB-INF\lib directory (which we will bundle into our WAR's WEB-INF/lib) and the BlazeDS XML-based configuration files in the C:\blazeds-expanded\WEB-INF\flex directory that we will edit for this example and place in our generated WAR's WEB-INF/flex directory.

5. Download Tomcat. I chose to use a separate Tomcat rather than the one included with BlazeDS turnkey.

6. Unzip/expand the Tomcat download. Instructions for installing/setting up Tomcat are available here. I installed Tomcat at C:\apache-tomcat-6.0.16.

7. Build the server-side servlet for supporting HTTPService. In most realistic cases, this is likely an already existing servlet. The code for this example's very basic servlet is shown next.


package dustin;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

/**
* Simple example intended to demonstrate BlazeDS with HttpService.
*/
public class BlazeHttpExample extends HttpServlet
{
/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
* @param request servlet request
* @param response servlet response
*/
protected void processRequest(
HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
final String xmlProlog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
final String lineSeparator = System.getProperty("line.separator");
response.setContentType("text/html;charset=UTF-8");
final PrintWriter out = response.getWriter();
try
{
final String user = request.getParameter("user");
out.print( xmlProlog + lineSeparator
+ "<Messages><Message>Hello, " + user
+ "!</Message></Messages>");
}
finally
{
out.close();
}
}

/**
* Handles the HTTP <code>GET</code> method.
* @param request servlet request
* @param response servlet response
*/
@Override
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
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
processRequest(request, response);
}

/**
* Returns a short description of the servlet.
*/
@Override
public String getServletInfo()
{
return "Simple intended to illustrate BlazeDS HttpService support.";
}
}


The highlighted section of code above shows the most important piece of the servlet. This simple servlet accepts a URL parameter "user" and places that in a Hello World-inspired String to return to the client.

8. Generate or edit an appropriate web.xml file. You can copy the web.xml file from the downloaded and expanded blazeds.war file and edit that. However, it comes with a Servlet 2.2-compliant web.xml file and I prefer a Servlet 2.5-compliant web.xml file (see this blog entry for the reason). So I prefer to paste the relevant portions from the BlazeDS-provided web.xml file into my own web.xml file that is servlet 2.5 based. This is often the case anyway because you often have your web application already. The next code listing shows the web.xml file for this example.


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>Dustin's BlazeDS Example</display-name>
<description>Example Using BlazeDS</description>

<context-param>
<param-name>flex.class.path</param-name>
<param-value>/WEB-INF/flex/hotfixes</param-value>
</context-param>

<!-- Http Flex Session attribute and binding listener support -->
<listener>
<listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>

<!-- MessageBroker Servlet -->
<servlet>
<servlet-name>MessageBrokerServlet</servlet-name>
<servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
<init-param>
<param-name>services.configuration.file</param-name>
<param-value>/WEB-INF/flex/services-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet>
<servlet-name>BlazeDSExampleServlet</servlet-name>
<servlet-class>dustin.BlazeHttpExample</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>BlazeDSExampleServlet</servlet-name>
<url-pattern>/blazeDS</url-pattern>
</servlet-mapping>

<!-- MessageBrokerServlet -->
<servlet-mapping>
<servlet-name>MessageBrokerServlet</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>

<login-config>
<auth-method>BASIC</auth-method>
</login-config>

</web-app>


9. Adapt the services-config.xml file and the proxy-config.xml files from the BlazeDS sample to work for this example. The next code listing shows the contents of the services-config.xml after it has been adapted and simplified for this example.


<?xml version="1.0" encoding="UTF-8"?>
<services-config>

<services>
<service-include file-path="proxy-config.xml" />
<default-channels>
<channel ref="my-amf"/>
</default-channels>
</services>

<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
</properties>
</channel-definition>
<channel-definition id="my-http" class="mx.messaging.channels.HTTPChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/http"
class="flex.messaging.endpoints.HTTPEndpoint"/>
</channel-definition>
</channels>

<logging>
<!-- You may also use flex.messaging.log.ServletLogTarget -->
<target class="flex.messaging.log.ConsoleTarget" level="Error">
<properties>
<prefix>[BlazeDS] </prefix>
<includeDate>false</includeDate>
<includeTime>false</includeTime>
<includeLevel>true</includeLevel>
<includeCategory>false</includeCategory>
</properties>
<filters>
<pattern>Endpoint.*</pattern>
<pattern>Service.*</pattern>
<pattern>Configuration</pattern>
</filters>
</target>
</logging>

<system>
<redeploy>
<enabled>true</enabled>
<watch-interval>20</watch-interval>
<watch-file>{context.root}/WEB-INF/flex/services-config.xml</watch-file>
<watch-file>{context.root}/WEB-INF/flex/proxy-config.xml</watch-file>
<watch-file>{context.root}/WEB-INF/flex/remoting-config.xml</watch-file>
<watch-file>{context.root}/WEB-INF/flex/messaging-config.xml</watch-file>
<touch-file>{context.root}/WEB-INF/web.xml</touch-file>
</redeploy>
</system>

</services-config>


The services-config.xml file showed above is necessary for any BlazeDS-based server support. As shown in the listing above, the services-config.xml file points to another file, proxy-config.xml for HTTPService-specific configuration details. It could have been a section directly within the services-config.xml, but BlazeDS externalized the specific details and that feels more modular and easier to manage to me as well. Note that BlazeDS Developer Guide points out that HTTPService and WebService are configured as proxy services (often configured as in this example in an a proxy-config.xml file) while BlazeDS Remote Objects are configured in remoting services (often in an external file remoting-config.xml) and BlazeDS Messaging is configured as messaging services (often in an external file messaging-config.xml). Because the example covered here only uses HTTPService, only the proxy-config.xml file is needed in addition to the services-config.xml file.

The next code listing shows the contents of the simplified proxy-config.xml file.


<?xml version="1.0" encoding="UTF-8"?>
<service id="proxy-service" class="flex.messaging.services.HTTPProxyService">

<properties>
<connection-manager>
<max-total-connections>100</max-total-connections>
<default-max-connections-per-host>2</default-max-connections-per-host>
</connection-manager>

<allow-lax-ssl>true</allow-lax-ssl>
</properties>

<default-channels>
<channel ref="my-http"/>
<channel ref="my-amf"/>
</default-channels>

<adapters>
<adapter-definition id="http-proxy"
class="flex.messaging.services.http.HTTPProxyAdapter"
default="true"/>
</adapters>

<destination id="BlazeDSHTTP">
<properties>
<url>/{context.root}/blazeDS</url>
</properties>
</destination>

</service>


10. Create the Flex client that will "talk" to the BlazeDS server. A simple Flex client is shown next. It enables communication with the servlet described above both via the standard HTTPService that does not use BlazeDS and via the BlazeDS-based HTTPService that has been the central part of this blog entry.


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="750" height="500">

<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.ItemClickEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

private const radioButtonFieldSize:int = 250;
private const resultsTextAreaHeight:int = 150;
private const resultsTextAreaWidth:int = 300;
private const userServiceNoBlazeDsUrl:String =
"http://localhost:8080/dustin-blazeds/blazeDS";

protected function invokeHttpService(event:ItemClickEvent):void
{
const selectedService:String = event.currentTarget.selectedValue;
if ( selectedService == "httpNoBlazeDS")
{
userServiceHttpSansBlazeDS.send();
}
else if ( selectedService == "httpYesBlazeDS" )
{
userServiceHttpWithBlazeDS.send({user: 'Someone Else'});
}
else
{
Alert.show("An unknown Service was requested");
}
}

protected function resultHandlerSansBlazeDS(event:ResultEvent):void
{
serviceResultsTextArea.text =
"Success withOUT BlazeDS!\n"
+ userServiceHttpSansBlazeDS.lastResult.Message;
}

protected function resultHandlerWithBlazeDS(event:ResultEvent):void
{
serviceResultsTextArea.text =
"Success with BlazeDS!\n"
+ userServiceHttpWithBlazeDS.lastResult.Message;
}

protected function faultHandler(event:FaultEvent):void
{
serviceResultsTextArea.text =
"Failure trying to access service.\n"
+ event.fault.faultString + "\n" + event.fault.faultDetail;
}
]]>
</mx:Script>

<mx:HTTPService id="userServiceHttpSansBlazeDS"
useProxy="false"
resultFormat="e4x"
url="{userServiceNoBlazeDsUrl}?user=Dustin"
fault="faultHandler(event)"
result="resultHandlerSansBlazeDS(event)" />

<mx:HTTPService id="userServiceHttpWithBlazeDS"
useProxy="true"
resultFormat="e4x"
destination="BlazeDSHTTP"
fault="faultHandler(event)"
result="resultHandlerWithBlazeDS(event)" />

<mx:Panel id="mainPanel" title="Output from HTTP Service Calls">
<mx:RadioButtonGroup id="serviceCallType"
itemClick="invokeHttpService(event);" />
<mx:RadioButton groupName="serviceCallType"
id="httpWithoutBlazeDS"
value="httpNoBlazeDS"
label="HTTPService without BlazeDS"
width="{radioButtonFieldSize}" />
<mx:RadioButton groupName="serviceCallType"
id="httpWithBlazeDS"
value="httpYesBlazeDS"
label="HTTPService with BlazeDS"
width="{radioButtonFieldSize}" />
<mx:TextArea id="serviceResultsTextArea"
width="{resultsTextAreaWidth}"
height="{resultsTextAreaHeight}" />
</mx:Panel>

</mx:Application>


I intentionally created this Flex client to use both standard HTTPService and BlazeDS-based HTTPService to demonstate how alike they are, but also to show the subtle differences. The most significant difference is that one specifies a url attribute for the non-BlazeDS HTTPService while one specifies a destination attribute for the BlazeDS-based HTTPService. This destination corresponds with the defined destination in the configuration XML files previously examined.

11. Build the Flex client (compile the MXML source into a SWF file); build the servlet; and package the client, servlet, and all the configuration files and BlazeDS JAR files into a WAR to be deployed. This is most easily done with a build script. I use Ant for this example and the build.properties and build.xml files are shown next.

build.xml

<?xml version="1.0" encoding="UTF-8"?>

<project name="JavaBlazeDS" default="default" basedir=".">

<property file="build.properties" />

<path id="classpath">
<pathelement location="${lib.servlet}"/>
</path>

<target name="initialize">
<mkdir dir="${dir.build}" />
<mkdir dir="${dir.classes}" />
</target>

<target name="clean"
description="Clean all generated/compiled files.">
<delete dir="${dir.build}" />
</target>

<target name="default"
depends="buildWar"
description="Default target for this build." />

<target name="compileFlex"
description="Compile Flex application into SWF file.">
<exec executable="mxmlc">
<arg line="-debug=${flex.debug}" />
<arg line="-context-root=${flex.context.root}" />
<arg line="-services=${flex.services}" />
<arg line="-publisher=${flex.publisher}" />
<arg line="-title=${flex.title}" />
<arg line="-description=${flex.description}" />
<arg value="${dir.flex.src}/BlazeDSInterface.mxml" />
<arg line="-output ${dir.build}/BlazeDSInterface.swf" />
</exec>
</target>

<target name="compileJava" depends="initialize"
description="Compile server-side Java code.">
<javac srcdir="${dir.src.java}"
classpathref="classpath"
destdir="${dir.classes}"/>
</target>

<target name="buildWar"
depends="compileFlex,compileJava"
description="Generate WAR file with Flex and server-side Java.">
<war destfile="${dir.build}/${war.dustin-blazeds-example}"
webxml="${dir.web-inf}/${file.web.xml}">
<lib dir="${dir.blazeds.lib}" includes="*.jar" />
<classes dir="${dir.classes}" includes="**/*.class" />
<zipfileset dir="${dir.flex.config}" includes="*.xml" prefix="WEB-INF/flex" />
<fileset dir="${dir.build}" includes="*.swf" />
</war>
</target>

</project>


build.properties

dir.build=build
dir.classes=${dir.build}/classes
dir.flex.config=WEB-INF/flex
dir.flex.src=web
dir.src.java=src
dir.web-inf=WEB-INF

file.web.xml=web.xml

dir.blazeds=C:\\blazeds-expanded
dir.blazeds.lib=${dir.blazeds}\\WEB-INF\\lib

dir.j2ee.home=C:\\apache-tomcat-6.0.16\\lib
lib.servlet=${dir.j2ee.home}/servlet-api.jar

flex.context.root=dustin-blazeds
flex.debug=true
flex.description="Example of using HTTPService with BlazeDS"
flex.publisher=Dustin
flex.services=C:\\NetBeansProjects\\JavaBlazeDS\\WEB-INF\\flex\\services-config.xml
flex.title="BlazeDS with HTTPService Example"

war.dustin-blazeds-example=dustin-blazeds.war


One vital observation to make from the Ant script above is the importance of passing two mxmlc compiler options to the mxmlc application compiler when working with BlazeDS. The -context-root and -services application compiler options are significant and required. These are also documented in the feedback section of the blog entry BlazeDS: Open Sourcing Remoting and Messaging.

12. Deploy the WAR that contains the Flex client and the BlazeDS JARs and configuration XML. Run the example by going to the appropriate Tomcat URL.

When this last step is followed, the simple Flex client shown above will look something like that shown in the next two screen snapshots.

Results from Using HTTPService without BlazeDS


Results from Using HTTPService with BlazeDS


The simple Flex client shown in these snapshots demonstrates using HTTPService to contact that same servlet both with and without BlazeDS. There are advantages associated with BlazeDS that may justify its significant additional effort. I discussed these in the previously mentioned BlazeDS RPC blog entry. They include the ability to perform authentication, the ability to log on the server-side, and the removal of the requirement to employ a crossdomain.xml file.

In this blog entry, I showed how the BlazeDS-provided samples could be modified to one's own examples to run HTTPService with and without BlazeDS. A developer could then add back in certain portions of the configuration files to add in security, logging, etc.

There are several useful BlazeDS references that provide significantly greater detail than that provided here. I referred to some of them above as well. These resources include the following:

* BlazeDS Developer Guide - Referenced multiple times in this blog entry.

* BlazeDS 30-Minute Test Drive - Useful for seeing BlazeDS in action by using samples directly with turnkey version of BlazeDS. Adds explanation to what the sample code and configuration is actually doing.

* BlazeDS Installation Guide

* BlazeDS for Java-Flex Communication

* BlazeDS: Open Sourcing Remoting and Messaging

* Simplified BlazeDS and JMS

Thứ Hai, 5 tháng 5, 2008

Flash Cookies with Flex

It is easy to persist and retrieve data from a "Flash Cookie" in Flex using the ActionScript class flash.net.SharedObject. In this blog entry, I will briefly introduce Flash Cookies and then demonstrate with a simple example how easy it is in Flex to read from and write to Flash cookies. I'll end the blog entry by discussing how one can find out which Flash cookies are already installed on his or her machine.

Local Shared Object (LSO) is the formal name for a "Flash Cookie." For the most part, a Flash Cookie is conceptually the same thing as the better-known HTTP Cookie. The key differences are that Flash Cookies (Local Shared Objects) have a much larger default storage size (100 KB compared to the Internet Cookie's typical 4 KB). Because "Flash Cookies" are so similar to Internet/HTTP cookies, most of the same controversies surrounding cookies apply to both styles equally.

As a web user with a Flash Player installed on your browser, you can control the size of the Flash Cookies (LSOs) stored on your machine for a specific domain. This is easy to do by right-clicking on any Flash-based application in your web browser and selecting the "Settings..." option. The next image is a screen snapshot that shows what appears after selection of "Settings...".



Note in the image above that there are four icons along the bottom of the "Adobe Flash Player Settings" pop-up (next to and left of the "Close" button). The icon that looks like a green arrow pointing into an open folder is the one selected for this screen snapshot shown above. As the snapshot indicates, one can then select the amount of local storage that the domain can use for its Flash Cookies. In this case, the default amount of "100 KB" is still set for the "local" domain (because I ran a Flex-based Flash application hosted on my localhost) and we can see that the cookie is currently 1 KB in size.

So, how do we place data from a Flex-based Flash application into these Flash Cookies and then extract the data back out again? The following code example demonstrates how to do this and the most relevant lines for this discussion are highlighted.


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="750" height="500">
<mx:Script>
<![CDATA[
/**
* To compile this Flex application from the command line, use
* mxmlc solExample.mxml
*
* This example demonstrates how to use ActionScript to write to, read from,
* and generally manage "Flash cookies" (Local Shared Objects stored on the
* user's host machine to track state for Flash applications).
*/

private const dustinLSO:SharedObject =
SharedObject.getLocal("dustinLocalSharedObject");


/**
* Persist the Local Shared Object (LSO) [AKA Flash Cookie].
*
* @param event Event being handled.
*/
private function persistFlashCookie(event:Event):void
{
// Must access access LSO contents via 'data' property or else see the error
// message "Error: Access of possibly undefined property name through a
// reference with static type flash.net:SharedObject."
dustinLSO.data.name = cookieNameText.text;
dustinLSO.data.text = cookieText.text;

}

/**
* Retrieve and display the Local Shared Object (LSO) contents.
*
* @param event Event being handled.
*/
private function displayFlashCookie(event:Event):void
{
cookieNameDisplay.text = dustinLSO.data.name;
cookieTextDisplay.text = dustinLSO.data.text;

}
]]>
</mx:Script>

<mx:Panel id="mainPanel" title="Showing Off Flash Cookies">
<mx:Form id="mainLsoExampleForm">
<mx:FormItem id="cookieNameItem" label="Name of Flash Cookie">
<mx:TextInput id="cookieNameText" />
</mx:FormItem>
<mx:FormItem id="cookieTextItem"
label="Text to Save to Flash Cookie">
<mx:TextArea id="cookieText" />
</mx:FormItem>
</mx:Form>
<mx:Button label="Persist Flash Cookie" click="persistFlashCookie(event);"/>
<mx:Spacer height="15"/>
<mx:HRule percentWidth="100" strokeWidth="2" strokeColor="blue" horizontalCenter="0" />
<mx:Spacer height="15"/>
<mx:Button label="Retrieve Flash Cookie" click="displayFlashCookie(event);"/>
<mx:Text id="cookieNameDisplay" />
<mx:TextArea id="cookieTextDisplay" />
</mx:Panel>

</mx:Application>


It is difficult in a blog entry to demonstrate persisting of Flash Cookie data that is available between separate executions of the Flash application, but I will attempt to do so with some screen snapshots (click on them to see focused versions) that follow.

The first screen snapshot shows what the Flash application generated from the Flex code above looks like when it first comes up.



The next screen snapshot illustrates placing some text into the name and text fields and pressing the "Persist Flash Cookie" button to write the name and text out to a Flash cookie.



When the "Retrieve Flash Cookie" button is pressed to retrieve data from the local machine stored in the Flash cookie, it is returned as demonstrated in the next screen snapshot.



To make sure that the data is really being stored on the localhost drive rather than simply being in the Flash application's memory, I reloaded the entire application and started by clicking on the "Retrieve Flash Cookie" button. The results are shown in the next screen snapshot.



As shown in the last screen snapshot, the data is retrieved from the client machine and so was persisted between completely different executions of the Flash application. In other words, just as traditional Internet Cookies are used for HTTP state management purposes, Flash Cookies similarly support state management between Flash applications execution.

With the overall conceptual description of Flash Cookies covered and a brief introduction to using Flex and Flash Player Action Script class SharedObject to read and write Flash Cookies covered, it is time to move on to detecting Flash Cookies on your own machine.

Flash applications persist Flash Cookies to a directory that includes a randomly-generated subdirectory name. On the Windows Vista machine I used for the screen snapshots above the Flash Cookies can be found at C:\Users\Dustin\AppData\Roaming\Macromedia\Flash Player\#SharedObjects\<RANDOM>. A screen snapshot showing the Windows Explorer perspective on the more specific subdirectory with the Flash Cookie for this entry's Flex example is shown next.



You may be wondering how to best manage/remove Flash Cookies from your own machine. While the Settings option shown at the first of this entry allows you to maintain the size of the cookies, seeing a list of the Flash Cookies on your machine and removing them is most easily done at an Adobe web site specifically designed for this purpose. The Adobe Flash Player Settings Manager is used to manage Flash Cookies. Specifically, the Adobe Flash Player Website Storage Settings Panel at http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager07.html allows users to see which Flash Cookies exist on their machine and to manage them.

While Flash Cookies and traditional HTTP Cookies are conceptually similar with similar restrictions on what they can be used for and what they should be used for, there are two major differences. The Flash Cookies (Local Storage Objects) can store significantly more text than the HTTP Cookie counterparts and Flash Cookies require visiting a special web site for removal rather than direct browser removal support (in most cases). A good resource on options for removing and blocking Flash cookies is How Flash Cookies Threaten Your Privacy. It includes a reference to the Firefox extension Objection that is designed to allow for removal of Flash Cookies via Firefox.

When a Flex developer wishes to store data between execution of his or her Flash application on a user's machine, Flash Cookies can be an easy approach for providing a better experience to the end user.

Thứ Bảy, 3 tháng 5, 2008

What Will Be the Big Announcement at 2008 JavaOne?

With 2008 JavaOne nearly upon us, it is interesting to speculate on what big announcements will be made there or which we would wish would be made there. I won't be speculating much on this in this blog entry, but I will be providing links to web resources (blogs, articles, and other resources) that provide summaries of previous JavaOne conferences. Reading some of these articles provides a trip down memory lane, but also might provide insight into what is coming at 2008 JavaOne. The 2008 edition of JavaOne has already inspired An Ode to JavaOne and a "will work for food" type request in the form of Will Blog, Write, or Program for JavaOne Pass.

The JavaOne presentations also provide hints as to themes and announcements. For example, 2007 JavaOne's biggest announcement was JavaFX. Hints of this were available in the form of previously scheduled presentations on Form Follows Function (F3), the starting point for JavaFX.

Speaking of JavaFX, I suspect that it will remain a major player in this year's conference along with other recently emphasized items such as NetBeans, GlassFish, OpenJDK (OpenJDK 2008 JavaOne Blog Central), other open source efforts, and various scripting language efforts. Of course, Java EE 6 and Java SE 7 are likely to receive significant attention as well. These speculations are pretty easy to make based on the history and trends of recent JavaOne conferences.

Here are some web resources that describe the major announcements and news stories from previous JavaOne conferences. One caveat to keep in mind is that the significance of news or announcements is often dependent on the person. So, these are things that the authors of the resource and/or I think are significant from previous JavaOne conferences. One other interesting (albeit not surprising) observation is that it is much easier to find recent JavaOne summaries and reports than it is to find earlier JavaOne resources.



2007 JavaOne (8-11 May 2007)

JavaFX was probably the most dominant announcement at 2007 JavaOne for most people, but news on OpenJDK progress and on real-time Java (the oldest JSR of them all) was also significant. Open source in general was also big and there was significant focus on NetBeans and GlassFish as part of this theme.

Juixe TechKnow's JavaOne 2007 Conference Notes

JavaOne 2007: Prodigal Sun Returns to the Client (Client-side and JavaFX)

2007 JavaOne Conference: OpenJDK: Now the Journey Starts for the Community

TheServerSide Javaone 2007 Coverage - Day 1 and Days 2/3

JavaOne 2007: Sun Announces JavaFX



2006 JavaOne (16-19 May 2006)

2006 JavaOne continued themes of Ajax, NetBeans, GlassFish, and talk of open sourcing Java. High hopes for Java EE 5 and newly announced Google Web Toolkit also seem to have been popular.

JavaOne Today (16 May 2006)
JavaOne Today (17 May 2006)
JavaOne Today (18 May 2006)
JavaOne Today (19 May 2006)

JavaOne 2006: The Executive Summary

Sun to Open-Source Java

JavaOne 2006: 'Not a Question of Whether, But of How'



JavaOne 2005

JavaOne 2005 featured The Return of NetBeans, Building and Strengthening the Java Brand (renaming JDK to Java SE and renaming J2EE to Java EE), and the beginning of Project Glassfish.

JavaOne Announcements

JavaOne 2005: Java Platform Roadmap Focuses on Ease of Development, Sun Focuses on the 'Free' in F.O.S.S.

JavaOne 2005 Special Report

TheServerSide @ JavaOne 2005 Day 1, Day 2, Day 3, Day 4

JavaOne 2005 Day 1: It's a Groovy Day
JavaOne 2005: Day 2: Hitting the Jackpot!
JavaOne 2005: Day 3: Share the News!
JavaOne 2005: Wrap Up

JavaOne 2005: Participate in the Future of Java



JavaOne 2004

Major news at JavaOne 2004 centered on Sun's desire to grow the large Java developer community to a much larger size, making an open source reference implementation of JavaServer Faces available, EJB 3.0, Service-Oriented Architecture, and JDK 1.5 ("Tiger", JSR-176).

JavaOne 2004 Keynote Webcasts

The Java Economy is Thriving

TheServerSide.com Coverage of JavaOne 2004

JavaOne 2004: Final Thoughts



JavaOne 2003

At JavaOne 2003, early talk about JDK 1.5 and J2EE 1.5 was underway and concepts that are popular today (scripting, annotations, and general ease of use) really got a head of steam.

JavaOne 2003: Less Hype, More Filling

JavaOne 2003: Java Roadmap (Technical Keynote)

JavaOne 2003: Technical Session Sampler

JavaOne 2003 Developer Conference

What's Happening at JavaOne (2003)

News from JavaOne (InformationWeek, 2003)



JavaOne 2002

Open source and the relationship of Sun, Java, and open source seemed to be a highlight of JavaOne 2002. The Java Specification Participation Agreement (JSPA) is one of the most obvious pieces of evidence for this statement.

Best and Worst of JavaOne 2002

JavaOne 2002: Open Source Gets Some Much Needed Movement

JavaOne 2002: Zig's Notes



JavaOne 2001

Web Services and Java Micro Edition were two popular topics at this edition of JavaOne. Running Java everywhere has been a regular favorite topic at JavaOne conferences and 2001 is one of the best examples of this.

JavaOne 2001 Conference

JDJ's JavaOne 2001 Summary

JavaOne 2001: Devices Take Center Stage



JavaOne 2000

Among other things, the bundling of JS2E on the Mac and the use of Java with the Sega Dreamcast were big announcements at this JavaOne. EJB 2.0 and Jini also seem to have been popular topics.

JavaOne: A Product Roundup (2000)

JavaOne: Visions of a Future Internet and 'Java Number Five' (2000)

Exciting News from JavaOne 2000

Games Distracting at JavaOne (2000)

JavaOne 2000 - JavaBroker Review



JavaOne 1999 (15-19 June 1999)

JavaServer Pages received significant attention at JavaOne 1999. At this time, of course, XML was making huge headway and so it was not surprising that Java/XML integration would be a big topic at this conference as well. It would be years later before standardized Java/XML binding would be available with the JDK (Java SE 6).

JavaOne 1999 Report



JavaOne 1998

Personal Java was perhaps the biggest announcement at JavaOne 1998. Sun has announced its End of Life because of it being superseded by J2ME.

JavaOne Unleashes Flurry of Announcements from Sun (JavaOne 1998)

Looking Back on JavaOne, April 1998


JavaOne 1998: A Watershed Moment




JavaOne 1997

This may have been one of the most interesting JavaOne conferences in terms of breadth of new language APIs to talk and dream about. Applets were at a zenith and were of high interest at this conference.

JavaOne Offers Advancements, Mind Benders, and Disappointments

JavaOne '97 Conference Report: Same Thing Last Year?



JavaOne 1996

The one that started it all ... does any more need to be said?


JavaOne Conference Report (JavaOne 1996)


SunSoft Announces Java Toolkit, JavaOS

JavaSoft to Host JavaOne: Sun's First Worldwide Java Developer Conference



When I had nearly finished this blog entry, I stumbled across a similar (but more concise) blog entry about JavaOne 2006. Bill Roth's JavaOne: A Look Back, and Predictions for this Year is similar in concept but has quite different coverage of past JavaOne conferences). I highly recommend reading it.

Two other interesting resources on the earlier years of Java are Java Technology: The Early Years (look back from 1998) and The Java Platform: Five Years in Review (look back from 2000).

Finally, another useful approach to determining what might be announced at a JavaOne conference is to try to find clues in other software development conferences. The Colorado Software Summit, for example, features Simon Phipps (Chief Open Source Officer at Sun) and John Soyring (IBM) regularly in their keynote speeches and I believe these give us a glimpse of what two major players in the Java space (Sun and IBM) are thinking regarding the future of Java and their support of Java.


If you have a prediction or guess as to what the big announcements will be at 2008 JavaOne, please consider replying with a feedback message and put your guess out there. Likewise, if you remember something that was important to you from a previous JavaOne conference, please consider replying with that memorable announcement as well.