Tuesday, September 15, 2009

Comparing Metro and CXF

Over the past couple of weeks, I have been doing some research on Metro and CXF to find out some performance nos and see if there's a clear advantage in using one over the other for JAX-WS compliant service. In short, Metro definately was faster than CXF (untuned or out of the box). Here's a chart I put together showing the numbers - I ran the same call in a loop, making 100 serial calls.


(All timings are in seconds)

The data was collected on a Toshiba Dual Core 2.1 Gz, 2GB RAM Tablet running Windows XP with the services hosted in Weblogic 10.3. The service itself did not do much in terms of functionality but to populate some in-memory data into the service response. So these times would not necessarily match true functional service times.

I also took a look at development simplicity as the other key measure in determining the viability of the 2 libraries in the enterprise. I took the WSDL first approach in the development of these services, and found that CXF was a little easier to work with than Metro. Code generation and configuration were the 2 key factors here and both had some quirks here (with namespaces especially). CXF, out of the box integrates well with Spring. Metro doesn't though there is some hook that has been developed for it (though the initial version had a memory leak) - I did not try Spring integration with Metro.

In terms of functionality and supporting the WS-* standards, Metro definately has it all, while CXF is catching up. To me the key WS standards are WS-Security/WS-Policy, WS-Reliability & WS-Transaction. While Metro supports them all being a reference implementation for the spec, CXF supports some parts of WS-Security/WS-Policy while it does not support WS-Transaction at this point.

Overall, I still prefer CXF over Metro, though it seems like Metro looks a winner in all key categories I considered. At the end of the day, the differences are marginal and really depend upon what your needs are from a project perspective. What I mean here is that if you are looking at other things - eg. an ESB maybe (though management hates that word now, so be careful on that one...:)), for your project, then CXF fits in very well since it is a part of the Apache FUSE project. For Metro, Sun's(or Oracle) libraries are under a binary license (BCL), so that may be an issue to you if you are looking to change code (sometimes to fix a problem), but other than that, both libraries are solid and will probably both have equal adoptance across the enterprise space. While it may be a no brainer that if you were using XFire before, you should move to CXF now, performance wise, don't expect any difference (in-fact XFire was actually marginally faster over CXF). One thing I do need to play with here is the tuning recommendations on marshalling/unmarshalling of Xml in CXF that the project site talks about when using JAXB. I will post that information later since currently the documentation on it is really lacking (besides this - SXC).

Hope this post helps you make a choice - if its not helped, then its probably because both libraries are equally good - though I will say that Apache Axis definately has fallen behind.

Sunday, September 13, 2009

Sync your Outlook Calender on an iPod Touch with Exchange even with the iPhone 3.1 update


Thought I would blog about this since many people are feeling the pinch from the latest iPhone 3.1 OS update that happened last Friday. While my solution may not be perfect, it works and helps you continue to sync your iPod touch or old iPhone 3G with your enterprise Exchange server that requires your device to encrypt your data. As you may be aware, the 3.1 OS update fixed a loophole Apple had introduced, for maybe the first time, in its product line. Old devices were allowed to sync with Exchange 2007 servers even if the servers required the hardware to require encryption of the data. In general when iPod Touch/iPhone communicated with Exchange, they wrongly stated to the Exchange server that they supported encryption. That is what allowed these devices to work with Exchange even though your enterprise policy required that such mobile devices store their data in an encrypted format. Apple has recently fixed this - meaning all these old devices, except the iPhone 3GS, won't lie about their encryption offering anymore. As a result of this, if your Exchange server requires encryption, your device will probably not sync with Exchange. So what do you do - and the option to get a 3GS makes no sense to you - well, then you workaround it of-course, because there was another feature added into the iPhone 3.0 OS upgrade that allows you to sync with Google Calender (using CalDAV) - and I believe you can sync with Yahoo Calendar too. Once again indirection solves the issue. You download Google Calendar Sync. This allows all your appointments in Outlook to be synced with Google calendar. Then setup your ipod touch or iphone to sync with Google Calendar. Here are the steps for that -
This is how I configured CalDAV for Google Calendar on my iPod Touch:

Go to Settings > Mail, Contacts, Calendars
Tap "Add Account...", then "Other"
Under Calendars, "Add CalDAV Account"
Enter account information:
Server: www.google.com
User Name: [your username]
Password: [your password]
Description: [e.g. Personal]
Tap "Next"


Hopefully, this option will help you out. Since CalDAV can be setup to use SSL, the appointment information can be securely pulled down. However, your enterprise still may not want your calendar information replicated to an outside site (in this case Google) for whatever reason. So be sure to confirm that this is considered a viable fallback option in your enterprise before you go ahead with it.

While this helps with appointments/meetings which it really what I find useful on the iPod Touch, the iPhone users may be making use of the Mail sync function as well - and there really isn't a solution for that besides upgrading to 3GS. That's why I recommend this solution more for the iPod Touch users than the iPhone users.

Tuesday, April 21, 2009

Using antlib to work with Maven

If you are using ant to build your project, you may want to start looking at moving away from ant and leverage Maven instead. I've been using Maven for about a year now and after the initial pains in understanding and building a complex project with Maven, I have understood and started liking what it has to offer. However, for those still stuck in the Ant world, you can still leverage the Antlib library to leverage the dependency management capabilities of Maven. The best thing is that if you use Eclipse, you can also leverage the m2eclipse plugin for maven for projects using antlib. This helps get rid of all your dependent 3rd party jars embedded in your project.

So if you have a JEE project - typically one that builds a separate WAR and EAR file, you will probably need 3 Maven POM files.

---MyProject
|
---web
| |_
| pom.xml
|
---ear
| |_
| pom.xml
|
---pom.xml <--(toplevel pom)
|
---build.xml

All that's required in the top level pom is the following:

<project xmlns="http://maven.apache.org/POM/4.0.0" xsi="http://www.w3.org/2001/XMLSchema-instance" schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelversion>4.0.0</modelversion>
<groupid>MyProject</groupid>
<artifactid>MyProject</artifactid>
<packaging>pom</packaging>
<version>1.0.0</version>
<modules>
<module>web</module>
<module>ear</module>
</modules>
</project>

The web and ear directories will have a POM file depicting the dependencies for these respective artifacts. So for example, the WAR pom file could look like:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.myproject</groupId>
<artifactId>myproject-war</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<dependencies>
<!-- Application specific -->
<dependency>
<groupId>struts</groupId>
<artifactId>struts</artifactId>
<version>1.2.8</version>
</dependency>

<!-- provided -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
<scope>provided</scope>
</dependency>
</dependencies>

</project>

Once you have these POM files setup, you can enable Maven support for the project using m2Eclipse. The "update configuration" and "update dependencies" options available through m2eclipse should create a CLASSPATH variable for your project with jars included based on those depicted in the WAR and EAR POM files.

Now for your ant scripts to work with Maven and these POM files, here's what the ant script could look like:

<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="buildAll" name="MyProject" xmlns:artifact="urn:maven-artifact-ant">
<!-- Antlib -->
<path id="maven-ant-tasks.classpath" path="lib/maven-ant-tasks-ext-1.0.0.jar" />
<typedef resource="org/apache/maven/artifact/ant/antlib.xml"
uri="urn:maven-artifact-ant"
classpathref="maven-ant-tasks.classpath" />
<artifact:pom id="maven.war.project" file="${web.dir}/pom.xml"/>
<artifact:pom id="maven.ear.project" file="${ear.dir}/pom.xml"/>
<artifact:dependencies pathId="war.dependency.pathset">
<artifact:pom refid="maven.war.project" />
</artifact:dependencies>
<artifact:dependencies pathId="ear.dependency.pathset">
<artifact:pom refid="maven.ear.project" />
</artifact:dependencies>


<target name="compile" depends="prepare">
<condition property="debug.flag" value="on">
<istrue value="${localbuild}" />
</condition>
<condition property="debug.flag" value="off">
<isfalse value="${localbuild}" />
</condition>
<echo>Compiling source files, debug turned on</echo>

<!-- Load dependency set -->
<javac debug="on" deprecation="${deprecation.flag}" destdir="${classes.dir}" optimize="${optimize.flag}" source="1.6" target="1.6">
<src path="${gensrc.dir}" />
<src path="${src.dir}" />
<exclude name="${cvs.dirs}" />
<classpath refid="war.dependency.pathset" />
<classpath refid="ear.dependency.pathset" />
</javac>
</target>

<target name="buildWebModule" description="Create the Project Web WAR" depends="compile">
<!-- Copy dependencies to temporary web lib -->
<artifact:dependencies filesetId="war.dependency.fileset" useScope="runtime">
<artifact:pom refid="maven.war.project" />
</artifact:dependencies>
<delete dir="${web.build.dir}/lib"/>
<copy todir="${web.build.dir}/lib" flatten="true">
<fileset refid="war.dependency.fileset" />
</copy>

<!-- Create WAR -->
<war destfile="${war.path}" webxml="${web-inf.dir}/web.xml" manifest="${web.dir}/META-INF/MANIFEST.MF">
<fileset dir="${web.dir}">
<include name="**" />
</fileset>
<classes dir="${classes.dir}"/>
<lib dir="${web.build.dir}/lib"/>
<webinf dir="${web.build.dir}"/>
</war>
</target>
</project>

And you can expand the build.xml for creating the EAR file similarly. Some caveats here: notice the usescope for the WAR dependencies (useScope="runtime"). This is critical to get dependencies that are only needed to be included in your WAR versus ones which are already in the App server classpath. However, the problem here is that Antlib doesn't work exactly like to the tune of the configuration specified for a jar in the Maven POM. I had to actually make a code change in the antlib source for it to behave exactly as Maven does for dependencies marked with a scope of provided as well as the optional flag in the POM file. The change is a simple one and is listed below if you want to have this behavior. Check out the source code using the information here.

The code needs to be added to the DependenciesTask.java file:
 (line 198)
for ( Iterator i = result.getArtifacts().iterator(); i.hasNext(); )
{
Artifact artifact = (Artifact) i.next();

// Added condition to recognize optional flag and filter artifacts based on useScope
if (!(artifact.isOptional() && (artifact.getScope()
.equals(Artifact.SCOPE_RUNTIME) || (useScope != null && useScope
.equals(Artifact.SCOPE_RUNTIME)))))
addArtifactToResult(localRepo, artifact, fileSet, fileList);

versions.add( artifact.getVersion() );

The changes I've listed above should help you migrate your application over to Maven easily when you or your organization decides to adopt Maven completely.

Saturday, March 14, 2009

Getting Struts 2.1 to work in Weblogic 10.3 - (contd)

Updates to struts2 convention plugin as well as XWork have been committed by Musachy Barraso based on my observations to help fix Struts2 for Weblogic 10.3. I would suggest you use them. You will need to take the source code for the following classes from the trunk of their src repository:

  • com.opensymphony.xwork2.util.finder.ClassFinder (xwork trunk)

  • com.opensymphony.xwork2.util.UrlUtil (xwork trunk)

  • com.opensymphony.xwork2.util.ClassLoaderUtil (xwork trunk)
    (Based on http://jira.opensymphony.com/browse/XW-672)

  • org.apache.struts2.convention.PackageBasedActionConfigBuilder (Struts2-struts2-convention-plugin trunk)

XWork trunk(http://svn.opensymphony.com/svn/xwork/trunk)
Struts2 convention plugin(http://svn.apache.org/repos/asf/struts/struts2/trunk)


Compile xwork first, install it into your local maven repository before compiling Struts2 convention plugin. The changes checked in will work in Struts 2.1.6 and xwork 2.1.2. While the changes will be present in subsequent versions of Struts2, there are other changes I am looking into for subsequent versions.

Please note here that you will still need to zip the action classes into a jar (that contains a META-INF/Manifest or META-INF/somefile) for the convention plugin to pick up your action classes.

Let me know if you have issues.

- Amit

Sunday, February 22, 2009

Getting Struts 2.1 to work in Weblogic 10.3

Over the last couple of days, I've been looking into the problems of running Struts2 in Weblogic 10.3. Once you wade through the errors however, the resolution is not as complicated. So, the main reason why Struts 2.1 does not work in Weblogic 10.3 (and could be earlier versions) is mainly because of the way Weblogic handles the WEB-INF classes and lib directories. The classes directory is zipped up into a jar called _wl_cls_gen.jar. Struts 2.1 specifically is looking for actions either in the classes directory or in a jar with a "META-INF/Manifest". Unfortunately, the jar created does not have the manifest and Struts2 does not know that it needs to look in a jar for the struts actions. Also, every jar within a WAR file is available as a resource via the zip file protocol, instead of a more standard jar file protocol (a really quirky implementation from BEA on this....:(, shame on them!!!).

So then how does one go about fixing these issues? Here are the steps:

  • You will need to add the META-INF/Manifest file into the classes directory of your WAR. This will guarantee that the _wl_cls_gen.jar is a valid jar that Struts2 will recognize.

  • You will then need to add a struts.xml to your project with the following information:
    <constant name="struts.convention.action.includeJars" value=".*_wl_cls_gen.*"/>
    Note here that with Struts 2.1, you will be using the struts2-convention plugin, and this is an override for one of the constants that the convention plugin uses to look for actions.

  • So far, these steps were pretty easy. Now to a little bit of coding work. You will need to change a small piece of code in the convention plugin. There are 2 pieces. One piece is the change in the plugin's struts-plugin.xml. In talking with Musachy Barroso, a lead developer on Struts, this change has already been committed to the trunk. This change is to allow the convention plugin to accept the zip protocol. The change in the struts-plugin.xml is:
    <constant name="struts.convention.action.fileProtocols" value="jar,zip" />

    The next is the following piece of code in the org.apache.struts2.convention.PackageBasedActionConfigBuilder class:


    private Set fileProtocols;

    @Inject("struts.convention.action.fileProtocols")
    public void setFileProtocols(String fileProtocols) {
    if (!StringTools.isTrimmedEmpty(fileProtocols)) {
    this.fileProtocols = TextParseUtil.commaDelimitedStringToSet(fileProtocols);
    }
    }

    private UrlSet buildUrlSet() throws IOException {
    UrlSet urlSet = new UrlSet(getClassLoader());

    urlSet = urlSet.exclude(ClassLoader.getSystemClassLoader().getParent());
    urlSet = urlSet.excludeJavaExtDirs();
    urlSet = urlSet.excludeJavaEndorsedDirs();
    urlSet = urlSet.excludeJavaHome();
    urlSet = urlSet.excludePaths(System.getProperty("sun.boot.class.path", ""));
    urlSet = urlSet.exclude(".*/JavaVM.framework/.*");

    if (includeJars == null) {
    urlSet = urlSet.exclude(".*?jar(!/)?");
    } else {
    //jar urls regexes were specified
    List rawIncludedUrls = urlSet.getUrls();
    Set includeUrls = new HashSet();
    boolean[] patternUsed = new boolean[includeJars.length];

    //- Changed section begins
    for (URL url : rawIncludedUrls) {
    if (fileProtocols.contains(url.getProtocol())) {
    //it is a jar file, make sure it macthes at least a url regex
    for (int i = 0; i < includeJars.length; i++) {
    String includeJar = includeJars[i];
    String extUrlForm = url.toExternalForm();
    if (Pattern.matches(includeJar, extUrlForm)) {
    // If the protocol is zip, convert to jar protocol
    if ( extUrlForm.indexOf("zip:")!=-1 ) {
    String newUrl = "jar:file:/" +
    extUrlForm.substring(4);
    url = new URL(newUrl);
    }
    includeUrls.add(url);
    patternUsed[i] = true;
    break;
    }
    }
    } else {
    //it is not a jar
    includeUrls.add(url);
    }
    }
    //- Changed section ends
    if (LOG.isWarnEnabled()) {
    for (int i = 0; i < patternUsed.length; i++) {
    if (!patternUsed[i]) {
    LOG.warn("The includeJars pattern [#0] did not match any jars in the classpath", includeJars[i]);
    }
    }
    }
    return new UrlSet(includeUrls);
    }

    return urlSet;
    }

    The piece of code that hasn't been committed and in talking with Musachy Barroso is felt temporary is the conversion of zip protocol to jar in the buildUrlSet method. This hasn't been committed to the trunk and most likely will go in as a broader code change that will affect the XWork library that Struts2 uses.


What I've laid out are fairly simple changes - compiling the struts2 convention plugin should be a cakewalk (maven based). I will keep this site updated as I hear more from Musachy if you are willing to wait it out before trying Struts2 in Weblogic. Let me know if you have problems getting things to work....

- Amit

UPDATE:
The above changes are not required with Struts 2.2. Just put the following in the struts.properties file of the project.
#struts convention property modifications for Weblogic
struts.convention.action.includeJars=.*?/your-web-app-name.*?jar(!/)?
struts.convention.action.fileProtocols=jar,zip

Wednesday, February 18, 2009

Struts2 Annotations, Dojo and Json plugin

Here's a nice way to integrate Dojo, json with the latest Struts2.1 web framework. A point to note here is that Dojo support via a plugin is deprecated in Struts2.1. My example does not use the dojo plugin, but the dojo library directly with the help of the json-plugin from the Google repository.

My Struts2 action looks as follows:

@ParentPackage("json-default")
@Namespace("/testing")
public class AjaxTestAction
{
@Action( interceptorRefs={@InterceptorRef(value="json", params={"enableSMD","true"})},
results={@Result(name = "success", type="json", params={"enableSMD","true"})} )
public String smd() {
return "success";
}

@SMDMethod
public String sayHello(String name)
{
return ("Hello " + name);
}
}


My javascript looks as follows:

//load dojo RPC
dojo.require("dojo.rpc.JsonService");

//create service object(proxy) using SMD (generated by the json result)
var service = new dojo.rpc.JsonService("<%=contextPath%>/testing/ajax-test.action");

// execute remote service call
var deferred = service.sayHello('Amit');

deferred.addCallback(function(result) {
alert(result.toString());
});

That's all you need. Cool eh?? One thing to note above is the url to the action - I'm using the convention plugin - but yours could look different. Have fun with Dojo. To me AJAX has never been easier. I'm not a UI guy, but my application is really looking like a RIA with Dojo toolkit and this JSON-RPC communication with the backend Java Struts2 actions.

- Amit

Tuesday, February 10, 2009

Convert a jar to an optional package

Here's the code I promised I would post to convert a jar pulled out of a Maven repository into an optional package. Note here that jars pulled out of a Maven repository have the following example syntax: examplelib-1.0.0.jar. Given this syntax, the following code manipulates the Manifest of the jar to make the jar compatible with the JEE optional package specifications:

ZipFile oldZipFile = new ZipFile(new File(fileName));
Enumeration entries = oldZipFile.entries();

ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
outFile));

// Loop through entries in the existing jar file
while (entries.hasMoreElements())
{
ZipEntry entry = entries.nextElement();
// Put all other files besides the manifest into the converted jar as is
if (entry.getName().indexOf("MANIFEST") == -1)
{
out.putNextEntry(entry);

InputStream inStream = oldZipFile.getInputStream(entry);
// Transfer bytes from the ZIP file to the output file
byte[] buf = new byte[1024];
int len;
while ((len = inStream.read(buf)) > 0)
out.write(buf, 0, len);

inStream.close();
}
else
{
manifest = new OptManifest();
manifest.setExtName(trueFileName);

// Read the manifest
InputStream inStream = oldZipFile.getInputStream(entry);
BufferedReader in = new BufferedReader(
new InputStreamReader(inStream));
StringBuffer buffer = new StringBuffer();

// Read each line of the manifest file to determine its state
boolean extnNameFound = false;
boolean specVerFound = false;
boolean implVerFound = false;

String line = null;
while ((line = in.readLine()) != null)
{
if ( !specVerFound )
specVerFound = ( line.toLowerCase().indexOf("specification-version")!=-1? true: false );

if ( !implVerFound )
implVerFound = ( line.toLowerCase().indexOf("implementation-version")!=-1? true: false );

if ( !extnNameFound )
extnNameFound = (line.toLowerCase().indexOf("extension-name")!=-1? true: false);

boolean hasOptPkgParam = ( specVerFound || implVerFound || extnNameFound? true : false );

// Add lines that are not optional package parameters
if ( line!=null && !line.equals("") && !hasOptPkgParam )
buffer.append(line + "\n");
}

// Add Optional package information to the manifest
String versionToUse = determineVersion(fileName);
if ( versionToUse!=null && !versionToUse.equals("") )
{
// Assign cleaned up specification version
String specVersionToUse = getSpecificationVersion(versionToUse);
buffer.append("Specification-Version: " + specVersionToUse + "\n");
manifest.setExtSpecVersion(specVersionToUse);

// Assign cleaned up implementation version
String implVersionToUse = getImplementationVersion(versionToUse);
buffer.append("Implementation-Version: " + implVersionToUse + "\n");
manifest.setExtImplVersion(implVersionToUse);
}

inStream.close();
in.close();

ZipEntry newManifestEntry = new ZipEntry(entry.getName());
out.putNextEntry(newManifestEntry);

// Transfer bytes from the file to the ZIP file
out.write(buffer.toString().getBytes());
}

// Close the new entry in the new zip file
out.closeEntry();
}
out.close();


Here's how your can derive the specification and implementation versions:

private String getSpecificationVersion(String versionToUse)
{
StringBuffer specVersionToUse = new StringBuffer();
// Regex expression that only allows digits and periods, and a digit cannot
// be preceded by an underscore
String regex = "(?<!_| )[\\.?[0-9]*]";
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(versionToUse);
while ( m.find() )
specVersionToUse.append(m.group());

if ( specVersionToUse.charAt(0)=='.')
specVersionToUse = specVersionToUse.replace(0, 1, "");

if ( specVersionToUse.charAt(specVersionToUse.length()-1)=='.')
specVersionToUse = specVersionToUse.replace(specVersionToUse.length()-1, specVersionToUse.length(), "");

return specVersionToUse.toString();
}

private String getImplementationVersion(String versionToUse)
{
String implVersion = versionToUse;
int versionIndex = versionToUse.indexOf('-');

// Get the version starting from the hyphen prior to a digit
String regexPattern = "[0-9]";
while ( versionIndex!=-1 )
{
char charAtIndex = versionToUse.charAt(versionIndex+1);
if ( !Pattern.matches(regexPattern, String.valueOf(charAtIndex)) )
versionIndex = versionToUse.indexOf('-', versionIndex+1);
else
{
implVersion = versionToUse.substring(versionIndex+1);
break;
}
}
return implVersion;
}

Sunday, January 25, 2009

A Tabbed Console for Windows

Ok, I forgot to mention one thing - there's a cool little open source project called Console (http://sourceforge.net/projects/console) that you just have to start using. Get the 2.0 development version - its pretty far along that you can use it without any major glitches. This little thing lets you create a tabbed Windows command shell or for that matter even a tabbed cygwin or powershell. Its free.....so what are you waiting for. Get it now, along with notepad++(http://notepad-plus.sourceforge.net/uk/site.htm) if you didn't know about that either.....:)

For example you could load multiple cygwin windows as shown here by pointing to the cygwin.bat file -


Once you have cygwin setup, you could create tabs that spawn off an ssh shell to a server by creating a cygwin modified batch file for connection to that server -
(Example contents of this script)
eg:
@echo off
C:
chdir C:\cygwin\bin
bash --login -i -c "export DISPLAY=localhost:0.0; ssh -X myuserid@remotesvrname"

- Amit

Thursday, January 15, 2009

Recommendations for hard economic times

Its been a while since I've blogged, partly because I've been busy on a JEE project with a pretty extensive scope. Lots of new technologies in use and its interesting to see how the Java enterprise landscape is shaping up with JEE 5 containers now getting adopted for mission critical systems in the enterprise. Here are a couple of observations worth mentioning:

While most people know more about the EJB3 features, very little has been mentioned about optional packages and their use. I've blogged about this before, and as I've seen on my project, optional packages is a worthwhile addition in JEE 5. Only problem here is the conversion of regular libraries to optional packages and I wrote code to automate that which I will post here soon.

Spring 2.5 with annotation support opens up all kinds of possibilities. I didn't like Spring before, but if you extend their annotation framework, sky's the limit to what you can do - like you could create your own code to inject service handles where the service protocol could be proprietary and not just RMI/IIOP as is the case with EJBs. Example would be some legacy services which you want to code to today, but maybe want to convert to EJBs down the line. In such a scenario, one could easily write a dynamic proxy and use Spring to inject it into say a Struts2 action or an EJB. Essentially here, you are converting a legacy service to have an interface that very well could look like an EJB interface and then providing ways to marshal data from the legacy service into Java objects in the dynamic proxy class.

Struts2 is maturing well and its overall quick adoption is making it the framework of choice again for web development ( besides JBoss Seam maybe ).

XStream is a really slick STAX xml parser that makes marshaling XML real easy and real fast to POJO objects. With a couple of tweaks to this library (which I'll post later), you can literally forget about the xml parsing piece in your app.

Finally, if your system requires a distributed caching mechanism, Terracotta may be a worthwhile addition to your enterprise suite. They have lots of good information on their site, and plenty of flexibility in implementation. However, they don't have a good implementation for Web Session offloading even though they state otherwise. Essentially, they support offloading web sessions only if your application doesn't have resources/pages secured via roles (which I just can't see any sense in - aren't all well architected apps in the enterprise secured by roles that limit access to information or pages based on the end user's role). Anyway, its still a great product for distributed caching even without the the web session offloading.

So I end my technology update here for now. Even though the times are hard, technologies abound and it maybe a worthwhile exercise to do your research on these new technologies. If you want to be adventurous, start learning a new language - the one I'm getting my feet wet in is Objective C. It would be useful skill incase your company decides to write an app for the iPhone. If you don't have a Mac (you can invest in it as the times start getting better), then you can play around with Obj C using GNUStep. I'm surprised Obj C doesn't get enough of a mention, because its a dynamic language as well.

Start preparing for better days ahead...

- Amit