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...
Related
What I want is different different session bean per tab/ window. I know session bean works but I want to create new sessionbean for new tab. Below is reason why I want new session bean.
Suppose on page1, I have list of users. On clicking user, I get details of user in page2.
Now suppose I have two tab. In both pages I have page1.xhtml.
On tab1, I clicked on User A. I see details of User A on page2.xhtml
Now I come on tab2.
On tab2, I clicked on User B. I see details of User B on page2.xhtml
Problem is here now. Now I come back to tab1 and refresh the page2.xhtml, I see User B details which is incorrect because earlier I was seeing User A details.
Hence what I want is new sessionbean per new tab/ window.
Is there any way to create new sessionbean per tab/ window? In primefaces or icefaces maybe?
I thought ViewScope was solution, but that was not. Referring BalusC article.
#ViewScoped: a bean in this scope lives as long as you're interacting with the same JSF view in the browser window/tab. It get created upon a HTTP request and get destroyed once you postback to a different view.
With SessionBean, I meant Java/ JSF managed session managed beans and not browser session (history).
If you are using CDI Conversation scope could help.
From the Java EE docs: ConversationScoped
These tutorials might help.
CDI Conversations Part 1
CDI Conversations Part 2
Hope this helps !!!
#ViewScoped is the solution to this problem, indeed. The problem looks to be that you're setting the UserX data (id, detail or something else) into session when navigating from page1.xhtml and then loading this session data in page2.xhtml. So, if this is your current situation, not even CDI #ConversationScoped will help you (since it uses cid query string param).
In order to solve this, you should make a GET request of your page2.xhtml and send the userId (or another useful parameter) to recover the details of your UserX.
More info:
How to choose the right bean scope?
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Note that you can encrypt the parameter (let's say, the ID) to add some security to your parameters. Please refer to the second proposed link in order to achieve this.
I have a JSF page where users can enter their car into my database. The form has three input fields:
Manufacturer
Model
Registration
The Manufacturer and Model field are both autocompletion fields. In order to complete the Model field, I need to know what value the user selected in the Manufacturer field prior to submitting the form. To that end, I set up an AJAX call to inform the managed bean of the selected value:
<p:autoComplete id="manufacturer"
minQueryLength="3"
completeMethod="#{carController.complete}"
forceSelection="true">
<p:ajax event="itemSelect"
listener="#{carController.manufacturerSelected}" />
</p:autoComplete>
And in the managed bean:
public void manufacturerSelected(SelectEvent se) {
manufacturer = se.getObject().toString();
}
The autocomplete field and handler method for the model look about the same, with slighly different values.
To retain the manufacturer value across the multiple XHR requests, I have set the bean to ConversationScoped and begin the conversation in a #PostConstruct annotated method:
#Named
#ConversationScoped
public class CarController implements Serializable {
#Inject
private Conversation conversation;
#PostConstruct
private void init() {
conversation.begin();
}
What I would expect is the bean getting only instantiated once for the page because the conversation has not been ended yet, and retaining the value in the manufacturer field. This, however, does not hold true, and the bean is instantiated again for each XHR request, causing the manufacturer field to be null as well. Setting a breakpoint in the PostConstruct method revealed that it is in fact getting called and so is the manufacturerSelected method.
I suspect this has something to do with the fact that I am not manually propagating the conversation ID but the documentation says that this ID should automatically be propagated with any faces request. Is this in fact true, and, does that mean that XHR requests are not necessarily faces requests?
Edit: Setting breakpoints at various locations in the bean has revealed that each XHR request has a new bean (conversation.getId() keeps changing) so I am obviously not propagating the ID right. How would one propagate the ID with p:ajax and where can I get it with EL?
Since you're not really using a conversation (at least not in the example you give), why not use the view scope? This will do exactly what you want, without the overhead of having to propogate a conversation id.
Do note that for #ViewScoped to work, you have to change the bean to a JSF managed bean (remove #Named). This is due to a design bug/spec oversight in Java EE 6. If you want to keep using CDI, then there's a Java EE 6 extension from Seam 3 available that fixes this bug.
Yup. This is an issue with JSF 2 and Primefaces together not propagating the conversation id. You can try the below workaround. (This is for other who will end up here due to the same issue).
<p:autoComplete id="manufacturer"
minQueryLength="3"
completeMethod="#{carController.complete}"
forceSelection="true" onchange="updateManufacturer([{name:'cid',value:'#{javax.enterprise.context.conversation.id}'}])">
</p:autoComplete>
<p:remoteCommand name="updateManufacturer" action="#{carController.manufacturerSelected}"
process="manufacturer" />
I'm using JSF (Mojarra 1.2) with Richfaces (3.3.2) within some facelets which are used as portlets (I'm using the Jboss Portlet Bridge 2.0 here). Now I'm facing something strange: I've got an actionlistener on my <h:commandButton> which is triggered, when the button is clicked but when I simply reload the page, the action is executed everytime I load the page again. This happens only if I already triggered the action before. Is this behaviour normal?
I should notice that Spring 2.5 is used to manage my beans, the mentioned beans are session-scope beans, maybe this is a interessting point?!
Yes, reloading a HTTP POST request will execute the HTTP POST request again and thus trigger all associated server-side actions again. This issue affects all webapplications in general and is not per se related to JSF.
A well known fix to this is the POST-Redirect-GET (PRG) pattern. Basically you need to redirect the POST request to a GET request immediately after processing the action, so that the result page will be delivered by a HTTP GET request. Refreshing this HTTP GET request won't execute the initial HTTP POST request anymore.
This pattern has however one caveat: since it concerns a brand new request, all request scoped beans are garbaged and renewed in the new request. So if you'd like to retain the data in the new request, you would need to either pass them as GET parameters or to store it in the session scope. Usually just reloading the data in bean's constructor is sufficient. But since you mention to use session scoped beans only (which is however not the best practice, but this aside), this shouldn't be a big concern for you.
Turning on PRG in JSF is relatively easy, just add the following entry to the associated <navigation-case>:
<redirect />
Or if you prefer to fire it programmatically, then make use of ExternalContext#redirect() in the bean's action method:
public void submit(ActionEvent event) {
// ...
FacesContext.getCurrentInstance().getExternalContext().redirect(someURL);
}
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.
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.