A bit of a newbie question I'm afraid, which I wonder if anyone can help with.
I'm a complete newbie to Spring, and am tring to get to grips with Spring MVC. I've been reading 'Expert Spring MVC and Web Flow', but to be honest its serving to confuse more than educate
I've put together the basic airline flight booking app in Chapter 4. Whilst its a bit better than a HelloWorld app, it still leaves me with unanswered questions, so I'm trying to put together something a bit more substantial.
The app I want to put together is a simple home insurance quote application. I have my domain classes already defined as a series of POJOs, and I want to present 3 screens - About You, About Your Home and Your Quote where fields on the screens are properties of the various POJO classes.
My main domain class is Quote which has properties of type Proposer, Property, CoverOptions, Premium. Proposer has properties to describe the policy proposer, Property describes the property to insure, etc.
As I see it at the moment I need 3 SimpleFormController controllers - one for each of the screens; and that I need to create a new Quote object and pass it between screens as the controller progressively populates more of the properties within. And that I also need to ensure that if a new session starts midway through the process - perhaps on the About Your Property controller - this is detected and they are sent back to the beginning where a new Quote object can be created for them.
My current thoughts are that I actually need 4 controllers - Init, About You, About Your Property and Your Quote; and the Quote object will be stored within the http session. In the constructor of About You, About Your Property and Your Quote I need to get the Quote object from the session. If it does not exist I need to issue a redirect: prefix to the Init controller whose contrstructor will create and store in the session a new Quote object and then issue a redirect: to the About You controller.
But this does not sound very 'DI' - especially the creation of the Quote object.
Any pointers to help clear up my understanding would be very much appreciated;
There are pretty much two approaches that don't necessarily require separate controllers
1. Use annotation driven controller to define methods to handle the submit from each page (I presume you're using Spring 2.5 or higher)
2. Look at WebFlow to do this work, which you've already discovered to be more difficult to master
Thanks for your reply Mark.
I've just got to the chapters in the book to do with Web Flow and was initially thinking 'yes, this is what I need'. However, having had a play I've decided its probably not what I need on the grounds that a) the book is outdated and b) it looks flippin' complicated !
OK, so going back to your original suggestion (using annotation driven controllers to define methods to handle the submit from each page), could you elaborate a little on that please? The book doesn't describe using annotations in controllers (and yes, I am using Spring 2.5 or higher - 2.5.6 SEC02 to be precise )
Roughly how would you go about setting up a project like mine? (3 pages where an object is passed between the pages and progressively populated with the data from each page). I'm imagining something like this:
But I cant picture how I would instantiate my Quote object once and pass it into each controller??
I'm also a bit confused with some of the terminology (which probably explains why I'm finding this so hard!) Is my POJO (my Quote object) my domain object? or my command object? or a bean? And where do services fit into this? - I imagine that I need to write some business logic methods somewhere, at a minimum I would need a doQuoteCalculation() (but extending the app into something more useful I would need doPostcodeLookup(), soSaveQuote() etc etc)
Any pointers you can offer would be very very much appreciated;
Joined: Jul 01, 2003
You've asked a lot of really good questions. Let me start with the architecture question first.
You have a lot of flexibility in how you architect a solution using the Spring MVC approach. A lot depends on the complexity. You might decide to compose a service layer that handles aggregating multiple calls to several DAO objects, which would be responsible for fetching your domain objects. If the service functionality isn't that complex, then it may just make sense to roll that into the controller and have it act as your service layer. The service layer should NOT just be a pass through proxy for calls to the DAO. Methods on the service layer should be more granular and provide cohesive business functionality. A good way to approach this is to roughly sketch the page flow from the user perspective on paper or a whiteboard and then simply begin to work it through to the back end in terms of what data you'll need. As you begin to look at required interactions, you may also begin to see logical groupings of functionality that may make a logical service layer. The controller would then simply be responsible for collecting data from the web page, validating and then calling the correct service functionality to perform the next steps.
Ok, in terms of the controller the Spring annotation way. I suspect you may indeed be getting tripped up by terminology. The term 'command object' comes from the old AbstractFormController/SimpleFormControler and from a broader pattern for doign MVC (I think Struts also uses this term). Another term that is used is 'form backing object'. This object can be a POJO and it could be a domain/model object and could even have persistence annotations (ala JPA or Hibernate). From here, I think your question is about the origin of this object and that has a lot to do with your approach to this series of pages. I assume from what you've said already that a user (me for example) would come to some starting page (/AboutYou.htm?) and begin filling out information over a series of pages. The implication is that there is a starting page, which will usually be accessed via a GET request. Usually this first invocation is where the command object is initialized. As an aside, if I was doing something similar with a specific instance (i.e. editing a product entry, which implies a product id as the key), I might pass in the product id as part of the initial GET request but initialization would probably result in a populated object (likely from a data store of some kind).
From there, the user would progress from one page to the next through a series of submits (which are POST requests). You would handle the submit for each page by doing whatever you needed to do (i.e. validating, performing calculations, etc) to set up for the next page. I found the POST -> redirect -> GET approach to work pretty well. The POST handles the binding of the data from the form to the command object. From there, I process the result and redirect to a GET request (same controller) where I prepare for the next page before returning the view to render the next form page. What makes this work is that the command object is a session object that is simply kept available to all invocations to the controller. This is made easier by the annotation based approach as there aren't the same restrictions as with the SimpleFormController.
Based on what you've posted, a controller to do all this might look like the following
I hope this helps.
Joined: Aug 18, 2004
Thank you so so much for this - I most definately owe you a beer for this
Its all coming together very nicely.
One further question though (hopefully it'll be my last!) - when a new session comes along and issues a GET to /AboutYourProperty.htm (ie. tries to start in the middle of the process) Spring throws 'org.springframework.web.HttpSessionRequiredException: Session attribute 'quote' required - not found in session'
Looking at the method this makes sense:
But is there a way of catching this nicely. My initial thought was that I'd do something like this:
But I think the exception is thrown before the method even runs.
Joined: Jul 01, 2003
This session issue can definately cause problems for sure. If you were using Spring 3.0+, there's a nifty @ExceptionHandler annotation you could add to a method that could do the work for you. As it is, you'll need to configure an exception resolver in the bean file for SimpleMappingExceptionResolver. One thought I had was that you could simply redirect back to a known good starting point. The one issue with the SimpleMappingExceptionResolver is that it mapps to a view (i.e. JSP). What you really want is to direct this kind of exception to a path (like your starting path). That would allow you to perform initialization via the controller before directing back to the starting view. This is where the @ExceptionHandler annotation is nice.
However, a few options come to mind. You could have a look at the classes and interfaces associated with the Spring Exception handler and write your own. Petty much all you would need to do is extend SimpleMappingExceptionResolver and override the doResolveException method. You'd still configure it in the bean file, but it allows you to do initialization if you want before directing to the configured view. Another solution would possibly be to create error definitions in the web.xml file that would map to the appropriate URI.
Joined: Aug 18, 2004
As before, thanks for your help Mark.
I wrote an implementation of org.springframework.web.servlet.HandlerExceptionResolver and in the method I wrote
The redirect does exactly what I need - Perfect