I have a dynamic Facelets page that needs to show information from database when the page loads. At this point in the flow, there have not been any form submissions. Every JSF example I can find only shows a form submission with dynamic results on the next page.
Every call I make to our database is currently takes place after an action has been triggered by a form submission. Where should this code go if there hasn't been a form submission, and how do I trigger it? A code snippet would really help me out!
You should be able to do your initialization work in the constructor (or lazily in one of your accessors) of your managed bean.
If you're using Spring integration (see here also), it's easy.
In your backing bean, simply use something like:
public class BackingBean implements InitializingBean
{
public void afterPropertiesSet()
{
loadInitialData();
}
}
If you're not integrating with Spring there are two options:
Load the initial data in the class constructor;
In your faces-config.xml, you can set properties to be injected. Properties are guaranteed to be set in the order they're specified in the config file. So, just create a dummy property and then within that method load up your default data. i.e. create a method public void setLoaded(boolean loaded) { loadInitialData(); }, and in your faces-config.xml have 'loaded' being set as a property on that backing bean.
Hope this is all clear!
You write (with my emphasis added):
Every call I make to our database is currently takes place after an action
has been triggered by a form submission. Where should this code go
if there hasn't been a form submission, and how do I trigger it? A
code snippet would really help me out!
It sounds to me that you want to retrieve information from the database prior to form submission.
It seems to me that you want to make an Ajax call to query the database. The Ajax call can fire on a different event than the form submisson event. This will probably entail using Javascript rather than the Faces framework.
Related
I would like to call a controller method using a button on a JSP page in Spring MVC, but I would like it to stay on a current page, don't reload it or anything, simply call a method. I found it difficult. My button is on cars.jsp page. In order to stay on this page I have to do something like this:
#RequestMapping(value="/start")
public String startCheckingStatus(Model model){
System.out.println("start");
model.addAttribute("cars", this.carService.getCars());
return "car\\cars";
}
button:
Start
But this is not a good solution because my page is actually reloaded. Can I just call controller method without any refreshing, redirecting or anything? When I remove return type like so:
#RequestMapping(value="/start")
public void startCheckingStatus(Model model){
System.out.println("start");
}
I got 404.
Add an onclick event on your button and call the following code from your javascript:
$("#yourButtonId").click(function(){
$.ajax({
url : 'start',
method : 'GET',
async : false,
complete : function(data) {
console.log(data.responseText);
}
});
});
If you want to wait for the result of the call then keep async : false otherwise remove it.
As mentioned elsewhere you can achieve this by implementing an Ajax based solution:
https://en.wikipedia.org/wiki/Ajax_(programming)
With Ajax, web applications can send data to and retrieve from a
server asynchronously (in the background) without interfering with the
display and behavior of the existing page. By decoupling the data
interchange layer from the presentation layer, Ajax allows for web
pages, and by extension web applications, to change content
dynamically without the need to reload the entire page.
To achieve this you will need to make changes to both the client and server side parts of your app. When using Spring MVC it is simply a case of adding the #ResponseBody annotation to your controller method which:
can be put on a method and indicates that the return type should be
written straight to the HTTP response body (and not placed in a Model,
or interpreted as a view name).
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody
Thus, for example, to return a simple String in the Ajax response we can do the following (without the #ResponseBody the framework would try and find a view named 'some status' which is obviously not what we want):
#RequestMapping(value="/start")
#ResponseBody
public String startCheckingStatus(Model model){
return "some status";
}
For the client part you need to add some javascript which will use the XMLHttpRequest Object to retrieve data from your controller.
While there are various frameworks which can simplify this (e.g. JQuery) there are some examples at the below using vanilla javascript and it might be worth looking at some of these first to see what is actually going on:
http://www.w3schools.com/ajax/ajax_examples.asp
If we take this specific example:
http://www.w3schools.com/ajax/tryit.asp?filename=tryajax_callback
and [1] copy the <button/> and <script/> elements to your JSP, [2] change the URL to point to your controller and, [3] create an element <div id="demo"></div> in your JSP then, on clicking the button in your page, this <div/> should be updated to display the String returned by your controller.
As noted this is a lot of code for one action so you can use some JS framework to abstract a lot of it away.
Expectation:
When i attempt booking, booking has been failed due to price change and throwing below price change exception.
Then i need to capture that errors in event and redirect the request to previous page and display the error message on top of the page.
As per my below logic i can able to redirect the request to previous page, while throwing price change exception but the issue is i can't able to
display my error message (Your booking price has been changed, please choose another) on previousPage.
Anybody can help me out. Thanks in advance.
BookAction.java
public class BookAction {
private static final String PRICE_CHANGED_ERROR = "Your booking price has been changed, please choose another";
public Event book(RequestContext context) {
try {
// Booking attempt
// Consider booking attempt failed and throwing price change exception
}catch (PriceChangeException priceChangeException) {
return handlePriceChange(context);
}
}
public Event handlePriceChange(RequestContext context) {
Errors pageErrors = getPageErrors(context);
if (true) { // Consider if its true and returning new Event "searchPage"
pageErrors.reject(PRICE_CHANGED_ERROR);
return new Event(this, "searchPage");
}
return error();
}
}
booking.xml
<webflow:flow>
<webflow:action-state id="book">
<webflow:evaluate expression="bookAction.book" />
<webflow:transition on="success" to="confirm" />
<webflow:transition on="error" to="handleFrd" />
<webflow:transition on="searchPage" to="searchPage" />
</webflow:action-state>
<webflow:end-state id="searchPage" view="externalRedirect:#{flowScope.bookState.searchResultURI}" />
</webflow:flow>
Unfortunately, this is a poorly documented part of SWF. What you are looking for is to use the flashScope. flashScope was specifically designed for this purpose after a variable in the flashScope is read once it is automatically deleted.
But... the design problem in SWF is that the flashScope only works between a Parent flow and an embedded child flow inside of it. It will not hold save your variables if you use an "externalRedirect" and try to access the error using "flashScope.error"... because technically the first flow ended and a new flow has begun. So how to solve this problem?
I was only able to solve it with 2 ways and both are hacks.
Option 1.
if the message is simple then in the 'externalRedirect:#{flowScope.bookState.searchResultURI}' append a HTTP GET param such as
?msg=An Error Occurred.
Although this will look ugly in the url bar, it is simpliest and the least painful solution.
Option 2.
is a bit more painful but provides the most flexibility if you want complex error msgs. Basically you have to merge the flashScope of Spring Web Flow and the flashMap of Spring MVC and send your externalRedirect to a spring mvc controller first.
You will need to create and register a FlowExecutionListenerAdapter
to merge (hack) the flashScope and the flashMap see the link directly below for step by step instructions (ignore the stuff about sessionMap)
Spring web flow how to add flash attribute when exiting from flow to external redirect
Then once you achieve that you can easily access your flash variable error msg using Spring MVC like so:
How to read flash attributes after redirection in Spring MVC 3.1?
Note: the variable will already be in the flashMap of spring mvc. So all you have to do is render a view in spring mvc and access the variable from the view.
My 2 cents: because of limitations like this I've personally stopped using webflow and only use Spring MVC exclusively now. I think Spring Web flow is still useful for very simple use cases (flow A -> flow B -> flow C) but if you're trying to to do something more complex or are trying to learn SWF from scratch right now... my advice to you is to just use Spring MVC... it will save you a lot of time in the long run. I think they are working on a major revision for SWF (3.0) maybe then all these limitations will get worked out
Indeed Spring Web Flow has a number of scopes where variables can be stored but when a flow ends all scopes end as well, unless the flow is a child flow delegating control back to the parent flow in which case you can use flash scope in addition to any parent flow scoped variables.
Spring Web Flow also knows how to interact with the Spring MVC flash scope. So when a top-level flow ends and the redirect is to a Spring MVC endpoint, there is a way to indicate that output variables should be put in Spring MVC flash scope, which would make them available after the redirect.
This is mentioned in the reference documentation http://docs.spring.io/autorepo/docs/webflow/current/reference/html/spring-mvc.html#spring-mvc-flash-output.
in booking.xml, add below changes
<webflow:transition on="searchPage" to="searchPage">
<set name="flowScope.error" value="'text u wanna show in next page'" type="string" />
</webflow:transition
And now in the redirected page try reading that text. hope it would work!
Use flash scope:
In xml
<set name="flashScope.flashScopeAttribute" value="'f'" />
In JSP
flash scope: #{flashScopeAttribute}
When developing a Web Application in Java, launching in Tomcat, I need to be able to create (dynamically) a new static address (link,URL) in the server that will be used to view the information of a new item, let's call it new_item_001, which have been just created by one user.
Say I want to create a new address
www.domain.com/webapp/items/new_item_001
which can be used to render a view of the contents of new_item_001.
Which is the best approach to do this?
Should I dynamically create a new servlet class for this view?
Should I dynamically create the folder items and one html file new_item_001 for this item inside of it?
Should I edit the server address mapping rules to create this static address and map it to a central servlet which somehow knows which item to display?
I understand the question is ill posed, and that I am far from even understanding the issue, so I would like some guidelines on what to look for.
None of the above.
You should simply have a servlet mapped to /items/*. When a request come to this servlet, analyze the actual path of the request, extract the part after /items/ to know the actual value (new_item_001) in your example, get the data corresponding to this item from the database, and send it to the browser.
Using a true MVC framework like Spring MVC would make that much easier. You could simply map a method of a controller using
#RequestMapping("/items/{itemId}")
public Item getItem(#PathVariable("itemId") String itemId) {
...
}
and let the framework do all the URL parsing for you.
I would like to tackle this in a simple way. Creating a servlet for each created item would be overkill and become quite cumbersome to manage after a successful run of the application for some time.
Changing/editing server mapping URL looks very naive approach and is not scaling too. Let configuration be there and change them only when you actually need to change them.
My suggestion is to create one servlet that handles all these requests. For example, you may save item information on a datastore or on file system(i.e images uploaded by user etc..). Next time a GET request is received by the application to fetch saved information of an item, servlet should be able to reference the item on database associated with the item id on the URL. If you don't wish to expose item id/surrogate key in the database, you can also have a simple mapping between them by implementing your own logic. Frameworks like Spring MVC do a good job in mapping URLs to resources like this should you wish to use a framework.
Additionally to minimize the number of requests to the same item, you can also implement an HTTP caching strategy(i.e. ETAG, If-Modified-Since) by instructing your web server at the time of first GET request from a user.
I have a small JSF application and the scope of the pages are request. I would like to have a method in my JSF's managed bean for logging user_id and page name to database. When I added this method to the constructor of bean, details are getting logged for every method is being called due to the fact that scope of the page is request.
What is the best way to log the above details only once when user first access the pages after logging into my application? Idea is which all pages user visited for each logging.
What is the best way to achieve this?
My JSF version is 1.1.
Thanks
You can add a method to the bean and invoke it from the page when the page is loaded. For example
add this into the page:
<h:outputText value="#{fooBean.logUser}"/>
bean method:
public void logUser(){
//log user and page
}
This way the method is only invoked once the page is loaded. If you get a method not found
error, then another option is to use a hidden form hack. For example:
<body onload="document.getElementById('formId:buttonId').click()">
<h:form id="formId" style="display: none;">
<h:commandButton id="buttonId" value="submit" action="#{fooBean.logUser()}" />
</h:form>
...
</body>
So, you want to log into a database table all accesses of all users to the different pages of your site but, avoiding logging post-backs, right? (a post-back happens when a user performs an action in one of your pages and the URL that receives the action is the same than the one from where the action was launched - it's very common in JSF, specially on versions prior to 2.x)
So, basically, what you need is to recognize when a post-back happens and avoid logging under that condition. The ResponseStateManager will provide you with a isPostback(...) method that returns true when processing post-back requests:
FacesContext context = FacesContext.getCurrentInstance();
ResponseStateManager rsm = context.getRenderKit().getResponseStateManager();
if (!rsm.isPostback(context)) {
// do whatever you need here
}
By the way, I wouldn't recommend to put that code in the bean constructor at all as different JSF implementations may perform some bean lifecycle management tasks right after invoking the bean's constructor. If you think that logic should be part of the "bean initialization process" I suggest putting that code in a separate public method annotated with a #PostConstruct annotation.
However, if you want that logic to be run in every single access to the given pages, using a managed bean will force you to either copy & paste that logic in every bean or define an abstract/base class with that logic and make all your beans extend that one. I like to keep all my managed beans as close as possible to the POJO concept so, as an alternative, I would consider implementing the same logic but using a PhaseListener tied to the RENDER_RESPONSE phase. It will give you a under-the-hood (much cleaner) and versatile approach: if some day you want to log actions instead of renders then you tie your listener to the INVOKE_APPLICATION phase, etc.
P.D.: I hope you are using some kind of cache or 'batching' technique to log those records to your database, otherwise you will have a noticeable performance impact when too many users are surfing your site. Also, in my own opinion, you should update your JSF version to, at least, 1.2, and you'll avoid some annoying bugs or unexpected behaviour...
I have a JSP struts application that uses Spring to integrate with the services/dao/database. Basically, struts uses spring to get the data from the DB, it builds the form and forward them to the JSP files.
I have a header file that is injected in each JSP file using Tiles. I would like to show "Welcome John Doe" on each page inside the header. Where "John Doe" is the name of the currently logged user.
What would be the best approach to do that? The solution that I can think of is:
Use a Spring Filter the catch the http request. Load the user from the database using a cookie that contains the user id(*) and put the name in a session bean named "CurrentUser"
In "header.jsp", get the spring application context. Using it, load the bean "CurrentUser" and get the name. Put the name in the html.
I think I could get this to work. But I'm not certain this is the best way to do it. Any thought on my approach?
(*) Of course, the cookie will be encrypted
Although it may be an extremely large hammer for your fairly simple use-case, we have gotten a really neat spring-jsp integration (jsp 2.1 required!) by using ELResolver. By following this tutorial you can basically inject any spring managed bean into your el-context and allow it to be accessed using jsp-el like this:
${spring.mybean.myproperty}
You can choose to inject pre-defined beans into your el-context or simply pass "mybean" to getBean and allow almost anything spring-managed to be accessible from jsp. mybean could easily be a session-scoped spring bean.
I'm not totally sure how this would align with tiles, though.
Are you not already storing some sort of User object in Session?
If so, I would just add a "getFullName()" method to this domain object and have the DAO populate it when it returns it. Ideally you should populate the User object when the user logs in, store it in session, and not need to load all of the user's details again from the database on each and every page request.
(Are you not using Spring Security? If so, they provide a pretty simple way to store a UserDetails-like object in Session, and easy access to it.)
I'd vote against both of your approaches because
This means (at least) an extra database call per page request
This wouldn't work if other users shared the same bean in the same context. Also, you really shouldn't have JSP files (which are your presentation layer) interacting with data services directly.