Extreme Java When vanilla Java is not enough

13Mar/070

The quest for the Java Portal – Gridsphere Tests

GridSphere compiled in an zero-error-in-deploy WAR file. But, with my well-known luck and my incredible skill of catch the weirdest bugs on Earth, I got the classic "error-page-that-throws-error" bug. A page throwed an error. This triggers the error page. But, the same error was throwed. This triggers the error page. I love when this happens. My luck was the StackOverflowError. In some EPTTE bugs the error page leads to HTTP infinite redirection.

The origin of the error? This little piece of code:

public synchronized Object deepCopy(Object oldObj) throws Exception {
  ObjectOutputStream oos = null;
  ObjectInputStream ois = null;
  try {
    ByteArrayOutputStream bos = new ByteArrayOutputStream(); // A
    oos = new ObjectOutputStream(bos); // B
    // serialize and pass the object
    oos.writeObject(oldObj); // C
    oos.flush(); // D
 
    ByteArrayInputStream bin =
      new ByteArrayInputStream(bos.toByteArray()); // E
    ois = new ObjectInputStream(bin); // F
 
    // return the new object
    return ois.readObject(); // G
  } catch (Exception e) {
    throw e;
  } finally {
    if (oos != null) oos.close();
    if (ois != null) ois.close();
  }
}

I will not comment the weird "catch e throw e" fragment, but this was the most expensive way to follow the Prototype pattern (deep copy variant) I ever seen. You don't need to be an extreme Java programmer to notice this code will fail on the first non-serializable class found by oos.writeObject. Now, guess what was the exception I got?

java.io.NotSerializableException: org.apache.log4j.Level

After digging into the code, I discovered that GS team knows the use of transient keyword, but didn't used it on BasePortletComponent.java, line 42:

protected Log log = LogFactory.getLog(PortletPageFactory.class);

How this works on Tomcat is a mistery to me. Changing it to a final static attribute and adding some dependencies of dependencies (Jackrabbit needs Lucene and yet another logging implementation - SLF4J), made it work, but not as expected.

Result: an installation page with some really ugly URL rewrite techniques.

<link ... href="/tmp54563GridSphere-exp.war/...css" ...>
<img src="/@GRIDSPHERE_DEPLOY@/...png" ...>

The second one is a side-effect of using my own build script. The first one (along with cool design patterns) gives GridSphere an one-way ticket to my attic.

13Mar/070

The quest for the Java Portal – Gridsphere Compilation

Current version of Gridsphere, 3.0.3, supports Tomcat and Jetty only. Since I'm extreme (-lly nuthead), I've tryed to run it on JBoss. I haven't found any live demo on its site - too bad.

Like Jetspeed and eXo, there's no WAR/EAR/SAR/CAR/etcAR file to download - I must download the source and compile (there's a corrupted, hidden WAR file on server, if you edit by hand the URL of download). Good news is: it uses Ant (not Maven) - that means I does not need to dig into obscure and unstructured XML. IMHO, Maven is good for the main developer, but not for the bug hunters, like me.

As expected, it wasn't straightforward. The steps:

  1. Set CATALINA_HOME to JBoss' server folder - "$JBOSSHOME/server/$instance". Compilation script needs a "lib" folder with JavaEE API. Tomcat 5 stores on "$C_H/common/lib", Tomcat 6 stores on "$C_H/lib". Gridsphere "detects" tomcat version by verifying if "$C_H/common/lib" exists.
  2. This leads to a "$C_H/bin" not found. After some digging in build scripts (I love Ant), I found an strange task that adds Tomcat's bin dir to classpath:
    110
    111
    112
    
    <fileset dir="${appserver.home}/bin/">
        <include name="*.jar">
    </fileset>
  3. Removing it and running the undocumented "ant war" task, creates the web archive.
  4. The archive will not deploy: a ClassNotFoundException nested in a WSException. The class that was not found was the CaptchaServlet. The CNFE is strange, since the class is on "WEB-INF/lib" folder. Since I will not allow new users, I removed the reference from web.xml, but the same exception was thrown, with class GridSphereServlet.

At this point, I got tired of editing files by hand, and I created a NetBeans project to handle everything to me. This is the approach I'm doing with Liferay, and, likewise, I found a lot of dependencies, like Castor (yet another binding framework), JFreeChart, and others.

BTW, JFreeChart deserves some lines. First, I thought one of JFree JAR files were corrupt in GS distribution. I've decided to download it from JFree site and I can't see the documentation because IT IS NOT FREE. Yup. I can download the sources, but not the developer documentation.

And, like every giant project, I found useless things, like Perl5Util. I don't want to discuss if P5U is useful, but use it to replace regex like "\.xml$" with "" is a waste of resources, since String.replaceFirst can do the job.

Finally, I could compile with zero errors (and a lot of unchecked warnings). But it didn't run. I got an weird exception in Castor. After some reverse engineering, since there isn't architetural documentation, I found I need to add six XML files to their correct folders. In original GS structure, these XML lies on a "config" dir, but are copied to different folders by one of the 10 Ant scripts. Copying them made a deployable WAR file.

This is a thumbs up no NetBeans: users will always have an well organized project structure. There are no lost files on lost folders needing a lost task on a lost script to copy them to the correct place.

BTW, the problem was a static Class.getResource returning null. I had to change the code (adding some assertions) to figure it out.

Tip to Gridsphere team: use NetBeans to manage the project...

6Mar/070

Thumbs down to Java Portals

One of the most frustrating things I have tryed to do was to use a Java Portal. I haven't found a JSR-168 compliant portal that could work at all. I have simple needs:

  1. The portal must run on JBoss: I'm renting a shared JBoss server;
  2. I need to put a "virtual-host" tag in every jboss-web.xml: this is how JBoss will map my domain to my applications;
  3. I must have total control over JNDI/JMX naming conventions: "default" names are unacceptable, since other "clients" may want to use the same implementation;
  4. The portal must NOT create any files on the filesystem: JBoss runs with an specific user that does not have write access to my private folders; and
  5. Memory and CPU consumption must be light: obviously.

What I found:

  • JBoss Portal:
    • PrĂ³s: Runs on JBoss (of course) and seems to be light
    • Cons: UGLY. Portal layout is defined with some weird arrows on an weird admin page
  • Liferay
    • Pros: beautiful, lots of AJAX, lots of out-of-box portlets
    • Cons: invasive (needs to create a lot of files), eats CPU and memory like I eat chocolate and creates JMX in runtime (but does not release them on undeploy). "Professional" version (WAR) takes 30 minutes to deploy, "Enterprise" version (EAR) crashed the server (yikes) and requires a lot of customization to run on a shared server
  • eXo
    • Pros: like Liferay, it's beautiful, lots of AJAX, lots of portlets, and have an awesome "desktop" version
    • Cons: I have to compile EVERYTHING and do a lot of digging on its source to customize it to my needs. I simply can't understand its compilation steps. Poor documentation. I could only test tomcat version, since I could not compile an working version...
  • Gridsphere
    • Pros: dunno
    • Cons: runs only on Tomcat

This is amazing... With JavaEE version 5, I thought I could find a portal that could be deployed as easy as 1-2-3:

  1. Download a WAR/EAR file;
  2. Add some jboss-web.xml (to map JNDI and virtual host) - this is my "deployer" role on an JavaEE application lifecycle (as defined by JavaEE specs);
  3. Deploy the file on server.

But step 1 is non-existent on portals like GridSphere. Step 2 becames steps 2.a to 2.zzzz on eXo and Liferay... Every portal wants the user to download a package bundled with tomcat/jboss or to follow this recipe:

  1. Download source (WORA, anyone?)
  2. Compile using Maven (yet-another-build-automation-tool)
  3. Package using Maven (its "custom" tasks does not allow me to add a jboss-web.xml, since even web.xml is generated by it)
  4. Deploy on tomcat (JBoss sometimes)

Why?? Is that hard to create a REAL JavaEE-compliant application that implements JSR-168??? That's a thumb down to Java Portals (and JSR-168)...