14 March 2006

Classpath Gymnastics: JAXP, Java5 and JBoss


Started to get a problem with JBoss and Java5. Not sure what caused it to _start_ occurring. I'm using isolated EAR deployments and excluding all xml-apis and xerces jars from my EARs since all this stuff should be provided by the java runtime.



Anyway, started to get this error:


XPathFactory#newInstance() failed to create an XPathFactory for the default object model: http://java.sun.com/jaxp/xpath/dom with the XPathFactoryConfigurationException: javax.xml.xpath.XPathFactoryConfigurationException: No XPathFctory implementation found for the object model: http://java.sun.com/jaxp/xpath/dom



It turns out you can enable jaxp debugging with the following system property:
-Djaxp.debug=true

This resulted in some useful messages:


08:17:52,874 INFO [STDOUT] JAXP: using thread context class loader (org.jboss.util.loading.DelegatingClassLoader@11c9fcc) for search
08:17:52,875 INFO [STDOUT] JAXP: Looking up system property 'javax.xml.xpath.XPathFactory:http://java.sun.com/jaxp/xpath/dom'
08:17:52,875 INFO [STDOUT] JAXP: The property is undefined.
08:17:52,876 INFO [STDOUT] JAXP: found null in $java.home/jaxp.properties
08:17:52,876 INFO [STDOUT] JAXP: no META-INF/services/javax.xml.xpath.XPathFactory file was found
08:17:52,876 INFO [STDOUT] JAXP: attempting to use the platform default W3C DOM XPath lib
08:17:52,876 INFO [STDOUT] JAXP: instanciating org.apache.xpath.jaxp.XPathFactoryImpl
08:17:52,877 INFO [STDOUT] JAXP: failed to instanciate org.apache.xpath.jaxp.XPathFactoryImpl
08:17:52,880 INFO [STDOUT] java.lang.ClassNotFoundException: No ClassLoaders found for: org.apache.xpath.jaxp.XPathFactoryImpl



Looking at the JAXP sources revealed the problem. In the source for Java5 javax.xml.xpath.XPathFactoryFinder we see this:


debugPrintln("attempting to use the platform default W3C DOM XPath lib");
return createInstance("com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl");


In the log, we see this message: instanciating org.apache.xpath.jaxp.XPathFactoryImpl

com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl != org.apache.xpath.jaxp.XPathFactoryImpl



So why is this? Looking at the JBoss endorsed directory at /lib/endorsed we can see it contains xml-apis.jar which contains javax.xml.xpath.XPathFactoryFinder which overrides the Java5 version. That overridden version tries to lookup org.apache.xpath.jaxp.XPathFactoryImpl but that doesn't exist in Java5; it was renamed. And since I've excluded all xml jars from my EARs, I don't have that class anywhere.



Of course, I'm not the first to have this problem.


I simply renamed the lib/endorsed directory and restarted. This seemed to work, although based on the second link above, there might be more work to do later.

24 February 2006

MacBook Pro vs Dell Pentium M w/ Ubuntu Linux


Matt Raible documents his experience with unwrapping his new Duo Core MacBook Pro.

For grins, I decided to compare his performance measurements with my 10-month old Dell Inspiron 9300 laptop. Obviously this test is far from scientific. Anyway, here are my specs:

Dell Inspiron 9300
Intel Pentium M @ 2Ghz
2Gb RAM
60gb 7200 RPM HDD
Ubuntu Linux 5.10 w/ Kernel 2.6.12-10-686

This laptop cost less than $3K in April 2005. Here is the precise cpuinfo specs:
vendor_id : GenuineIntel
cpu family : 6
model : 13
model name : Intel(R) Pentium(R) M processor 2.00GHz
stepping : 8

And here are my results. Note: I ran each test 4 times.

ant clean package-web
16 seconds
14 seconds
13 seconds
13 seconds

ant setup test-all
1 minute 25 seconds
1 minute 21 seconds
1 minute 23 seconds
1 minute 23 seconds

His results on the new MacBook Pro were 12 seconds for the first test and 1 minute 28 seconds for the second test.

Conclusion? Its all about the file-system and cacheing. Running OS X or Linux greater improves file IO, 2 Gb RAM allows the OS to aggressively cache disk access and a 7200 RPM HDD offers substantial performance improvements when it does have to hit the disk.

In my opinion CPU and clock speed matters the least.

Here is how I reproduced his tests:
Prerequsite:
* Mysql installed and running;
* CATALINA_HOME environment variable pointing to Tomcat 5.5 installation

cvs -d :pserver:guest@cvs.dev.java.net:/cvs login
cvs -d :pserver:guest@cvs.dev.java.net:/cvs checkout appfuse
cd appfuse
ant clean package-web
ant clean package-web
ant clean package-web
ant clean package-web
ant setup test-all
ant setup test-all
ant setup test-all
ant setup test-all

09 February 2006

Maven2 and xdoclet and antrun

I found it very difficult to get the maven2 xdoclet plugin to generate my struts config xml file.

I first tried this

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>xdoclet-maven-plugin</artifactId>
<version>1.0-alpha-2</version>
<executions>
<execution>
<configuration>
<tasks>
<webdoclet destdir="target/generated-sources/xdoclet" mergedir="src/main/xdoclet" verbose="true">
<fileset dir="src/main/java"/>
<strutsconfigxml validatexml="true" version="1.1" destinationFile="struts-config.xml" />
</webdoclet>
</tasks>
</configuration>
<phase>generate-sources</phase>
<goals>
<goal>xdoclet</goal>
</goals>
</execution>
</executions>
</plugin>


which procedded to generate an empty struts-config.xml (well, it merged in my merge files but I had no actions or forms). It turns out that since my Forms and Actions inherit from a sub-class defined elsewhere, that class must be available in the classpath of the plugin. Unfortunately, there does not seem to be a way to modify a plugin's classpath.

I tried adding dependencies to the plugin, but none of that worked.

I tried the xdoclet2 maven2 plugin; it produced garbage struts-config.xml files; it seems to screw up the merge files.

I finally settled on using the antrun plugin to use the ant tasks. This allowed me to modify the webdoclet's classpath:

<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<configuration>
<tasks>
<taskdef name="webdoclet" classname="xdoclet.modules.web.WebDocletTask" classpathref="maven.compile.classpath" />
<webdoclet destdir="${project.build.directory}/generated-sources/xdoclet" mergedir="src/main/xdoclet" verbose="true">
<fileset dir="src/main/java" />
<strutsconfigxml validatexml="true" version="1.1" destinationFile="struts-config.xml" />
</webdoclet>
</tasks>
</configuration>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>

What do you know, it worked!

07 February 2006

Struts Menu - Velocity Rendering


Struts Menu Tag Library - Overview

I recently started using Struts-menu to replace a home-grown menu system. It works well, although there were a couple of small speedbumps along the way, mostly with the Velocity rendering:

  1. Velocity rendered didn't invoke my custom permissions adapter for all menu items; it only checked one level deep.

  2. The Velocity renderer modifies the application-scoped menu definition when checking permissions; thus if permissions are on a per-user (i.e. per-session) basis, each user trashes the other users' menus.

  3. I also found that I couldn't really define a menu that was a Struts action in another module; the menu always appended the current module to the generated link.


To solve (1) I modified the VelocityMenuDisplayer to recurse into all levels. I submitted a JIRA issue with a patch: http://issues.appfuse.org/browse/SM-2

To solve (2) I defined a Session-listener (very similar to MenuContextListener) that loaded the menu definition into session scope. The taglib is already coded to find the menu definition at the lowest scope, so it picked it up from there.

To solve (3) I added a "module" attribute to the menu and modified the taglib to compute the URL with the module (if any). I submitted a JIRA issue with a patch: http://issues.appfuse.org/browse/SM-1

Here is my session-scoped listener. There is a lot of duplication with the MenuContextListener class.


import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import net.sf.navigator.menu.MenuRepository;
import net.sf.navigator.util.LoadableResourceException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* Session listener for loading Struts-menu into session.
* <p>
* This is necessary since the Velocity displayer trashes the menu based on permissions.
* Thus we can't store it in application scope.
* </p>
* @author Tim Morrow
* @since Feb 6, 2006
*/
public class MenuSessionContextListener implements HttpSessionListener {

private static final Log log = LogFactory.getLog(MenuSessionContextListener.class);

private static final String DEFAULT_MENU_CONFIG_LOCATION = "/WEB-INF/menu-config.xml";

public void sessionCreated(HttpSessionEvent event) {

if (log.isDebugEnabled()) {
log.debug("Starting struts-menu initialization");
}

String menuConfig = DEFAULT_MENU_CONFIG_LOCATION;

// check for menuConfigLocation context-param
String override = event.getSession().getServletContext().getInitParameter("menuConfigLocation");
if (override != null) {
if (log.isDebugEnabled()) {
log.debug("using menuConfigLocation: " + override);
}
menuConfig = override;
}

MenuRepository repository = new MenuRepository();
repository.setLoadParam(menuConfig);
repository.setServletContext(event.getSession().getServletContext());

try {
repository.load();
event.getSession().setAttribute(MenuRepository.MENU_REPOSITORY_KEY, repository);

log.info("struts-menu successfully loaded into session scope from " + menuConfig);

} catch (LoadableResourceException lre) {
log.fatal("Failure initializing struts-menu: " + lre.getMessage());
}
}

public void sessionDestroyed(HttpSessionEvent event) {
event.getSession().removeAttribute(MenuRepository.MENU_REPOSITORY_KEY);
}

}


Usage is the same as the servlet context MenuContextListener:

<listener>
<listener-class>MenuSessionContextListener</listener-class>
</listener>

23 December 2005

Hibernate assertion failure

Q: So what is the likely cause of the following Hibernate exception:

[AssertionFailure] (JMS SessionPool Worker-2:) an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session) org.hibernate.AssertionFailure: null id in entry (don't flush the Session after an exception occurs)

A: After much scratching of heads it turns out it was as simple as a mandatory field not filled in on an object being saved.

So why did we get that message? I've no idea. Maybe its due to one or more of these factors:
* We're using MS SQL and the table had an identity column
* We're using Spring's TransactionSynchronizationManager to manage the transaction
* There is some logical explanation based on a thorough understanding of hibernate's internal mechanisms as to why this particular error means a missing mandatory field. I looked at the source code, but couldn't figure it out.

Its likely that the original error got buried somewhere and this one was a side-effect. However we couldn't see anything in our logs.

Well, now we know.

01 November 2005

Struts w/ Ajax and submitting editable dynamic content

I wanted to dynamically render a portion of a page using AJAX. I started by reading Sprinkle Some AJAX Magic in Your Struts Web Application by Paul Browne, but felt this fell short for a number of reasons:

  • I wanted to minimize the amount of Javascript I had to maintain for perform the round-trips

  • I wanted to use Struts actions and JSP to render the dynamic content

  • My dynamic content contains input elements which must be submittable and maintained across submits


I took a quick look at the prototype.js and decided to use it for performing the asynchronous stuff. It doesn't seem to have much documentation, but browsing the Javascript source helped. I found Using prototype.js v1.3.1

Modifying the main JSP page to dynamically include some content was easy. I had a select list which, when a new value was selected, would asynchronously load some data then present it in a table using displaytags. The values would be editable. Here is what it looked like:

<html:select property="configureApplicationId" styleid="configureApplicationId">
<html:optionscollection property="configureApplicationOptions">
</html:select>
<html:errors property="configureApplicationId">

<div id="configurationSettingsId">
</div>

<script type="text/javascript">
new Form.Element.EventObserver(
'configureApplicationId',
function(element, value) {
new Ajax.Updater(
'configurationSettingsId',
'myAction.do',
{parameters: Form.Element.serialize(element)}
)
}
);
</script>

This sets up an event listener on the change to the configureApplicationId which invokes an asynchronous HTTP post to myAction.do with the parameter configureApplicationId=value. On return, it replaces the content of the configurationSettingsId element with the result of the invocation.
The action is a regular Struts action which populates a form with the given parameter. The action forwards to a plain JSP page which renders the form content. Since the JSP page doesn't contain an <html:form> Struts tag, you have to add the name="formname" attribute to each <html:...> input element as so:

<html:hidden name="configureForm" property="configSettingsSerialized" />

Now when we submit the form after dynamically rendering a portion, the dynamic portion will disappear unless we take steps to re-render it. We could use some more javascript to do another asynchronous round-trip, but that usually has a noticable visual delay. I chose to render the dynamic portion in-line.
To do this we need to make available the same Struts ActionForm that is normally instantiated during the asynchronous round-trip and include the JSP page that is normally forwarded to during the asynchronous round-trip.

To do this, I added a method to the main page form which returned the ActionForm needed for the rendered portion and did a JSP include:

<div id="configurationSettingsId">
<c:set scope="request" var="configureForm" value="${mainForm.configureForm}" />
<jsp:include page="/path/to/jsp/myPage.jsp" />
</div>
<
Of course, for this to work, the main form must be able to populate the sub-form's values such that they can be rendered by the included JSP. That is simply a case of adding the appropriate setters to the main form, or including the same ability to setup the data as the other Action.

28 October 2005

Issues Upgrading from Middlegen hibernate 2 tools to hibernate 3 tools

Reason for move

The hibernate tools shipped with Middlegen 2.1 generates equals/hashcode based on the ID of the object. This is a well-known issue that makes it impossible to add multiple new objects in a Set. I haven't had this problem up to now.

I tried adding all of the following to the ant task:
  • equalsHashcode="true"
  • columnsIncEquals="true"
  • columnsIncToString="true"
It seems to generate toString() correctly, but equals() and hashcode() still do not contain all fields. The hbm files do seem to contain the correct meta-data though.

<update>
It appears the version of hibernate-tools.jar that I'm using was old. I downloaded hibernate-extensions-2.1.3 and used the hibernate-tools.jar from that. It seemed to solve the equals-hashcode issue. The remainder of this post servers as a reminder of what I need to do to upgrade to hibernate 3 tools...
</update>

I figured I'd try to move to hibernate 3 tools.

Finding Hibernate 3 tools

I went to the Hibernate Tools page and clicked Download. The version listed there is for Hibernate 3.1. (I tried it with hibernate 3, but it doesn't work; a particular class is missing a method). I'm not prepared to upgrade to Hibernate 3.1 yet as it is not officially released.

I tried the All Downloads for Hibernate page. It seems the latest Hibernate 3.0.x based tools is hibernate-tools-3.0.0.alpha4a.zip. It looks like the they're only going to release hibernate tools on 3.1 so the alpha4a is the last one for hibernate 3.0.

I had to switch out the task with a task as documented on the Hibernate Ant Tools documentation.

Problems Encountered
  1. Classes no longer have full constructors. This mainly caused problems in unit tests; I often populate composite keys using the constructor. All this code needs fixed.
  2. boolean types in the hbm file seem to get mapped to Boolean objects instead of primitives. This probably makes sense, but it is code that needs fixed and tested. The getter signatures have changed from boolean isSomething() to Boolean getSomething().
OK. So the constructor issue is mentioned on the Hibernate Forums. It looks like there are bugs in toString and equals/hashcode that have only recently been committed to CVS and probably only available in the Hibernate 3.1-based tools. Hmm... I think I'll forget about the Hibernate 3.0.x-based tools for now.


25 April 2005

Installing TIBCO on Ubuntu

Needed to install TIBCO EMS on Ubuntu (Hoary Hedgehog). However, running the installer (Installshield) failed with an error about libstdc++

The problem appears to be with Installshield wizard requiring an old version of libstdc++.

I followed the steps in the post Debian hath been defeated and it installed successfully

23 April 2005

Ubuntu Laptop - Playing DVDs

I stuck in the original Matrix DVD.
Totem DVD player started up and promptly hung.
I killed it, restarted it, tried to play disc again. It hung.

Used Synaptics package manager to install totem based on xine (it appears default totem is based on gstreamer).

Fired up Totem, went to play DVD, it told me it was copy protected and was I trying to play a protected disc without libdvdcss?

Couldn't find libdvdcss on Synaptics package manager.
Googled for libdvdcss, installed it.
Fired up totem again.
Played disc. It worked!

DVD title menu works
Plays full screen, audio works.

Success.

22 April 2005

Installing Cisco 3000 VPN Client

A. Download VPN client software for Linux
1. Download it
2. Unzip it to a temp directory

B. Get required Linux headers and gcc

1. Determine Kernel Version
uname -r
2.6.10-5-386
2. Select System --> Administration --> Synaptic Package Manager
3. Select Development from tree
4. Find linux-headers-2.6.10-5-386 (it _MUST_ match the kernel version)
5. Check it, OK any dialog boxes
6. Find gcc, check it, OK any dialog boxes
7. Click Apply, confirm dialog box
8. Close package manager

C. Compile VPN client

1. Open a console window and change directory to the unzipped VPN client
2. Run the install script as super user
sudo ./vpn_install
3. Accept the defaults. It should automatically detect the Linux headers you downloaded. They are linked from a number of places, including
/lib/modules/2.6.10-5-386/build
4. It will compile successfully
5. Change directory to where the VPN client tarball extracted the bundled profiles (*.pcf files), if any
6. Copy those files to
/etc/CiscoSystemsVPNClient/Profiles/
D. Start VPN client

1. The VPN client will start when the system boots. However, start it manually for now by executing:
sudo /etc/init.d/vpnclient_init start
2. Run the client with
vpnclient connect
where is one of the profiles, without the .pcf extension

3. Enter your username (username part only, not your full email address)
4. Enter your SecurID password+token

It will connect. That console window will remain busy. You can put the job in the background.

Ubuntu Laptop Install - Part 3

Unhibernate

Powered up the computer.
Prompted me to login.
It restored the windows I had open previously. Nice.

Summary

* Sound works
* Screen works at max resolution (still need to see up upgrading driver)
* Hibernate works
* Wired and wireless networking works

Seems pretty successful so far

21 April 2005

Ubuntu Laptop Install - Part 2

Continuing the Installation

I selected my time zone
I specified the user account
Ubuntu finished telling me the first stage of installation is complete
It ejected my CD and continued to reboot the computer

Booting

Watched all the messages scroll by on the boot. Everything said [ok]. Seems promising.
It then appeared to unpack a bunch of Debian packages Scrolls by too fast to read. This seems to be the slowest part of the installation by far. The hard drive is (quietly) working hard.

It appears to be done. I now have an Ubuntu logo taking up most of the screen and its prompting for my username. The screen resolution seems good. A good sign.

I logged in. Some kind of tree-hugging waterfall, birds and windchimes sound plays. At least the sound card works.

I went to System --> Preferences --> Screen resolution. Sure enough, 1920x1200. Nice.

Configuring Wireless

I pulled out my network cable.
I went to System --> Administration --> Networking
If I understand it correctly in order to configure multiple wireless networks (or perhaps multiple networks altogether), I need to assign each to a different location.
I selected the Location dropdown and choose Create New and called it "Home"
I clicked on Wireless Connection and clicked Properties
I checked on This device is configured
It detected my wireless network, so I selected its ESSID from the dropdown
I entered the wep key. I keep it on a USB stick which, when I plugged into the laptop, popped up on the desktop.
Selected DHCP and hit OK
The Wireless Connection was still selected so I clicked Activate; it popped up a dialog box telling me it's Activating interface "eth1". It took a minute or too before disappearing.
I switched the Default gateway device as eth1. I think this is what tells it which one to use in this profile.
Clicked OK to get rid of the Network settings dialog, although it doesn't seem to be going away. There is a spinning cursor when I hover the mouse over it, but the rest of the system seems fine. Hmm, still there after a few minutes.

I pulled up Firefox and went to google.com. It didn't work.
I clicked on the little network icon on the top-right of the screen and typed into the dropdown "eth1". It switched to that.
Firefox works now.


Standby?

I closed the lid. Opened it up. I was left with a console login prompt.
I pressed a few random function keys.
The computer powered off.
Powered it back on. Not sure what happened.
Its trying to boot. It came back up with a fresh login. It really did power off.

Hibernate?

I went to System --> Logout and selected hibernate.
It seemed to hibernate. Powered down.

Ubuntu Laptop Install

Notes on installing Ubuntu Linux (Hoary HedgeHog)

Here is what I'm installing on:
Dell Inspiron 9300
2Gb DDR2 533Mhz
2.0Ghz Pentium M
17" WUXGA (1920x1200)
60Gb 7200rpm HDD
256Mb Nvidia Go 6800
Ethernet and b/g wireless
Bluetooth

Getting started

* I download and burned Ubuntu Install CD
* Restarted computer hitting F12 to open boot menu and select boot from CDROM

Installation Process

* Answered various questions about Location, Language Keyboard

Configure the network
It found two network cards: eth0 (the 10/100 card) and eth1 (the wireless card) and wants me to pick which one to use while installing Ubuntu. Since I'm not plugged in, selecting Wireless (I have the WEP key)

It failed to find my access point, so I entered the SSID
I entered the WEP key
It tried to configure with DHCP, but failed. Hmm.. may have to plug in
I plugged into the network, hit Go Back and picked eth0; OK, that worked
I entered the hostname

Partion disks
I've been trying to decide whether to install a dual boot system or a Linux only system.
Since this is a brand new laptop, and I've not spent any time tweaking windows, I figured I'd blow away Windows and partition the whole thing for Linux.

For good measure I screwed around with resizing the existing Windows partition. I halved the size. The installer told me it shrunk, but I didn't try rebooting to see if it still worked.

I went back to guided partitioning and selected Erase entire disk
Since this is a personal machine, single user, I accepted the default of a 57Gb / ext3 partition and a 2.5Gb swap partition. I'm sure there is a superior filesystem to ext3 that I should be using, but I don't feel like researching it.

There also seem to be options for installing the Logical Volume Manager, but I'm thinking I don't need that.

I committed the partition and Ubuntu continued with the installation.