Matt RaibleMatt Raible is a writer with a passion for software. Connect with him on LinkedIn.

The Angular Mini-Book The Angular Mini-Book is a guide to getting started with Angular. You'll learn how to develop a bare-bones application, test it, and deploy it. Then you'll move on to adding Bootstrap, Angular Material, continuous integration, and authentication.

Spring Boot is a popular framework for building REST APIs. You'll learn how to integrate Angular with Spring Boot and use security best practices like HTTPS and a content security policy.

For book updates, follow @angular_book on Twitter.

The JHipster Mini-Book The JHipster Mini-Book is a guide to getting started with hip technologies today: Angular, Bootstrap, and Spring Boot. All of these frameworks are wrapped up in an easy-to-use project called JHipster.

This book shows you how to build an app with JHipster, and guides you through the plethora of tools, techniques and options you can use. Furthermore, it explains the UI and API building blocks so you understand the underpinnings of your great application.

For book updates, follow @jhipster-book on Twitter.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.
You searched this site for "jsps". 109 entries found.

You can also try this same search on Google.

Trails - like Rails, but with Tapestry, Spring and Hibernate

I've been thinking about Rails ever since I wrote a post about it on Monday. The main reason is because of Dion's comment:

Matt - You should follow the lead and do a video of setting up a simple app using AppFuse.

This might sounds like a good idea, but if I did it right now in AppFuse's current state, it'd be a disaster. The reason? Because you have to manually create a whole bunch of classes to do CRUD on a database table. Here's a list of new classes needed for adding a new "person" table.

  • model.Person
  • dao.PersonDAOTest
  • dao.PersonDAO
  • dao.hibernate.PersonDAOHibernate
  • service.PersonManagerTest
  • service.PersonManager
  • service.impl.PersonManagerImpl
  • webapp.action.PersonActionTest
  • webapp.action.PersonAction
  • web/pages/personList.jsp
  • web/pages/personForm.jsp

The last two JSPs can be generated, but that's still a buttload of classes (9) just to CRUD (and test!) a database table. Not too mention all the files you need to edit for Spring and i18n.

  • dao/hibernate/applicationContext-hibernate.xml
  • service/applicationContext-service.xml
  • test/web/web-tests.xml
  • web/WEB-INF/classes/ApplicationResources_en.properties
  • web/WEB-INF/menu-config.xml

Result: to CRUD a database table using AppFuse you have to create 11 new files and modify 5 existing files. 16 files. What a beotch, huh? If I made a video of this - it'd be 20 minutes long! While this might make AppFuse look silly, it's really more of a symptom of the patterns we have in J2EE and how we're supposed to architect our apps. 3 tiers, test-driven, loosely-coupled and internationalized. Of course, if I was focused on fast and efficient, I could do this all with 1 JSP and JSTL's SQL tags. Everyone would slap my hand for not following patterns, but I'm willing to bet it'd work just as well and be just as fast. But I digress.

There have been a fair amount of requests (and some patches submitted) to generate and modify all of the files listed above. For the most part, I've frowned upon adding such a feature because I think if folks can run "ant generate -Dmodel=Person" - they'll end up with a whole bunch of code that they know nothing about. Sure there's the tutorials, but folks will quit reading those. Instead, they'll create a whole slew of POJOs (maybe even using Middlegen) and run "ant generate" on all 50 of them. Poof - now they've got 550 new files to maintain. Talk about a maintenance nightmare. Even worse - a support nightmare for me.

Nevertheless, if I wanted to create a cool video for AppFuse, I'd spend a few days writing this code-generation engine. Then I could show how you could create the data, service and web layer (including UI) in a matter of seconds. It'd be cool and folks would dig it. I'm still considering it, but I'm also leary of the resulting support fiasco. Maybe I could just say "use at your own risk". ;-)

A while back, I saw Erik Hatcher suggest a better solution than code-generation. I can't remember what he called it, but it was something like "meta-data dynamic rendering". The idea is that your application reads the metadata of a table (or POJO) and renders the appropriate UI for it. I loved the idea as soon as I heard it. I've always wanted a way to dynamically render the UI rather than writing HTML. Of course, I still want the ability to edit the templates and HTML since I fancy that sort of stuff. I don't like writing HTML for each row in a form, but I do like tweaking the HTML and CSS to look good.

Earlier this week, I saw the concept in action with Rails and its demo. IMO, something like Rails would never fly in Java because it appears to be tightly coupled to the database and only MySQL, PostgreSQL, and SQLite are supported. The Java community always seems to pride itself on database abstraction, partly due to JDBC and its ability to connect to anything that has a JDBC Driver. Ruby will probably catch up someday, but right now it appears to be looking for something like JDBC.

Then along comes Trails, which made me smile earlier today when I first read about it on the tapestry-dev mailing list.

I've been working on a project called Trails that uses Tapestry quite heavily and I thought it time to start soliciting feedback. Trails is a domain driven development framework that uses Tapestry, Spring, and Hibernate. Trails is very much in it's infancy, but the current version is functional and should give people a rough idea where I am heading. It's my first real forray into Tapesty and I have really found Tapestry a joy to work with.

Trails is very much like Rails, except that it doesn't talk directly to a database table. Instead, it talks to your domain objects that you mark up with XDoclet/Hibernate tags. To test it out, I dropped a User.java file into the org.trails.demo package, marked it up with XDoclet and deployed. It didn't work at first because the .hbm.xml files are explicitly listed in Spring's applicationContext.xml. I changed the "sessionFactory" bean to use the following and wammo - success! I could list and CRUD the table that my User object was mapped to.

  <property name="mappingDirectoryLocations">
    <list>
      <value>classpath:</value>
    </list>
  </property>

Trails is very cool, and I'd love to incorporate it into AppFuse or Equinox. Does an LGPL license allow me to do this? The one problem I can see with adding it is that it's specific to Tapestry and Hibernate, which doesn't always suite folks. I think developers might be willing to change because this solution will vastly improve their development productivity, but who knows. I think the best solution would be to offer this option in AppFuse/Equinox, but also offer the current manual and code-generation options. The holy grail would be the ability to plug in iBATIS or JDO instead of Hibernate. In addition, using Struts, Spring, WebWork or JSF instead of Tapestry would have folks clammering to use this stuff.

Mad props to David Heinemeier Hansson and Chris Nelson - you guys are developing awesome software.

Posted in Java at Oct 29 2004, 01:50:30 AM MDT 22 Comments

Comparing Web Frameworks: Presentation Outline

My Comparing Web Frameworks outline is due to the ApacheCon Planners today. I spent a couple of hours putting it together this morning and I'm mostly done. In reality, I know that I won't cover the things that everyone wants to know. In an ideal world, I could simply target the presentation towards people like me: they know Struts but want to learn more about the other frameworks. Before diving in, they want to know some things to watch out for.

The problem with doing that is writing for people like me sometimes backfires. I'd like to make it an advanced presentation, but I'm not an authority on all 5 frameworks - so that seems a bit far fetched. However, if I could add Tapestry and JSF support to AppFuse in the next two weeks - I'd have a lot more experience to refer to. But that ain't gonna happen - the first update chapter for Spring Live is due next week. OK, enough rambling - here's the slide titles I've come up with. So far, it's 22 slides and shouldn't grow any since it's supposed to be finalized when I submit it.

  • Who is Matt Raible? My background, open source involvement, etc. Trying to give myself some credibility. ;-)
  • Framework Experience: Long time Struts user, used Spring since end of last year, developed apps with other three this summer. AppFuse supports Struts, Spring MVC and WebWork; Tapestry and JSF coming before end of year.
  • Meet the Candidates
    • Struts: Pros and Cons. Pretty much a standard, lots of examples, HTML tag library kicks ass. ActionForms kinda suck, can't unit test (only StrutsTestCase integration tests), Mailing list is swamped.
    • Spring MVC: Nice lifecyle (for overriding binding, validation, etc.) integrates with many view options seamlessly, IoC for easy testability. Not widely used, requires lots of code in JSPs, almost too flexible (no parent controller for SimpleFormController and Controller).
    • WebWork: Simple architecture, tag library easy to customize, interceptors are pretty slick. Documentation only recently written, few examples, client-side validation needs work.
    • Tapestry: Very productive once you learn it, templates are HTML (great for designers), healthy project. Documentation very conceptual, rather than pragmatic (lots of "read the book"). Steep learning curve, few examples. Impossible to test - page classes are abstract.
    • JSF: J2EE standard (lots of demand and jobs), fast and easy to develop with, rich navigation framework. Tag soup for JSPs, a bit immature (doesn't come with everything), no single source for implementation.
  • Controllers and Views: Show controllers for all 5 frameworks and template page (JSP/HTML) for forms. Give URL to download sample apps.
  • List Screens: How easy is it to integrate a sortable/pageable list of data? Struts, Spring and WebWork can use tag libraries like the display tag, value list or data grid. Tapesty has contrib:Table. JSF has dataTable component that requires custom logic for sorting (let me know if this has changed).
  • Bookmarking and URLs: Struts, Spring and WebWork give you full URL control. Tapestry has ugly URLs. JSF does a POST for everything. Conclusion: first 3 play nicely with container-managed authentication. Tapestry doesn't (although there's a patch for pretty URLs). JSF is not friendly to bookmarking.
  • Validation: Struts and Spring MVC can use Commons Validation - a mature solution. I've heard it's architecture is ugly, but who cares - it works! WebWork uses OGNL which allows powerful expressions. Client-side support is new and maturing. Tapestry has robust validation w/ good messages out-of-the-box. JSF has ugly default error messages, but is the easiest to configure.
  • Testability: Struts you can use StrutsTestCase, which requires web.xml and struts-config.xml. WebWork and Spring allow easy testing with Mocks. Spring has a spring-mock.jar that can be used for mocking the Servlet API with any framework (it's not Spring-specific). Tapestry is impossible to test because page classes are abstract. The argument I've hard is "you don't need to test them b/c they're so simple." JSF page classes can be easily tested and actually look a lot like WebWork Actions (except they don't extend anything).
  • Success Messages: The easiest way to solve the duplicate-post problem is to redirect after doing a POST. Struts is the only framework that allows for success messages to live through a redirect. It's fairly easy to get i18n messages in a Struts Action. Spring has nothing "built-in" for success messages. Long classnames / method arguments make it kinda clunky to get messages. WebWork has the cleanest way to get messages using a simple "getText()" method. Both require a custom solution to make your messages live through a redirect. Tapestry requires you to throw an exception to redirect and it still doesn't redirect (I haven't solved the duplicate post problem with Tapestry). JSF requires lots of code to get a ResourceBundle, but you could use Spring's IoC to inject a "messageSource" bean, making it a lot easier. JSF requires a custom solution for messages to live past a redirect.
  • Spring Integration: Supported by all, sample apps have examples (quick slide).
  • Internationalization: JSTL's <fmt:message> tag makes it easy in all JSP-supporting frameworks. Too bad there's not a standard for getting bundles in controllers and setting messages. Struts, Spring and JSF encourage you to use one ResourceBundle for all messages while WebWork and Tapestry encourage separate files for each page/action. This strategy is probably better for large teams.
  • Page Decoration: I used to be a huge advocate of Tiles (using it since August 2001). SiteMesh is much easier to setup and use. Tiles can be used in Struts, Spring MVC and JSF. SiteMesh can be used with all 5 frameworks. In fact, the sample app uses the same SiteMesh setup for all w/o changing a thing. SiteMesh requires no maintenance, Tiles requires you add entries to tiles-config.xml whenever you add a new page.
  • Tools: Struts has lots of tools available for it and even frameworks built on top of it (i.e. Beehive's NetUI/PageFlow). Spring has Spring IDE which is really just an XML tool for validating your bean and their relationships. WebWork has none (that've been released), but it is on the roadmap. Tapestry has Spindle, which is great for code-level programmers. It doesn't do drag-n-drop, but it does validate relationships between files. JSF has lots - but all of them cost money and tend to hook into proprietary app servers.
  • Business/Marketing: Struts has lots of jobs and is well-known in the industry. A lot of enterprise applications are based on it. Spring is getting more press, but mostly because of the framework's other features. Knowing the rest of Spring is more valuable than Spring MVC. WebWork is gaining ground, but it's still virtually unheard of on job boards. If you do get gigs using WebWork, it's likely word-of-mouth and you'll probably be working with some pretty smart people. Often used with Spring and Hibernate. Tapestry needs more marketing - no one has heard of it, hence no jobs exist for it. JSF is quickly becoming the most popular. Want to stay employed? Learn JSF. The best reason: so you can back up your arguments on why you do/don't like it.

    Dice Job Count: Struts (1006), Spring (15 - not a very good name for searching, used "spring framework"), WebWork (14), Tapestry (9), JSF (147).

  • My Opinion: You have to come to the conference for that ;-). Slides should be posted following my presentation.
  • Resources: Link and such for downloads, frameworks and tools mentioned.

Comments, suggestions, like it/hate it? Let me know. I expect to send this in in a few hours - so you'd better be quick!

Posted in Java at Oct 15 2004, 12:02:03 PM MDT 22 Comments

I don't hate JSF

I'm still amazed by all the traffic and comments received by my experience with JSF. In some cases it feels like I insulted some of these guys wives or something. Here's some good quotes from the comments:

The less than positive experience is because Matt wants to write an app while asking a million questions all over mailing lists and not investing the time to learn.

I think you're too in love with struts to have a clear sight on what's going on.

Pick your poison and stop bad-mouthing others because you don't get.

I admit that I was a bit harsh on JSF in my post. Here's why. I developed 5 simple apps this summer, all doing the same thing with different frameworks: Struts, Spring MVC, WebWork, Tapestry and JSF. All of them hooked into the same backend, which was Spring+Hibernate. I had a learning curve to overcome with WebWork, Tapestry and JSF. I already knew Struts and Spring MVC, so those versions where easy to develop with. Of the three (WebWork, Tapestry and JSF), I was able to complete the JSF version the fastest. It took me a 1 1/2 days for WebWork, 3 days for Tapestry and 1 day for JSF. Or so I thought.

The JSF app was pretty close to being finished, but I was missing one thing - a sortable/pageable table. And I hadn't written the test for my page classes yet. This was Wednesday. I made a post late that night on how much I liked the JSF-Spring integration library. If you'll notice in this post, I mentioned that I got the displaytag to work too (almost). I found that I had to use an empty tag to pull the list of users from my page bean into the request, and then the displaytag could render the list. This was almost perfect, but the <h:commandLink> didn't work, so I had no way of editing a record from the table.

At this point, it all started to break down - and my frustration began. I was sooo close to completing this application and so far (even with the minor snags I'd hit) it was the easiest one to develop. Juergen left a comment stating that Spring's core now had a DelegatingVariableResolver in its core. So I figured I'd make my application more "pure" and use that instead of the JSF-Spring integration library. This was my first mistake. The DelegatingVariableResolver from Spring didn't work with Sun's RI and only worked with MyFaces. I figured it wouldn't be hard to switch (JSF is a standard, right?) - so I went to it the next day. I'd also heard that MyFaces had a sortable/pageable dataGrid - so the switch seemed like a good idea.

The first bump in the road was that while the DelegateVariableResolver worked, MyFaces reported an error. The Spring guys blamed MyFaces and MyFaces blamed Spring. Result: it's a bug that no one will fix. I spent a couple hours being a good open-source citizen by 1) trying to fix it and 2) reporting it. The second bumb in the road was that I had to change a few things that worked in the RI but didn't work in MyFaces (I forget what now). However, this wasn't so bad since it also fixed a couple of issues.

Next I tried to use MyFace's <x:dataTable> to replace the display tag. Since it had a sortHeader component, I figured this would be easy. It turns out this component requires you to implement custom logic in your page bean. Things might've changed since July, but I doubt it. Lastly, aftering getting everything to work, I went to work on writing a jWebUnit test to test it all. While doing so, I found that it was almost impossible to test my edit screen. With other frameworks, I could specify a URL with a record id and then proceed to change form fields and push buttons. With JSF, there was no easy way to determine the URL. The rendered UI used heavy JavaScript and when you clicked on a link in the dataTable, this called a JavaScript function. I have no problems with JavaScript and I think it's a great technology. However, the current UI testing frameworks I use (jWebUnit and Canoo WebTest) use the Rhino JavaScript library (js.jar) and it sucks at JavaScript. It reports errors where there are no errors - and hence, I tend to exclude the JAR and disable JavaScript support in my tests. If there was better JavaScript support in these testing frameworks - I'd likely have to problems with JSF's "I post for every link" mantra.

In most of my development life, I've most often been a framework user rather than a developer. This means that if I find problems, I don't extend the framework - I look for other solutions. Call me whatever you like, but I'm just trying to get my job done and the project completed. I don't want to mess with the internals of a framework to make that happen. With JSF (and Tapestry too), you have to be more of a framework developer. You have to be willing to create your own components. These frameworks are designed as component frameworks and they want you to extend them. That's one of the major points of their architecture.

So here I am again, posting about JSF - which will undoubtably get an incredible amount of hits just because it has "JSF" in the title. Why? Because you can't ignore JSF. Even if you don't want to develop with it - there's going to be tons of jobs that require JSF in the near future. In fact, there are already quite a few. Surprisingly enough, in my job search last month, I has a more opportunities for JSF (and WebWork suprisingly enough) than Struts. Finding a Tapestry gig - good luck. Of course, I'm a consultant, not an employee - so I don't often don't get to choose frameworks for companies.

I think JSF is an immature technology that will rapidly mature. I hope it does b/c it was fairly easy to develop with - there were merely some minor bugs in the two implementations. These can be fixed. In reality, I think we should all quit bashing on JSF and jump in to try and make it better. Rather than complaining, let's try to help.

I've done my part and applied to be on the JSF Expert Group. They probably won't let me in though - everyone seems to link I hate JSF. If I had my way, I'd scrap the sucker and make Tapestry the JSF standard. ;-)

Posted in Java at Oct 15 2004, 09:04:03 AM MDT 12 Comments

Resin slower than Tomcat, fails the AppFuse Test

As part of AppFuse 1.6, I was hoping to supply the option to use Resin instead of Tomcat. In reality, hardly anyone has asked for this on the mailing list - but since so many folks say that "Resin is better" - I figured I'd give it a whirl. I hate to be the guy that spoils all the Resin users mantra, but it seems like Tomcat is the better server. Why? Speed, compliance and shit works. Here are some things I found:

  • Server startup time when AppFuse is only app installed (deleted work directories before starting):
    • Resin: 25 seconds
    • Tomcat: 14 seconds
    I also found that Resin loads my StartupListener twice b/c it's defined in web.xml and XDoclet puts it in the generated TLD. I think the best solution is to add a flag to XDoclet to allow you to exclude listeners from the TLD when it's generated. I commented out the entry in web.xml for this test.
  • Running "ant test-canoo" which runs through all the JSPs using Canoo's WebTest (server already started):
    • Resin: 1st run (no compiled JSPs) - 53 seconds, 2nd run (compiled JSPs) - 24 seconds.
    • Tomcat: 1st run (no compiled JSPs) - 49 seconds, 2nd run (compiled JSPs) - 14 seconds.

I did have to hack a number of things to even run the tests (which verifies titles, success messages, etc.) on Resin. First of all, SiteMesh seems to puke with if you try to use <dispatcher>FORWARD</dispatcher> on the filter-mapping. I had to comment this out, resulting in a lots of HTML Tidy errors during the tests ( Error: <content> is not recognized!). I also had to turn off Resin's fast-jstl to bypass this bug. Finally, I had to revert the displaytag to using the non-EL version (which required changing titleKey -> title and name="${listName}" -> name="listName"). It seems that Resin enables EL even if you're using a 2.3 DTD. With Tomcat, and I believe the spec says this, EL is only enabled if you use a 2.4 XSD.

So there you have it folks, not only is Resin 3.0.9 slower than Tomcat 5.0.28 (JDK 1.4.2, Windows XP), but it's not standards compliant. Of course, the speed could be due to my using the open source version (with performance enhancements turned off) - but I'd think the other (albeit minor) stuff should be easy to fix. Everytime I try to use Resin, I end up finding bugs and going back to Tomcat. I guess this is another one of those days. I don't think I'll include a Resin installer as part of 1.6, but I might distribute one later if I can get these issues solved. I wonder if I should give Orion a run for its money?

P.S. Since I know people are going to ask: Tomcat 5.5.3 with JDK 5.0 took 9 seconds to startup. Running "test-canoo"? First run: 37 seconds, second: 14 seconds. Looks like the new compiler is quite a bit faster.

Posted in Java at Oct 07 2004, 07:13:09 PM MDT 24 Comments

The New Gig

I started a new gig on Monday. For the next 6 weeks or so I'll be helping Oak Grove Systems enhance their Reactor products. I'm excited about the opportunity. I was going to use Equinox to build the prototype (due Monday), but since the final product requires security and user management - AppFuse is a more logical choice. I'd like to use WebWork or Spring, but since Oak Grove's reference implementation of their portal framework uses Struts, it's probably wise that I do the same so they can maintain it easier.

I almost went to work full-time and in an office - but now that I've chosen to take a gig with 100% work-from-home, I'm pretty glad I did. It would be tough to give up the musing-blaring, 23" monitor environment I've grown to know and love. The new office later this month will make it even nicer. BTW, the house is coming along and we should move in in a couple of weeks. Here's some pics: view from front, view from back and inside view.

Posted in Java at Oct 07 2004, 10:02:11 AM MDT 4 Comments

Aren't out-of-container tests supposed to be faster?

Earlier this week, I converted my StrutsTestCase tests from using CactusStrutsTestCase to MockStrutsTestCase. At first, this seemed like a great thing - on my Windows XP box. To run all the tests in AppFuse, it takes around 2:20. I know, this is quite a bit of time for unit tests - but it was 3:15 in version 1.5. At least it's faster than it was. 1:20 of this is for starting Tomcat and running Canoo WebTests.

This all seemed great until I ran "ant test-all" on my PowerBook tonight. It averages around 7:00 minutes. WFT?! I know PowerBooks are slow - but they're not that slow. I'm guessing the reason is because Spring's ApplicationContext is loaded by each test - whereas the Cactus versions would always grab it out of the ServletContext. Sounds like I need a TestSuite.

What if I isolate and compare times for just the "test-web" target? This target runs all the Action, Filter and Listener tests. Time to execute on Windows: 33 seconds, PowerBook: 2 minutes 21 seconds. What about just testing the JSPs (with Cargo)? Windows: 1:24, PowerBook: 3:32. My Windows desktop has 1.5 GB RAM and a 2.6 GHz processor. My PowerBook has 1 GB RAM and 1.5 GHz. I agree that it seems like an unfair comparison - but AppFuse tests ran in around 4 minutes on the PowerBook in the last release. I guess it's back to the drawing board.

Update: Nevermind. I just downloaded and checked out AppFuse 1.5 - almost 8 minutes for running all the tests. If I were going to work on my PowerBook for my next project, I'd refactor AppFuse Managers and Actions to use jMock. Luckily I'm using my Windows machine. ;-)

Update 2: I found a solution! Using Ant 1.6.2's forkmode="once", I was able to reduce the time of the test-web target from 2:21 to 24 seconds on my PowerBook!! Windows: :33 -> :18. Running "ant test-all" on the Mac is now 2:15 - while the Windows version is 2:20.

WTF!? My PowerBook is actually faster than my Windows box? I never thought I'd see the day - Yeeee haaawwww!!

Now I just have to figure out how to detect 1.6.2 and warn users if they aren't using it.

Posted in Java at Oct 05 2004, 09:26:56 PM MDT 8 Comments

Integrating WebWork into AppFuse

Last week was a busy one as I was trying to finish up AppFuse 1.6 before starting my new gig (more on that later). Most of this involved 1) integrating WebWork and 2) writing the Ant-based installer for replacing Struts with WebWork. I've worked with WebWork before, so this post is mostly an extension of that - as well as documentation on what I did so others can understand AppFuse+WebWork better. Below is a list of things I found.

  • WebWork's concept of "results" are very similar to Struts "action-forwards". However, they also allow you to chain to other actions and use expressions.
  • Implementing the ModelDriven interface in your Actions is a good idea, but it's not that great. I found that by simply using get/setUser(), I got the same functionality - except that I had to add "user." to all my form elements in my JSP. The advantage to doing things this way (over ModelDriven) is you can have an Action that services many objects. The main reason I'm not using ModelDriven is because the following tag doesn't render the label (it's just blank). Strangely enough, it renders just fine after validation fails.
    <ww:textfield label="getText('user.firstName')" name="'user.firstName'"
        value="user.firstName" required="true"/>
  • WebWork is similar to Tapestry in that they encourage you to specify i18n messages (properties files) on a per-action basis (Tapestry is per page). Some might think this is a good idea, but after using Struts and Spring, I'm used to using one ResourceBundle for everything. To migrate AppFuse from using 1 bundle to many bundles just for WebWork would've been a pain. Luckily, in WebWork 2.1, they added the ability to specify a custom bundle using "webwork.custom.i18n.resources=ApplicationResources" in webwork.properties. I believe my issue with ModelDriven and labels is caused by this new feature.
  • The Spring-integration provided by SpringObjectFactory is quite nice. Unfortunately, client-side visitor validation doesn't work with it.
  • Unit-testing WebWork actions is easy, though it's kinda wierd to not send request parameters and such to set values (instead, you just set values directly on the Action).
  • I experimented with putting stuff on the stack and pulling it off in JSPs, but never got it to work quite right. After failing the 10 minute test, I decided to just put stuff in the request and get it working. Looking through the code now, there's only 3 places where I'm stuffing attributes into the request: 2 in FileUploadAction and 1 in ExceptionHandlerInterceptor.
  • With most frameworks I've used in the past, I rarely jumped into the source to try and see how things worked. Because of this, I rarely extended framework classes for my own use. However, with WebWork/XWork, I found it quite easy to dig in and extend the framework. Especially with Interceptors - which I dig.
  • Following up on Interceptors, I was able to easily create my own ValidationInterceptor that cancels validation on GET requests, and when cancel or delete is clicked.
  • Other interceptors that came in handy are a UserRoleAuthorizationInterceptor (which I borrowed from Spring) and an ExceptionHandlerInterceptor (which is modeled after Spring's SimpleMappingExceptionResolver). The 1st interceptor made it easy to mimic Struts' ability to declare a "roles" attribute on an action-mapping. The 2nd one allows you to map exceptions to results. This is something I was looking for when I first started using WebWork.
  • The "required" attribute in WebWork's JSP tags have nothing to do with validation, except that they add an asterisk to the field label. I'd rather this be integrated with validation - where an asterisk shows up when a field is "required" in its Action/POJO-validation.xml file.
  • I had a few other issues with validation: client-side validation shows one error at a time, client-side validation doesn't allow cancelling and field errors are displayed in a random order. If I were to start a project tomorrow that wanted WebWork with client-side validation, I'd probably try to integrate Commons Validator.
  • Testing Actions is easy, as it should be. Using Spring's Servlet API mocks made it easy to test Actions that used ServletActionContext to set cookies and other such request-related stuff.
  • I dig the rich set of form tags and it's great that these can be easily customized. I customized a few for AppFuse to make the syntax a bit more XHTML-compliant.
  • I'm a stickler for formatting and good-looking XHTML in JSPs. I try to keep lines less than 80 characters. Here's a comparison of the LOC required by the different frameworks for userForm.jsp:
    • Struts: 298
    • Spring: 319
    • WebWork: 186

All in all, I enjoyed working with WebWork and if given a choice of an AppFuse combination to use on my next project - it would probably be WebWork+Hibernate. If the client wanted client-side validation, I'd either integrate Commons Validator (which shouldn't be too hard) or use Spring+Hibernate. After using Spring and WebWork, which allow you to use your model objects directly in the view, it would be tough to go back to Struts. However, I still do know Struts better than the other two - so if I had a really tight deadline, that might be the smart way to go.

Posted in Java at Oct 04 2004, 08:17:37 AM MDT 2 Comments

[ANN] Cargo 0.2 Released

Download · Release Notes · Learn More · Project Weblog. I've been looking for something like Cargo for a while now - mainly so I could provide an easy way to test JSPs (and therefore the whole app) in-container. For the last couple of years, I've been using Cactus' <runservertests> task, but it requires you to configure your own startup and shutdown targets - which can be difficult for the different containers. Cargo makes this easy. So easy that I've added it as a topic to Chapter 8 of Spring Live. BTW, the MyUsers sample app shows you how to use jMock and Easy Mock for isolating Manager tests and Action/Controller tests - something that I might eventually move to with AppFuse.

In 1.5, AppFuse's build.xml is tightly coupled to Tomcat for running Cactus tests and Canoo WebTests. There are only a couple of Cactus tests in the Spring and WebWork versions - LoginServletTest and ActionFilterTest and I believe I can use Spring Mocks to refactor those and test them out-of-container.

Using Spring's ContextLoaderPlugin for Struts, I should be able start testing the Struts Actions using MockStrutsTestCase too. This means that the only in-container tests will be Canoo WebTests. As part of 1.6, hopefully you'll be able to run the "test-web" target sans container. Right now it sounds like quite a challenge, but being able to run Spring's Controllers and WebWorks Actions out-of-container is a really nice feature. Furthermore, removing Cactus as a dependency would open the doors for using JDK 5.0.

I'm also hoping to add support for switching containers - by running a simple script that nips and tucks the build.xml. For 1.6, I'll try to add an installer for Resin. If it's easy, I might take a stab at Jetty and Orion too.

Update: Cactus is gone - there's no need for it anymore as all actions/controllers, filters and listeners can be tested out-of-container. The best part is I didn't have to change any application code - just a few tests. Sweet!

Posted in Java at Oct 03 2004, 04:15:48 PM MDT 2 Comments

My Tiles to SiteMesh Migration

I spent a few hours last night replacing Tiles with SiteMesh in AppFuse. I had the Struts version done in 2 hours, and most of the Spring version done in an additional hour. Then I spent another 3 hours today twiddling with things and getting it just right. During this process, I discovered a few things I thought I'd share. Keep in mind that I'm pretty much a SiteMesh rookie. Hopefully, implementing and using it in AppFuse will help me to fully understand its power.

  • SiteMesh lacks injection. Tiles allows you to inject JSP fragments into your base layout on a per-page basis. I didn't use this feature much, but I did use it to determine which menu should show up on what pages. For example, the login page would get a "projects used" menu, the signup page wouldn't have a menu, and all other pages would get the standard site menu. With SiteMesh, all of this seems to be done best with JSP includes and some <c:if> logic in your decorator. I ended up using <c:import> in my login.jsp for the login menu, importing nothing for signup, and using <c:if> statements in my decorator to see if the user was logged in. If they were, then I import the site menu and its necessary scripts and styles. In the end, this worked fine and it's probably easier for new AppFuse users to understand - so that's a good thing.
  • Injecting scripts and stylesheets. With Tiles, I was able to easily control which scripts and stylesheets were shown on each page. With SiteMesh, this is pretty easy to do by putting them in the <head> element of your page. However, what I would've really liked to see is the ability to put these in an included JSP and have them end up in the <head> of the final document. I know it's virtually impossible, but it would be cool.
  • Headings. Adding a <title> title element per-page works great using the <title> tag. However, if you want to add a heading (which I specify with an <h1> tag), you have to use a <content> tag. It would be cool if I could somehow use <h1> or <heading> in my page to indicate a heading. I ended up going with the following in each page to specify the title and heading:
    <title><fmt:message key="mainMenu.title"/></title>
    <content tag="heading"><fmt:message key="mainMenu.heading"/></content>
    There's probably an easier way to do this, but this works for now.
  • Injecting <body> ids. Using body ids to set styles on a per-page basis is a great way to control CSS. With Tiles, I set this as an attribute in tiles-config.xml and then grabbed/used it with the following JSTL code:
        <c:set var="bodyId" scope="request">
            <tiles:getAsString name="body.id" ignore="true"/>
        </c:set>

    <body<c:if test="${not empty bodyId}"> id="<c:out value="${bodyId}"/>"</c:if>>
    Yeah, it's ugly, but it works. With SiteMesh, you can easily set a body id by using <body id="name">. In fact, you don't even need to wrap your content with it, you can simpley do <body id="name"/>. I'm grabbing and using this in my decorator with the following code. This works, but it would be cool if I could check for the existence of the attribute first - so I could eliminate id="" when no body id is set.
    <body id="<decorator:getProperty property='body.id'/>">
  • Inheritance. With Tiles, you could extend page definitions and override attributes. This feature seems to be completely lacking in SiteMesh. I don't know how you could implement it, but it would be nice to have something where you could specify the parent - for instance, to use the same <head> content.
  • Error page decoration. SiteMesh seems to be incapable of decorating error pages (i.e. 404) in Tomcat 5.0.x (even if I add <dispatcher>ERROR</dispatcher> to the filter-mapping). A workaround is to use wrap the error page with a <page:applyDecorator> tag. This works, but if you specify elements in <head>, they will end up in the body of page, rather than in the decorator's header.
  • SiteMesh simplifies. Switching from Tiles to SiteMesh allowed me to delete somewhere around 8 JSPs. AppFuse has 34 after committing everything. Most of these were JSPs that sat in the root folder and had a one-liner to pull in a Tiles definition. However, it also eliminated 11 JSPs from the Spring MVC install - allowing reduction of duplication. The Spring MVC install now has 17 JSPs.

So there you have it. AppFuse now uses SiteMesh instead of Tiles! I'm sure the implementation will get cleaner and more refined as more folks use it. I'm looking forward to deleting some chunks out of AppFuse's tutorials because SiteMesh makes page development so much easier. The hardest part of SiteMesh is setting up the infrastructure. Once you're got that done, you hardly ever touch it again.

Next task: WebWork integration.

Posted in Java at Aug 21 2004, 11:50:50 PM MDT 14 Comments

Should I ditch Tiles in AppFuse?

I've haven't been developing applications with AppFuse since May. Instead, I've been using Equinox. One of the main things in Equinox I've grown to love is SiteMesh. It's worked in all the webapps I've written, which utilize frameworks like Struts, Spring MVC, WebWork, Tapestry and JSF. Tiles will only work in 3 of those.

I think it's time to make an executive decision on AppFuse and ditch Tiles in place of SiteMesh. It's faster and easier to develop with, and it doesn't get in your way. In fact, I didn't have to change a single line of SiteMesh-related code to support any of the aforementioned frameworks. Furthermore, using SiteMesh would also greatly reduce the duplicate between frameworks. I've thought about keeping Tiles around, but it's a pain in the ass to maintain parallel sets of documentation.

Whaddya think - any reason you can think of to keep Tiles? I can't. In fact, I think I'd cringe if I had to start my next AppFuse-based project w/o SiteMesh.

Posted in Java at Aug 18 2004, 10:11:56 AM MDT 33 Comments