I just finished converting our company's back-office application from Tapestry 5.1.0.5 to 5.2.5. I had been putting it off because I knew it would take some time and there was always something to add to the application. The application has functions for sales, customer support and billing and is comprised of more than separate 250 pages (.tml files.)

The conversion took about two days for code changes and testing, plus less than another day for debugging based on user reports. Here are my notes on the conversion, which may help others contemplating a similar upgrade.

First, the new web site is a definite improvement. Secondly, it was not as simple as updating my pom with the new version.

I first updated my pom for the new versions of Tapestry, Chenille Kit and Tapestry Security. There were some typical dependency conflicts that took a while to figure out (Maven is rather stupid.)

The first item I noticed was the changes to Hibernate's API, which were all easy to fix. (All were constant name changes, which I think improved the API.)

My @InjectSelectionModel annotation wouldn't compile, of course, due to the changes to class the transformation API. I do like the new API much better, since it's easier to debug. However, it did take a few hours to get it working since I wasn't familiar with the New Way.

At this point, I tried to start up my web app, but it didn't appear to work. Tapestry wasn't starting up and there was no logging information to tell me why. After a while, I noticed that one library I included used an older version of slf4j and Maven decided the old one was the only one it would include. This made tapestry not startup and not display any error messages to stdout, either. That was sort of frustrating. After forcing Maven to use 1.6.1 and removing the incompatible TapX I was using, it started up. I did google for quite some time looking for a compatible version of tapx-datefield, but failed. Later, someone posted the URL to the mailing list but I had replaced it by then. I'm using the Chenille Kit one, which I like better anyway. However, it can't easily be used in ModalBox dialogs, since it wants to initialize positioning after page load, and it can't do that inside a <div style="display:none">. I ended up adding an "afterLoad" parameter to ModalBox.show and manually convert the t:textfields via "new Control.DatePicker".

The most tedious part was converting the deprecated @Include... annotations with the new @Import one. Also changed the RenderSupport to JavaScriptSupport.

I had some overly clever code for session state objects where I was casting it from the interface to the implementation and calling protected methods from a service. That worked in 5.1, but it causes an exception in 5.2. I just made all methods public on the SSO.

Next up was figuring out why some pages with Chenille Kit's OnEvent mixin when added to select components weren't working right. I use this in several places to update child select components as well as updating zones. In the multiple select case, I settled on using the onCompleteCallback feature of the mixin to re-populate the select lists in JavaScript via JSON returned from the event handler. It's very, very fast and seamless that way. Many of the select components were setting an object property on a parent object, e.g. setting the corporation object on an account object. Oddly, something changed such that in the @OnEvent handler for changes to the select component, the value of the property on the parent object was always null. The first thing I did in the method was to do a query to find all relevant items for the next select component. That caused Hibernate to flush changes, resulting in an exception when it tried to write a null value to a non- null property. I fixed that by explicitly querying based on the key value passed to the event handler. Not sure what changed here, but it is working.

Some AJAX routines didn't work due to a change where JSONObject's toString pretty printed, causing the output to be on multiple lines. I tracked all of those down and either changed it to toString(true) or ended up passing the object directly to functions instead of a string of a JSON object (e.g. in JavaScriptSupport.addScript()).

I ran into several instances where it couldn't find zome t:zones, but adding a normal "id" attribute (in addition to the "t:id" attribute) fixed that. I only needed to do this for some t:zones, however. Continuing with t:zones, I also got the puzzling, "rendered content did not include any elements that allow for the positioning of the hidden form field's elements" exception. Searching google turned up nothing terribly helpful. I ended up moving the t:zone to outside of my t:form, and that fixed the problem.

I also had some instances where I wanted to assign an "id" to a component (in my case a submit button.) I generated like: id="delete-$ {object.id}". In 5.2 my ID was nowhere to be found, and you can't do that trick on t:ids, apparently. I ended up putting in an empty <span> with the id and used Prototype's next() and previous() methods to find my objects.

After I had everything working pretty well, I put it onto the production server where it ran for a few hours and then died with a PermGen exception. Previously, my app would run for months with the 64 MB of PermGen allocated to servers by default. Now, after upgrading to 5.2.5 it wouldn't run for more than a few hours at 128 MB. I ended up setting it to 512 MB, but that just seems outrageous. Is this normal to require 8x PermGen? I haven't made any other change to my app, just those required to upgrade.

Now I guess I get to work on upgrading to 5.3... I see it claims to remove the "suppress redirects" functionality, but I still do use that via a custom annotation that injects in an overridden ActionRenderResponseGenerator and the IMMEDIATE_RESPONSE_PAGE_ATTRIBUTE. I hope that doesn't go away.

Norman Franke
Answering Service for Directors, Inc.
www.myasd.com



Reply via email to