Matt RaibleMatt Raible is a Java Champion and Developer Advocate at Okta.

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.


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.

My Tapestry Experience

I've finished migrating the sample app I'm working on from WebWork to Tapestry. You can also read about my WebWork experience. WebWork took me 2 full days to complete and the Tapestry version took me about 4. I had a bit of an advantage with WebWork as I've read a lot about it before working with it. I'm probably a bit biases against Tapestry because everyone thinks it's the bees knees - I don't mean to be harsh - I'm just reporting through the eyes of a developer. I'm sure I'll have similar gripes with JSF. Below is a list of things I discovered:

  • There's something wrong with a project when its documentation is outdated and folks tell you to "Buy the book" rather than "read the documentation". On that same note, most of the documentation that does exist seems to be targeted at the advanced user.
  • Like WebWork, there was no simple CRUD example I could look at. Then, like a ray of light from the sky - Warner published one yesterday! This tutorial vastly improved my productivity - thanks Warner! The only things I saw that I'd change in this tutorial is the use of individual setters vs. a domain object. Also, an ICallback is in the code, but never really used.
  • The recommended way to name templates (.html) and page specification (.page) files is starting with an Uppercase letter. So rather than home.html (which most web developers are used to), it's recommended you use Home.html. Of course, this is easy to change - just seems like a weird recommendation, almost Apple-ish or Microsoft-ish.
  • By default, all templates and pages are cached. Sure this is good because Tapestry is production-ready, but when you're developing - this needs to be off so you can get deploy+reload functionality. If you're using Tomcat, you can turn caching off by setting a $CATALINA_OPTS environment variable with value "-Dorg.apache.tapestry.disable-caching=true" (no quotes).
  • Tapestry integrates with Spring very nicely. So easy it's almost silly. When I first created my list screen, it actually had only one line: public abstract UserManager getUserManager(); - and then I used OGNL to get my list of users: userManager.users. It doesn't get much easier than that.
  • While setting success messages is fairly easy - I couldn't find a good way to prevent duplicate postings. With most frameworks, I stuff messages in the session and then retrieve them on the next page. With Tapestry, you have to throw a RedirectException if you want a true redirect (which requires a lot to calculate the URL of a page). I ended up using a PageRedirectException in hopes of simplifying this - but this seems to just do a forward instead of a redirect. In the few hours I spent on it, I couldn't find a way to save success messages and have them persist through a redirect. The reason I want to use a redirect is so a refresh of the page doesn't submit everything again. I know it's trivial, but is is an issue that most frameworks don't handle cleanly (except for Struts).
  • There's no way to test Tapestry classes - since they're abstract, you can't just invoke them and test. Granted the classes are simple - but as long as other frameworks allow you to test their .java files and Tapestry doesn't - this will be an issue.
  • When you first enter a blank form (i.e. to add a new user), the cursor's focus is put on the first required field. As a developer and user, I'd like to control this a little more (for example, by putting it on the first field). Furthermore, I'd like to control it easily - without having to subclass ValidationDelegate. On that same note, it'd be cool if required fields had an asterisk by default. WebWork does this and Spring/Struts can do this using Hatcher's LabelTag.
  • There's no easy way to get the URL of a page - for example, to use in a <button>'s onclick handler to do location.href. I ended up having to implement a method in my page class, and a @Block in my template, setting the button's value with OGNL, and then using JavaScript to do onclick="location.href=this.value". The default components that ship with Tapestry only produce links and submit buttons (that must be in a form).

When developing this sample app, I often felt like I was banging my head against the wall. This is likely because I didn't want to take the time to truly understand how Tapestry works - I just wanted to get my app done. I did end up buying Tapestry in Action, but I probably won't read it until I have time or decide to use Tapestry on a project. I agree that Tapestry is cool, but it's certainly not intuitive for a Struts guy like me. I do look forward to working with it in the future and I'm sure I'll grow to like it more as I gain more experience.

Many thanks to Erik for his tech support and knowledge and to Warner for his nice kickstart tutorial.

Posted in Java at Aug 03 2004, 04:39:42 PM MDT 6 Comments

Matt - One of the things I tell folks coming to Tapestry from Struts or other non-desktop and non-WebObjects experience is that Tapestry is more of an *unlearning* curve than a learning curve. You have to give up some control of _plumbing_ in order to truly appreciate what Tapestry is doing for you. For example, with your button issue - working directly with URL's is considered plumbing and perhaps the right approach is to use a custom component in this case (I'd have to look into the details more in depth on this to give a more definitive best approach). Did you try making your message persistent? That is the trick to have it *persist* :) through a redirect. I can guarantee you that if you build a big app (or suite of apps like we are) with Tapestry, you'll find your productivity greatly enhanced - so give Tapestry more of a chance to grow on you. I'll be the first to admit its not perfect - I fight with issues every day, but it has saved us a dramatic amount of time and effort.

Posted by Erik Hatcher on August 03, 2004 at 07:25 PM MDT #

I'm glad you're looking at Tapestry. One of the great challenges in writing a framework is adequately addressing all current and future needs of users. It's very hard! For example, Tapestry could mandate how required and invalid fields are represented, but then your are forced to use its approach. Instead, key decisions are pushed into overridable classes, such as ValidationDelegate. The VD can, in fact, decorate required fields (I do this all the time). It can also decorate labels. It isn't limited to just adding characters ... it can add graphics, JavaScript, whatever you can dream up. BTW ... your description of defining a page and using OGNL leads me to think you could get by without the Java class at all (right up until you need to write a listener method of some kind). You are free to put property-specifications into your page or component spec without having to create a class or an abstract accessor for that property ... an enhanced subclass will still be created and instantiated. Finally, your Button example .... you may have missed the Any component, which can emulate an tag whatsoever. You can use a bit of OGNL to access a service and have it generate a link. There's a LOT inside Tapestry, more than any single person, even me, knows about. Welcome into the fold! Testing Tapestry classes is still a challenge. If you check my blog, you'll see that I've posted a tool used inside unit tests to instantiate Tapestry page and component classes (it uses simple assumptions to fill in any missing attributes and accessors). You actually missed on common complaint about Form ... it doesn't have a native concept of "cancelling" the form (as distinct from ordinary form submission). I'd be very interested in input on a suitably flexible way to identify duplicate posts in an efficient manner, preferrably one that *doesn't* require any server-side state. Many of your other observations are true ... to a point. Tapestry is full of extension points to allow for great flexibility; it is reasonable to augment or replace significant portions of the code, such as to change the default behavior used to locate templates. The naming is what was natural to me, 4.5 years ago; but I doubt I'd change it much if I started from scratch today. The plan for 3.1 is very tight integration of Tapestry and HiveMind, with HiveMind acting as a gateway to Spring. It is unfortunate that the intersection of understanding of Tapestry, and the time and ability to write about it, is so narrow. I'm still quite exhausted from writing Tapestry in Action. If you've been following HiveMind, you know that I've made a special effort there to keep the documentation up to date at all times so that I don't face the same uphill battle there.

Posted by Howard M. Lewis Ship on August 03, 2004 at 08:14 PM MDT #

Wierd ... the posting ate my line breaks and seems to have reordered my sentences. Howard needs sleep badly!

Posted by Howard M. Lewis Ship on August 03, 2004 at 08:15 PM MDT #

Hey Matt, glad that my tutorial helped out. Maybe I'll get the next one done in time so you can review it as well ;-).

Posted by Warner Onstine on August 04, 2004 at 03:00 PM MDT #

Howard, I don't think it's possible to detect duplicate posts without maintaining some server state... You need to know which tokens are still valid.

Posted by Jason Carreira on August 04, 2004 at 03:01 PM MDT #

I'm in process of converting an application from Struts to Tapestry and I had the same reaction as Matt:

"The only things I saw that I'd change in this tutorial is the use of individual setters vs. a domain object" <p/> Many of the examples out there use this pattern, but it seemed so "un tapestry" to pass around ID's rather than models, so my app uses models. There are 2 gotchas with this approach: 1) Largeish sessions, and 2) Dealing with detached Hibernate objects. I've decided that in a high-scalability situation to take the ID approach, but the largest part of my app is the admin, which won't be accessed by millions of users simultaneously. Writing using the domain model approach instead of individual setters has saved me miles of code. Plus it was interesting to learn how Hibernate deals with detached objects and serialization.

I think I'll write a short article discussing the two patterns. They both work and have their place.

Posted by Kurt Williams on August 05, 2004 at 12:16 PM MDT #

Post a Comment:
Comments are closed for this entry.