JSF: Showing page while backingBean is still working - ajax

maybe this title is not really precise, but here's the problem.
I'm doing some JSF project and I need a following:
When user logs on my application (ie. when he navigates to Home page), a method, which gets some money exchange rates from the internet and/or database, needs to be called. The execution of that method (page parsing, data saving etc.) lasts about 5-6 seconds. I don't want that navigation from Login to Home page lasts 5-6 seconds, i want one of these (didn't decide which one yet) to happen:
1) When user logs in, he sees Home page (immediately, not after 5-6 seconds) and is able to click and/or navigate while method is working in background.
2) When user logs in, he sees Home page (immediately), some loading sign in one part of the page and after method is executed, he sees dataTable with exchange rates.
Question is, how to do any of this?
If I put that method in HomePageBean constructor, page wont be displayed until method is executed. Maybe i can put some invisible element on Home page and call that method within some Getter, but i don't know if that's the right way to do (seems like hack) or will it actually work.
I'm using JSF 2.0 and Primefaces.
Thanks in advance

Use ApplicationScoped bean like in example below:
#ManagedBean(name = "exchRateBean", eager = true)
#ApplicationScoped
public class ExchRateBean {
public List<Rate> getRates(Date date) {
// cached data or DAO methods here
}
#PostConstruct
public void init() {
// Initial web-app initialization, will run once upon deploy
}
}
And here is your backing bean accessing application-scoped bean.
#RequestScoped
public class MyBean {
#ManagedProperty(value="#{exchRateBean}")
private ExchRateBean exchRateBean;
// your JSF methods
public List<Rate> getRates(Date date) {
return getExchRateBean().getRates(date);
}
// G&S
public ExchRateBean getExchRateBean() {
return exchRateBean;
}
public void setExchRateBean(ExchRateBean exchRateBean) {
this.exchRateBean = exchRateBean;
}
}

Related

Spring sending user to a view when no view is being requested

I have written a book catalog in Spring.
It collects books (pdf, epub, mobi, ebook) from a directory, collects some metadata from them, stores them in a DB and then puts them in a List that is made available to my views:
#Slf4j
#Controller
public class BookCatalogController {
// == Fields ==
private final BookService bookService;
#Autowired
public BookCatalogController(BookService bookService){this.bookService = bookService; }
// == Model attributes ==
#ModelAttribute
public List<Book> bookData(){ return bookService.getBooksFromMemory(); }
public static final File bookDirectory= new File("D:\\edu_repo\\ebooks_test\\");
.
.
.
// Catalog Simple View
#GetMapping(Mappings.CATALOG_SIMPLE)
public String catalogSimple(Model model){
log.info("catalogSimple method called");
// This is adding the entire BookManager book list into the model.
model.addAttribute(AttributeNames.BOOK_DATA, bookData());
return ViewNames.CATALOG_SIMPLE;
}
// Catalog Detail View
#GetMapping(Mappings.CATALOG_DETAIL)
public String catalogDetail(Model model){
log.info("catalogDetail method called");
// This is adding the entire BookManager book list into the model.
model.addAttribute(AttributeNames.BOOK_DATA, bookData());
return ViewNames.CATALOG_DETAIL;
}
.
.
.
#GetMapping(Mappings.LOAD_BOOKS)
public void loadBooks(Model model) {
bookService.loadBooksFromDirectory(bookDirectory);
}
}
Obviously I'm not using #GetMapping(Mappings.LOAD_BOOKS) properly as you can see in the error below:
The error:
There was an unexpected error (type=Internal Server Error, status=500).
Error resolving template [load-books], template might not exist or might not be accessible by any of the configured Template Resolvers
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [load-books], template might not exist or might not be accessible by any of the configured Template Resolvers
How does one invoke a method like I am doing but without Spring trying to redirect the user to another view?
I'm not expecting the page to update at all since I'm not returning a View!
When you click a link in your browser with a load-books anchor, your browser sends it to the server and waits for result, which causes your page to be reloaded. Once the request to a load-books endpoint reached to the server, Spring MVC handles this and starting to looking up an appropriate controller with its method. It founds public void loadBooks(Model model) in your case. When Spring MVC invokes the method, it expects to obtain a view name to resolve and return back to your browser.
Since you haven't provided a View or String as a return type, Spring MVC used the endpoint's path as a view name (I'm not seeing your Mappings.LOAD_BOOKS constant, but it supposed to be load-books).
If you're not going to return any view back to the browser, you can annotate the method like that:
#GetMapping(Mappings.LOAD_BOOKS)
#ResponseBody
public void loadBooks(Model model) {
which tells Spring to treat void as a response body.
But it's not preventing a page refreshing, you'll just see an empty page after clicking the link. In order to fix this you can redirect a user to another page by returning the following string (without ResponseBody annotation on the method)
return "redirect:/path-to-redirect";
When Spring MVC sees this prefix it redirects you to another controller, but user going to notice that too.
If you really don't want to see a blank page for a moment, you'll have to use some JavaScript to perform AJAX request to the server when button is clicked.
Actually, it seems that you want to preload some files in a service by a given path. If it's all you want to do, you can use Spring's runners like that:
#Component
class Preloader implements ApplicationRunner {
private final BookCatalogService bookService;
#Autowired
public Preloader(BookCatalogService service) {
this.bookService = service;
}
#Override
public void run(ApplicationArguments args) throws Exception {
bookService.loadBooksFromDirectory(BookCatalogController.bookDirectory);
}
}
Spring automatically calls all registered runners when application is ready, so your code will be executed without having a user to visit load-books endpoint.

ZK & Spring - Safe to use Executions.getCurrent() in Spring Bean?

I want to create a utility Bean for common URL parsing in my ZK Composers. However, I want to make sure it is safe to use things like Executions.getCurrent() in a Spring managed Bean. I'm pretty sure it is as Executions.getCurrent() is static to begin with.
Here's what I'm thinking of doing..
#Component
public MyUrlBean {
// TODO I will, of course, program to an interface here =)
private static final String MY_OBJECT_URL_PARAMETER = "my_obj";
public MyObject getMyObjectFromURL() {
Execution ex = Executions.getCurrent();
String value = ex.getParameter(MY_OBJECT_URL_PARAMETER)
// ... db fetch and the like
}
}
..used like so..
#VariableResolver(DelegatingVariableResolver.class)
public MyComposer extends SelectorComposer<Window> {
#WireVariable
public MyUrlBean myUrlBean;
#Override
public void doAfterCompose(Window component) {
MyObject myObject = myUrlBean.getMyObjectFromURL();
// ...
}
}
So, doing things this way, should everything work fine or should I anticipate problems with user sessions clashing or the like?
Spring beans are NOT static singletons, correct? Instead they are instance classes that are autowired to save computation time of actually newing up objects, correct? If that is the case then there definitely won't be clashes between users like this.
Anyway, as I mentioned, Executions.getCurrent() is static. Hmm, how does that work with multiple users accessing a webapp?
Yes, it's safe.
I don't have much official sources to link here, but for what it's worth, my previous team has been using this in almost every page (to get a user context) of an app serving over 3000 users in production with no recorded problem in two years.

Spring JSF application flow

I have previously written Spring MVC web applications where there is a front controller and we have a request mapping in each of the methods and this method in turn invokes a service implementation finally returning a view to the UI. Now when I design JSF applications am not able to understand the flow as such -
This is what I currently have in my application:
The initial index.html redirects to the login page.
A backing bean for the login page which populates label values. Since it is an input form there is no other logic involved.
Once the user clicks on submit -> in the action method I have logic which will invoke the service(No.1) for authentication process and redirect the user to the home page by returning the name of the page
The home page displays various fields which are bound to a backing bean whose fields have to be populated by another web service call(No.2).
It is between the steps (3) and (4), I have a confusion. Previously in Spring I had an explicit mapping and I can "actually" control the logic in the front controller method. In JSF, I dont know whether the logic for No.2 web service call should be combined along with authentication call since I dont have a method to populate the beans.
It is as if I dont have the explicit control over the flow. I have read many articles trying to understand this but not am able to understand. Please provide me pointers and also some references which will actually explain this better.
Why can't you control the logic in JSF bean?Example usage with EJB
#ManagedBean
#RequestScoped
public class LoginBean {
#EJB
private AuthBean authBean;
#EJB
private UserSettings settingsBean;
private String name, password;
#PostConstruct
private void init() {
//do your initialization here
}
public String loginAction() {
User user = authBean.authenticate(user, password);
if(user != null) {
UserSetting settings = settingsBean.getSettings(user.getId());
return "home";
}
}
//setters and getters
}

Struts2 - Session Troubles

I make (thanks with some users on this portal) my application that implements SessionAware.
This is my actual code :
public class UserManager extends ActionSupport implements SessionAware {
private Map<String, Object> session;
#Override
public String execute() throws Exception {
return SUCCESS;
}
public void setSession(Map<String, Object> map) {
this.session=map;
}
public String checkLogin() {
session.put("loggedOn", true);
return SUCCESS;
}
public String checkLogout() {
session.clear();
return SUCCESS;
}
}
And i check these variables on my .jsp :
<s:if test="#session['loggedOn']!=true">
DIV LOGIN
</s:if>
<s:else>
DIV LOGOUT
</s:else>
An easy piece of code.
What i'd like to know is this :
1 - the bean is (as default) request scoped. So when the request is finished it will be destroyed. But i see that, when i put a variable in the Map, it still alive on the server. How is possible? Is a variable of my Bean.
2 - Who call the setSession method? I think the servlet, due to the fact I implements that interface?
3 - I would like to detach about saving object/bean on a Session Object. I'd like to use the Bean session scoped (as for any kind of MVC framework). How can I do it on struts2?
Hope you can make clear these questions :) Cheers
1) Your bean is a struts2 action as such it is action scoped (which is a more restrictive scope than request). I say that action is a lesser scope because you can forward an action to another action in which case the previous action goes out of scope, request scoped objects however will stay in scope until the request is served. When you implement SessionAware you are provided with a reference to the SessionObject you are then putting your object into the Session object who's life span is much longer than your action. Clear your browser cache will remove the session value... you can also remove them by code be implementing SessionAware and removing the value from the map.
2 - The session already exists. You can get the session and add keys but even if you don't put anything in it, it will be there for use.
3 - You have a later question already for this topic see my answer there.

What happens if I forget to mark the Spring SessionStatus as "Complete"?

In Spring MVC, suppose I define a SessionAttribute, using the #SessionAttribute tag like so:
#SessionAttributes(value = "myModel")
public class MyController{
...
}
Suppose that I forget to call status.setComplete() on the SessionStatus like so:
#RequestMapping(method = RequestMethod.POST)
public void doSomething(#ModelAttribute("myModel") MyModel model, SessionStatus status){
...
//status.setComplete(); <-- Never gets called
}
Will the model stay in the session forever? Will it ever get cleaned out, or will the session keep growing larger and larger as the user navigates the site?
There is a big debate on whether the session attributes are cleared after controller exit.
To clarify once and for all, we can look at the Spring MVC 3.1.0 RELEASE source code.
The interface org.springframework.web.bind.support.SessionAttributeStore exposes the following methods:
void storeAttribute(WebRequest request, String attributeName, Object attributeValue);
Object retrieveAttribute(WebRequest request, String attributeName);
void cleanupAttribute(WebRequest request, String attributeName);
The default implementation is org.springframework.web.bind.support.DefaultSessionAttributeStore
By doing a "Open Call Hierarchy" on cleanupAttribute() in Eclipse, we can see that the method is called by 2 different flows:
1) org.springframework.web.method.annotation.ModelFactory
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
if (mavContainer.getSessionStatus().isComplete()){
this.sessionAttributesHandler.cleanupAttributes(request);
}
else {
this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
}
if (!mavContainer.isRequestHandled()) {
updateBindingResult(request, mavContainer.getModel());
}
}
2) org.springframework.web.bind.annotation.support.HandlerMethodInvoker
public final void updateModelAttributes(Object handler, Map<String, Object> mavModel,
ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {
if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
}
}
...
}
It is clear that in both cases, the session attribute is removed only when this.sessionStatus.isComplete() is called.
I dug into the code of DefaultSessionAttributeStore. Under the hood, it gets the real HTTP Session object to store attributes, so they can potentially be accessed by other controllers in the same session.
So no, the session attributes are not removed after a clean POST.
EDIT #2: Note that this answer is no longer correct. See #doanduyhai's answer below.
EDIT: Please note that this is for Spring 2.5 and may, but does not necessarily ensure that the same is for Spring 3.x. Double check the docs!
This is along the lines of what #Gandalf said.
Form controllers model a form request lifespan, from initial viewing of the form through form submission. After the form is submitted, the form controller's job is done, and it will remove the command object from the session.
So, to keep the command object in the session between form workflows you will need to manage the session manually. After a clean POST, the object is removed from session.
In short, I believe the setComplete() method is just good practice but is not necessarily required.
EDIT: I just looked in my Spring book to confirm this. I'll quote it:
When #SessionAttribute is not used, a
new command object will be created on
each request, even when rendering the
form again due to binding errors. If
this annotation is enabled, the
command object will be stored in the
session for subsequent uses, until
the form completes successfully. Then
this command object will be cleared
from the session. This is usually
used when the command object is a
persistent object that needs to be
identical across different requests
for tracking changes.
Essentially that's what I was saying above. It stores it in the session until you either A) call setComplete() or B) the controller successfully completes a POST.
Is there some reason why you would want to do that?
from this thread : #SessionAttribute Problem
The #SessionAttributes works in the same way as the sessionForm of the SimpleFormController. It puts the command (or for the #SessionAttributes any object) in the session for the duration between the first and the last request (most of the time the initial GET and the final POST). After that the stuff is removed.

Resources