aspose file tools*
The moose likes JSF and the fly likes Committing a response before @PostConstruct method executes Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Spring in Action this week in the Spring forum!
JavaRanch » Java Forums » Java » JSF
Bookmark "Committing a response before @PostConstruct method executes" Watch "Committing a response before @PostConstruct method executes" New topic
Author

Committing a response before @PostConstruct method executes

Rob Micah
Ranch Hand

Joined: Aug 30, 2011
Posts: 94
I have some error checking conditions in the constructor of my managed bean that, if an error is detected, commit a response using HttpServletResponse.sendError(). The problem is that my @PostConstruct method continues to execute after this situation creating NullPointerExceptions which mask the original error. Is there a way I can prevent this? I'm wondering if just checking ServletResponse.isCommitted() at the top of my @PostConstruct method is enough.
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16142
    
  21

It doesn't work that way.

Managed beans are constructed on-demand. Once constructed, they stay constructed. Only request-scope beans get constructed on a per-request basis.

However, the JSF bean architecture is based on the JSF lifecycle, not on traditional HTTP request/response processing. Attempting to influence HTTP itself in a backing bean is not a good idea.

On top of that, my own personal experience is that Managed Beans (and for that matter, ANY JavaBeans) should not attempt to do "heavy lifting" in their constructors.

Since you cannot inject datasources into ManagedBean constructors (they cannot have parameters), the only way to access a database at that point is to use a Service Locator instead of Inversion of Control, which violates the JSF design contract. Only at PostConstruct time are you guaranteed to have all necessary resources available to you (assuming that you did, in fact, inject them all).

However, PostConstruct isn't part of the HTTP process either.

So, in short, you need to re-think the whole arrangement.

Probably your best bet is to throw a FacesException at the point of error. If you use Facelets, the error reporting should be fairly good (one reason I became a Facelets fan, in fact).

If you throw a FacesException, it will be caught by the FacesServlet and passed on to a suitable error processor. By default, a rather minimalistic error page gets displayed. For Facelets pages, you get a better error page, but still not one that users would ever want to see. However, you can also set alternative error handlers in web.xml.


Customer surveys are for companies who didn't pay proper attention to begin with.
Rob Micah
Ranch Hand

Joined: Aug 30, 2011
Posts: 94
I'm not doing much in the constructor but checking for a valid HttpSession. What I'm really doing is trying to catch someone accessing the jsf page directly and send back an appropriate error response 403 (in combination with web.xml). I need my @PostConstruct to do some initialization with an EJB reference.

But I can see after trying this that I'm not stopping the JSF lifecycle. You think it's best to use an exception here?
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16142
    
  21

By accessing the JSF page directly, I take it you mean using the resource URL "xxx.xhtm" instead of the View URL ("xxx.jsf"). A better way to forbid that is to simply set an access rule in the web.xml file. In any event, direct resource access generally won't work too well, since it doesn't get routed via the FacesServlet and therefore most of the JSF mechanisms won't get executed at all, including the critical FacesServlet.

If what you really mean is that you only want people to be able to go to View B from View A (for example, a Wizard approach) and not from a bookmark or direct jump, the best way to do that is to have View A set a state-control switch that gives View B the go-ahead (and to have View B clear it). Flash Scope in JSF2 is one potential way to manage this. Another is to setup a property on the View B backing bean that is injected with the "go-ahead" from View A and which when queried throws the FacesException. That will allow operation when the View B bean is at View or Session scope, where subsequent URL requests wouldn't invoke PostConstruct.
Rob Micah
Ranch Hand

Joined: Aug 30, 2011
Posts: 94
Your "wizard" description is probably closest to what I am doing. What I have is a servlet that a client accesses first which creates an HttpSession and stores a necessary Object in it then forwards the request on to the facelet (step2.jsf). In the constructor of the ManagedBean for this facelet I'm checking for the HttpSession and the object. There is where I was using HttpServletResponse.sendError().
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16142
    
  21

OK. Since JSF Session-scope objects are exactly the same thing as J2EE Session-scope objects, you can avoid coding web- and JSF-specific code.

Just define the session-scope object as a JSF ManagedObject and inject it as a ManagedProperty into the backing bean of the JSF View B. You'll need to supply a no-argument constructor for this object for JSF's benefit.

If you then provide a property (let's call it "lastStepNumber") in this object and initialize it when you manually create the object (for example, set it to 1, where the no-args constructor sets it to 0) and add it to your session in the servlet, then the backing bean for View B can interrogate that property, and it if determines that the "lastStepNumber" isn't a proper value (for example, if View B is Wizard step 2, then usually, valid values would be 1 or 3), then View B can take appropriate action, such as throwing a FacesException.
Rob Micah
Ranch Hand

Joined: Aug 30, 2011
Posts: 94
Tim Holloway wrote:OK. Since JSF Session-scope objects are exactly the same thing as J2EE Session-scope objects, you can avoid coding web- and JSF-specific code.

When you say this are you comparing an HttpSession to a SessionScoped ManagedBean?

Tim Holloway wrote:Just define the session-scope object as a JSF ManagedObject and inject it as a ManagedProperty into the backing bean of the JSF View B. You'll need to supply a no-argument constructor for this object for JSF's benefit.

How exactly can I do this from an HttpServlet? Doesn't using the ManagedProperty annotation require it to be used inside a class annotated with ManagedBean?
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16142
    
  21


When you say this are you comparing an HttpSession to a SessionScoped ManagedBean?


No, because a session-scope object is a session-scope object, regardless of whether it was constructed by a servlet or it was constructed by JSF (as a ManagedBean). In fact, there is absolutely no way to examine an object and tell if it was created via JSF or via stock J2EE code.



No again. For one thing, annotations didn't even exist until JSF2. JSF1 could only use faces-config.xml to manage beans. For another (see above), the ManagedBean/ManagedProperty rules are exactly that: rules.

When a reference is made to a named object (for example EL in a View, faces-config.xml or JSF annotation), JSF will first look up the name in its EL object name resolution dictionaries. Only if the name is not found will JSF attempt to use the ManagedBean mechanism to construct and initialized the object. Since the application scope and session scope attributes dictionaries are the same dictionaries as plain-vanilla J2EE uses, a servlet can do a getSession().setAttribute("myBean", new MyBean())" and that will make the name "myBean" visible to JSF as a session-scope object.

The reason for declaring "myBean" in faces-config (or via annotation) is so that JSF will know how to construct the myBean object if someone bypasses the servlet that would have constructed myBean manually. Otherwise, the EL processor would fail because myBean wasn't defined.
Rob Micah
Ranch Hand

Joined: Aug 30, 2011
Posts: 94
It seems like no matter what approach I use I will have to do some check at initialization in the backing bean whether the constructor or @PostConstruct method.
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16142
    
  21

Not if the bean is in session scope, because then it would only work on the first attempt. Once constructed, the constructor and postconstruct methods won't get called again until the bean is destroyed and a new bean is created. And since request scope almost never works the way you need it to, I wouldn't recommend even trying that one.

Here's an alternative, based on the use of a hidden View control and a session bean named "wizardBean" that gets manages the various steps in the Wizard process:


Explanation:

"wizardBean" tracks the progress of the wizard workflow. Among (possibly) other things, it knows which page of the wizard we were on last. It's a ManagedBean (although it may be actually constructed and placed in-session by a servlet) and it is injected as a ManagedProperty into the View B bean.

JSFUtils is a general class that I define for my apps that hide platform-specific code such as JSF internal (FacesContext) access and HTTP-specific stuff (such as request.getSession()). Using it makes the rest of the app more abstract and easier to test off-line. In this particular case, the "forwardToURL" method would be a method that translated the "/wizardSequenceError.jsp" URL into something like "http:/mywebapp/wizardSequenceError.jsp", then did a traditional J2EE forward. I'm pretty sure I can use this here, as the "isValidView" method invoked by a hidden reference to "viewB.validView" on the View B page definition would be invoked before JSF started building the JSF output (otherwise you'd get an InvalidStateException).

I could be wrong about that, in which case, the alternative - throwing the unchecked FacesException will definitely do the trick. You can refine this by subclassing FacesException and setting up a special error handler for that subclass in your web.xml.

Rob Micah
Ranch Hand

Joined: Aug 30, 2011
Posts: 94
Correct me if I'm wrong here, but it looks like you've taken the validation code and put in into a method called isValidView() and put that inside a hidden element somewhere in the facelet. So you are still using my same logic but relocating to be called from the facelet rather than it being called in the construct/@PostConstruct of the backing bean.
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16142
    
  21

Rob Micah wrote:Correct me if I'm wrong here, but it looks like you've taken the validation code and put in into a method called isValidView() and put that inside a hidden element somewhere in the facelet. So you are still using my same logic but relocating to be called from the facelet rather than it being called in the construct/@PostConstruct of the backing bean.


Well, technically, "Facelets" are called "View Definitions", and they don't "call" anything, because the View Definition Language is a template language, not a sequential (programming) language. So the hidden element is actually making a bean property reference using EL, and the "calling" is being done by JSF.

But yes, I did relocate the validity check in the backing bean from the constructor methods to a property-get method, because, as I said, the construct/postconstruct code is only invoked when the bean is created, and not when subsequent requests are made for it.

JSF beans are never "called" directly. They can only be used as reference objects by Views. But since the actual goal is to avoid people bringing up views out of sequence (when the backing bean isn't properly set up to handle them), that's OK.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Committing a response before @PostConstruct method executes