Why is #PostConstruct method called after post/postback? - spring

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.

Related

Spring scopes behaviour with respective to single and multiple web request/session

Whenever I try to analysis to understand the spring scopes I am stuck up somewhere. Below is my understanding from my analysis and before conclude myself I would like to confirm with you. Please correct me if my understanding is wrong.
<bean id="signupController" class="com.crp.controller.SignupController"
scope="">
If scope is "request", then for every new request from client irrespective of session, the spring container will generate new instance. Once the request is completes then spring container will manage to close the life cycle of instance.
If scope is "session", then for first request of a session a new instance will be generated by spring container and maintain it for all the client request for that particular session. Once the session timed out then spring container will manage to close the life cycle of instance.
If scope is "prototype", new instance will be generated by spring container whenever the bean is requested irrespective of session. Developer should manage the life cycle of the instance because spring container will not manage life cycle of prototype scope.
If scope is "singleton", only one instance generated by spring container and maintain it to be available for all request irrespective of sessions. For every session a copy of singleton instance will be maintained so that the one session singleton object will not share by another session and spring container will manage the life cycle of the copy of singleton instance and close it when session timed out.
Note: I believe most of you may have different opinion in my understanding on singleton scope. Even I am also confused in the behaviour of singleton scope and getting different information during my analysis. Please share your thoughts
Thank you.
For singleton scope how container works for below implementation for multiple user (session) send request at same time.
Login.java:
public class Login {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
LoginController.java:
public class LoginController extends MultiActionController {
private Login login;
public ModelAndView submitLogin(HttpServletRequest request,
HttpServletResponse response) {
String userName = request.getParameter("username");
String password= request.getParameter("password");
getLogin().setUserName(userName);
getLogin().setPassword(password);
// TODO send login bean to DAO for executing further business logic.
}
public Login getLogin() {
return login;
}
public void setLogin(Login login) {
this.login = login;
}
}
context.xml:
<bean id="login" class="com.crp.bean.Login" scope="singleton">
</bean>
<bean id="loginController" class="com.crp.controller.LoginController" scope="singleton">
<property name="login" ref="login"></property>
</bean>
No, that's not entirely correct.
If scope is "request", then for every new request from client irrespective of session, the spring container will generate new instance.
No. The bean instance will only be created if the code calls a method on the bean (i.e. on the scoped proxy that wraps the actual bean instance). But you're right that every request has a bean instance that is different from the other requests, and that the bean is destroyed at the end of the request (i.e. its life-cycle hooks, if they exist, are called when the request ends).
If scope is "session", then for first request of a session a new instance will be generated by spring container and maintain it for all the client request for that particular session.
Same remark as for the request scope: the bean instance is created on-demand.
If scope is "prototype", new instance will be generated by spring container whenever the bean is requested irrespective of session.
Right. But note that you need to request a bean instance to the spring context to get a new instance. If you inject a prototype to a singleton bean, the singleton bean will keep a reference to this bean for all of its life, and the same prototype bean will thus be used every time the singleton bean calls it.
If scope is "singleton", only one instance generated by spring container and maintain it to be available for all request irrespective of sessions. For every session a copy of singleton instance will be maintained so that the one session singleton object will not share by another session and spring container will manage the life cycle of the copy of singleton instance and close it when session timed out.
No. That is completely wrong. No copy is made at all. A single instance is created, injected everywhere, and used concurrently everywhere. The bean is only destroyed when the application itself ends.

Spring form commandName = session attribute

I have many pages with the same form where I bind my object using commandName. I try to put the object to session with name "myObject" and use it for form (commandName = "myObject"). But system throws exception (Neither BindingResult nor plain target object for bean name "myObject").
How can I bind the object to session for any controllers and requests?
That error is typical when your use a form:form tag that targets a command object that is not available in the request.
A good approach is to combine #ModelAttribute annotated method with #SessionAttributes on a Controller that intially forwards to the view that contains the form, something like
#Controller
#SessionAttributes("myObject")
public class FormController {
#ModelAttribute("myObject")
public MyObject createMyObjectBean() {
return new MyObject();
}
...
}
The initally createMyObjectBean will be called with whatever is the first request to the controller methods, but it won't be called on subsequent request as the myObject value will come from session due to #SessionAttributes
just note that for this approach to work, you must have a controller that forwards to the view that contains your form

jsf converter loses injected property

I had this working before, but then I changed some things, and I can't get it to work again. I am trying to use my service tier to hit the database and get a correct object from my converter class, depending on what the user clicks. I inject the service property into my converter with spring. During debugging, I can see that the property gets sets properly. But then when I go to call getService, it is null.
#FacesConverter("PlaceConverter")
#SessionScoped
public class PlaceConverter implements Converter {
private SearchQueryService searchQueryService;
/**
* #return the searchQueryService
*/
public SearchQueryService getSearchQueryService() {
return searchQueryService;
}
/**
* #param searchQueryService the searchQueryService to set
*/
public void setSearchQueryService(SearchQueryService searchQueryService) {
this.searchQueryService = searchQueryService;
}
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String submittedValue) {
try {
Criteria criteria = new Criteria();
criteria.setId(Integer.parseInt(submittedValue));
return getSearchQueryService().findPlaces(criteria).get(0);
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object value) {
((Place) value).setCategory(" (" + ((Place) value).getCategory() + ")");
return String.valueOf(((Place) value).getPlaceId());
}
}
<bean id="placeConverterBean" class="com.ghghg.converter.PlaceConverter">
<property name="searchQueryService" ref="searchQueryServiceBean" />
</bean>
Dependency injection in a converter works only if the converter is declared as a managed bean by the dependency injection framework in question. E.g. JSF's own #ManagedBean, or CDI's #Named, or Spring's #Component. You should remove the #FacesConverter altogether and reference the converter instance in EL scope instead of referencing it by the converter ID.
Thus, so
<h:inputXxx converter="#{placeConverter}" />
or
<f:converter binding="#{placeConverter}" />
instead of
<h:inputXxx converter="PlaceConverter" />
or
<f:converter converterId="PlaceConverter" />
Your concrete problem suggests that you were referencing it by converter ID (thus, via #FacesConverter). This way you end up getting a converter instance without any injected dependencies.
See also:
How to inject Spring bean into JSF converter
As to the role of the converter itself, this is mandatory because HTML code is represented as one large string and HTTP request parameter values can only be represented as strings. Complex Java objects would otherwise be printed via Object#toString() like so com.example.Place#hashcode, making it unusable in the server side.
I found a better way, and probably more proper way to do get what I wanted. I was not completely sure how the converter works and how the value of the selected item gets passed back to the managed bean. I just declared a new Place object in my method, set the required values. Then I saw that it got passed to my managed bean
I got it to work like this in java EE with jsf 2.0. By making the converter a member of the backing bean. I instantiate this member using CDI but it should work the same with spring.
First the backing bean:
#ViewScoped
#ManagedBean
public class SomeView implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
private SomeConverter converter;
public Converter getConverter() {
return converter;
}
}
And then this is the jsf xhtml:
<p:selectOneMenu id="someId" value="#{someView.value}" converter="#{someView.converter}">
<f:selectItems value="#{someView.values}" var="object" itemLabel="#{object.name}" />
</p:selectOneMenu>
Converter comes to play before updating your model bean. When user fill some input and this value is transferred to server first are updated your server side components and next conversion has happened. Converted values as saved in your bean (with method getAsObject) and before rendering the view values from beans are again converted to String because from user side everything is a string (then method getAsString is invoked).
In summary - Converter methods are the best place to change user input into your application logic, bean fields and in other way to convert your logic, bean fields into user friendly strings.
Due to your question and problem. You mean that SearchQueryService isn't available inside getAsObject method. Try to add an addnotation #Resource with proper name attribute and then it should be injected by your container.

Struts2 and Spring integration thread safe

We are using Struts2-Spring integration and all my action classes implement SessionAware, sample code to action class and their spring definition is given below,
public class IRXxxxAction extends ActionSupport implements SessionAware {
private Map session;
public String execute()
{//}
public void setSession(Map<String, Object> session)
{
this.session = session;
}
}
Spring Configuration
<bean name="userAction" class="com.IRXxxxAction" >
<property name="adminDAO" ref="adminDAO" />
</bean>
If I understand correctly, each auto-wired property will be a singleton, so if the above is true, is there anyway that the session map get shared between two simultaneous requests?
Regards,
Ayush
You have asked same question on the user mailing list and as said if you are using Struts2-Spring plugin make sure to put bean scope as prototype.
Struts2 create new instance of action on each request, since action work as a model also and in order to make it thread safe a new object is being created on each request and placed on value stack.
Not proving scope will be treated by Spring as singleton and for ever request same action instance will be given back which can leads to a lot of issue from data corruption to weird behavior.

RequestScope-Bean init after apply request

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() {
// ...
}

Resources