RequestScope-Bean init after apply request - spring

i've an Requestscoped Bean which reads the state of an conversation scoped bean in it's #postconstruct mehod. But it is created before the request is applied to the conversation scoped bean, so the data is one request behind. How can i init the request-scoped bean later?

Make use of <f:event type="preRenderView">. Put this somewhere in top of the view (the exact location is actually irrelevant, but somewhere in top of view is most self-documenting):
<f:event type="preRenderView" listener="#{bean.init}" />
The method can just look like this, don't forget to remove the #PostConstruct.
public void init() {
// ...
}

Related

Why is #PostConstruct method called after post/postback?

I have following request scoped Spring bean with #postconstruct method init():
#Component
#Scope("request")
public class editUserBB {
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
private UserDto user;
#Autowired
private IUserService userService;
#PostConstruct
public void init() throws IOException {
String id_string = params.get("id");
Long id = Long.parseLong(id_string);
user = userService.getUserById(id);
}
public String save(){
// save to database
return "user?faces-redirect=true&id=" + (long)user.getId();
}
}
And userEdit.xhtml with h:form and commandButton:
<h:commandButton value="Save" action="#{editUserBB.save()}" />
However, after the save button is clicked the init() method is called once again annulling all the changes made to the UserDto object before I can save it to the DB. So what am I doing wrong?
And I just tested, the init() method is called even before save(), which I also don't understand..
That's not the fault of the init() method. That's just your own fault of placing the bean in the request scope instead of in the view scope.
A request scoped bean lives as long as a single HTTP request-response cycle. Opening the page with the form counts as one HTTP request. The HTTP request is garbaged when the associated HTTP response is finished sending the result to the client, including all associated request scoped beans. Submitting the form counts as another HTTP request which thus creates a completely new instance of the request scoped bean. If you explore/debug the instance's hashcode (and constructor), you'll notice that it are actually two physically completely distinct instances. There's thus absolutely no means of the init() to "override" the old values. The old values aren't there in first place.
JSF has solved this awkward behavior of a request scoped bean with introducing the view scope. You probably have ever heard/read about it before replacing JSF bean management facility by Spring's one for some reason. Spring bean management facility doesn't have a native concept of the view scope, you'd need to homegrow one.
If you intend to stick to Spring bean management facility, then your best bet is retaining the request parameter responsible for proper initialization of the data along with the form submit. You can use <f:param> for that:
<h:commandButton value="Save" action="#{editUserBB.save()}">
<f:param name="id" value="#{param.id}" />
</h:commandButton>
See also:
How to choose the right bean scope?
How do request/session/application work?
Unrelated to the concrete problem, you should avoid calling FacesContext during instance construction/initialization. This is bad design. In this particular case, move that line of obtaining the request parameter map to inside the init() method. Also here, JSF bean management facility has a standard solution in flavor of #ManagedProperty while Spring one doesn't have.

Keep bean attributes after ajax form submit

I have a #ViewScoped #ManagedBean which creates an unique ID. This ID is rendered in a form like:
<h:form>
<h:outputText value="#{myBean.uid}" id="uid"/>
<h:hiddenInput value="#{myBean.uid}" id="hiddenId" />
....
<p:commandButton actionListener="#{myBean.create}" ajax="true" update="#form" value="Create" />
</h:form>
So far so good. On first request the page is rendered correctly. After submitting the form and in the case of validation failure, the outputText is empty but the hidden input field keeps its variable.
Any clue what I'd have to do to prevent this behavior and too let the outputText keep its state?
I realized that the bean seems to be initialized after each ajax request. But then, why does the hidden input field keeps the old variable?
Here is the relevant code of my bean:
#ManagedBean(name = "myBean", eager = true)
#Stateful
#Model
#ViewScoped
public class MyBean implements Serializable {
...
private String uid;
...
#PostConstruct
public void initWithData() {
this.uid = UUID.randomUUID().toString();
}
}
JSF input components have 3 places where the value (the state) is stored:
Submitted value (the raw unconverted/unvalidated String request parameter value).
Local value (the successfully converted/validated object value, stored inside component itself).
Model value (when the entire form is successfully processed, stored as bean property)
JSF processes input components as follows:
Get HTTP request parameter value by component's client ID and store it as submitted value.
If conversion/validation succeeds, set it as local value and set submitted value to null.
If entire form is successfully processed, set it as model value and set local value to null.
JSF renders values of input components as follows:
If submitted value is not null, display it.
Else if local value is not null, display it.
Else display model value.
So, in your particular case of a general validation failure, JSF is for that hidden input component just redisplaying the local value instead of the model value. If you want to achieve the same with the output text, I think best is to just do the same as JSF:
<h:outputText value="#{empty hiddenId.submittedValue ? empty hiddenId.localValue ? hiddenId.value : hiddenId.localValue : hiddenId.submittedValue}" />
<h:inputHidden id="hiddenId" binding="#{hiddenId}" value="#{myBean.uid}" />
Alternatively, you could just use a read only input and remove the border by CSS if necessary:
<h:inputText id="hiddenId" value="#{myBean.uid}" readonly="true" style="border: none;" />
As to your bean, I'm not sure what's happening there as this class seems to be extremely tight coupled. I'd rather split it into 3 classes: one real backing bean, one stateless service and one model entity. Further, you should also make sure that you aren't binding view build time tags or attributes to a property of a view scoped bean. Otherwise it will indeed guaranteed be reconstructed on every single request.
See also:
JSTL in JSF2 Facelets... makes sense?
#PostConstruct method is called even if the ManagedBean has already been instantiated (e.g. on AJAX-calls)
By the way, the eager=true has only effect in #ApplicationScoped beans.
The problem is probably the #Model stereotype (was, as you already removed it). It combines #Named and #RequestScoped and makes the bean a CDI request scoped bean. CDI managed beans should be resolved before JSF managed beans and therefore the #ViewScoped has no effect (it creates a JSF managed bean).

Is it possible to annotate a backing bean method to be called on a phase?

I need an initializer method in the backing bean to be called after components are bind. #PostConstruct is called before component bindings. Is there any JSF annotation for methods which cause method call after component binding?
Currently it's possible to use something like <f:view afterPhase="#{bean.initialize}"> or <f:event type="preRenderView" listener="#{bean.initialize}" /> which requires code on page side and bean side. Is there any bean-side-only solution?
There's nothing like that in standard JSF API.
Closest what you can get is lazy loading in getter.
public UIComponent getSomeComponent() {
if (!initialized(someComponent)) {
initialize(someComponent);
}
return someComponent;
}
or lazy executing in setter.
public void setSomeComponent(UIComponent someComponent) {
if (!initialized(someComponent)) {
initialize(someComponent);
}
this.someComponent = someComponent;
}

when using spring autowiring then struts tag does not map with action class

when i am putting autowiring interceptor(interceptor-ref name="autowiring") in the action tag in spring.xml then struts tag in index.jsp does not able to map with the setter of that action class. and when i am removing that autowiring tag from the action tag then in that case struts tag of jsp does able to map with the setter of the action class.
Any help from your side will be more than welcome. if you need any sort of example then let me know.
<s:textfield name="name" label="Name" />
<s:textfield name="salary" label="Salary" />
<s:submit value="Add Record" />
then this struts tag with name 'name' and 'salary' does not set the value in action class represented with the same name of setter/getter.
public void setName(String name) {
this.name = name;
}
public void setSalary(String salary) {
this.salary = salary;
}
If you define an interceptor On an action you must define all interceptors on an action. If your parameters aren't being set then whatever the "autowiring" interceptor reference doesn't include the "parameters" stack, the interceptor responsible for transferring form properties to actions.
And Umesh is correct, if you're using Spring, the plugin handles injection for you, and you do not need to manually define the "autowiring" plugin on your action. If you just remove that interceptor definition, your parameters should be set as normal, and the action should still be wired up.
That said–using a session factory manually inside an action would not be considered a best practice. Any session factory logic should be wrapped up inside your DAOs/services/etc. Actions should rarely (read: never) be aware of the persistence layer.

Why does the navigation only work correctly when the backing bean is session-scoped?

Consider the following page (header and stuff omitted for brevity):
<h:body>
<h:form>
<h:inputText
id="theInput"
value="#{theBean.text}">
<f:ajax render="theButton" />
</h:inputText>
<h:commandButton
id="theButton"
value="Result"
disabled="#{theBean.disabled}"
action="result" />
</h:form>
</h:body>
TheBean looks like this:
#Named
#RequestScoped
public class TheBean {
String text;
public TheBean() {
this.text = "test";
}
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
public boolean isDisabled() {
return this.text.equals("test");
}
}
result.xhtml is a placeholder that displays some static text.
The idea is to enable the button only if the entered text is valid (in this case different from the default). It works, and the button is correctly enabled if I enter something other than test, but when I click on the button, it does not navigate to result. However, it works if I do one of the following:
I make the button always enabled. This baffles me, because in the example above the button is correctly enabled, it only does not navigate to the next page. Why would that change if I leave it enabled all the time?
I change the scope of TheBean to #SessionScoped. I do not understand this either. How can the scope of the backing bean influence the page-navigation? Everything else in the example seems to work just the same with #RequestScoped.
I would really like to keep TheBean request-scoped. Is there any way to make it the navigation work without making it session-scoped?
Your bean is request scoped which means that every single HTTP request (also ajax requests!) creates a brand new bean. When you change the input, then the ajax request creates a new bean and sets the value. When you press the button, then the normal request creates a new bean (thus the value set by ajax is lost!) and determines if the button is eligible to be invoked -which is not- and thus won't invoke the action.
The view scope is designed to overcome exactly this kind of problems. Put the bean in the view scope.
#ManagedBean
#ViewScoped
public class TheBean {
This way the bean will live as long as you're interacting with the same view by either normal or ajax requests.
Sorry, I don't know how from top of head what the proper CDI declaration is. It's at least called "conversation scope".

Resources