I'm working on a JSF 1.2 + RichFaces system. This system is a kind of blog. Users can create their blogs and manage them. Blogs are accessible by a url type this:
www.meublog.com/NameOfBlog
I used a single Managed Bean with session scope to make all controls. When the user accesses the blog, I use a filter that through the URL, which identified the blog being accessed, put the ID of the blog in the session and gave foward to the blog index.
I used this session ManagedBean to control everything in the view of the blog.
The problem is that the browser shares the same session between multiple tabs. When a user accesses the blog like this www.meublog.com/julio on one tab and www.meublog.com/fulano in another tab, I can not identify the two blogs because I have only one session established.
I wonder to know if anyone knows the correct path to follow here ...
You should not store request scoped information in the session scope for exactly the reasons you're facing. Use a request scoped managed bean instead which get initialized based on the request URI. You can get most of the request details by ExternalContext and/or HttpServletRequest.
E.g., inside bean's constructor or #PostConstruct method:
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
// If you use suffix mapping like *.jsf
String servletPath = ec.getRequestServletPath();
// Or if you use prefix mapping like /faces/*
String pathInfo = ec.getRequestPathInfo();
// Now initialize based on the value of either servletPath or pathInfo.
See also:
How to choose the right bean scope?
Related
I have a web application which uses JSF 2.3 for the front end and offers also an API which is consumed by the front end with Jersey 2.29.1 running on a Tomcat 9 server.
The front end offers a login which will store the authenticated user in a #SessionScoped AuthenticationBean. The API methods should only be consumable if the user is logged in on the front end.
#Path("/service")
public class ApiService {
#GET
#Path("/data")
#Produces(MediaType.APPLICATION_JSON)
public String loadData() {
final AuthenticationBean authBean = CDI.current().select(AuthenticationBean.class).get();
if (authBean != null && authBean.isLoggedIn()) {
// Do business logic
}
else {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
}
}
On the JSF page the API is consumed with a 'basic' jQuery AJAX call $.ajax() and updates some visual content based on the JSON response. The URL which I pass to the AJAX call is "#{request.contextPath}/api/service/data".
Everything works fine as expected, until I disable the cookies in the browser. If the cookies are disabled, the session ID is added to the URL (instead of being stored in a session cookie) by the servlet engine. So if I do not explicitly append the session ID to the URL for the AJAX call I have no access to the current session in the API service method, e.g. "#{request.contextPath}/api/service/data;jsessionid=#{session.id}" and therefore I can not check whether the user is logged in or not.
My question is now if I do have to add the jsessionid manually for every AJAX request or is there any other 'clean' possibility to pass the session ID to the service methods? Or do I have to access the session in any other way in the API service?
... explicitly append the session ID to the URL ...
The HttpServletResponse#encodeURL() does exactly that task, see also javadoc (emphasis mine):
Encodes the specified URL by including the session ID, or, if encoding is not needed, returns the URL unchanged. The implementation of this method includes the logic to determine whether the session ID needs to be encoded in the URL. For example, if the browser supports cookies, or session tracking is turned off, URL encoding is unnecessary.
So, basically:
#{request.contextPath}#{response.encodeURL('/api/service/data')}
The same method is by the way delegated by ExternalContext#encodeResourceURL().
Return the input URL, after performing any rewriting needed to ensure that it will correctly identify an addressable resource in the current application.
Jakarta Servlet: This must be the value returned by the javax.servlet.http.HttpServletResponse method encodeURL(url).
So, technically you can also do
#{request.contextPath}#{facesContext.externalContext.encodeResourceURL('/api/service/data')}
But this is a bit less convenient to type down. Moreover, using #{request.contextPath} already indicates that you're using JSF on top of servlets, not portlets, so using #{response} should be perfectly fine. You might want to make it yet shorter by defining a custom utility method in an application scoped bean. E.g.
#{functions.encodeURL('/api/service/data')}
#Named #ApplicationScoped
public class Functions {
public String encodeURL(String uri) {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
return ec.getRequestContextPath() + ec.encodeResourceURL(uri);
}
}
This 'Answer' is not an answer to your real question but an explanation on why this is in no way JSF related (at the end there is a hint though to an answer)
JSF is an api and Jersey is an implementation of an api (JAX-RS). They effectively are complementary technologies and have no relation to one another in any way. It's the same like asking can I use JPA and Jersey in one application. Your actual questions have like stated no relation to JSF whatsover. JSF uses the session mechanism provided by the servlet engine, just like jax-rs does.
The second thing that is unclear is what your definition of 'front-end' is and where you run what. JSF in the JavaEE 'stack' is a front-end technology. JSF has a server side part in which you declare the components(See What is the definition of "component" in JSF) and when the html is generated they have client side html/javascript/css counterparts which communicate with the server in a way that is specified in the JSF specs. JSF 'components' by themselves do nothing if not backed by 'glue logic' on the server that in turn calls services (See JSF Controller, Service and DAO)
It might by now be clear that
is it possible for a JSF component to have called a Jersey service by an AJAX call?
is 'vague' and most likely given by your lack of knowledge of what the JSF is/does and how Jersey fits in (Jersey should be 'rest' here or jax-rs if you mean the api), See also How to implement JAX-RS RESTful service in JSF framework
JSF Components' html part on the client side communicates with the server in a JSF specific way, so
Rest is not needed there to get the client-side to communicate with the server (superfluous)
Trying to get rest in there makes things overly (overly) complex and without a real advantage
If you are trying to get the JSF server side components to communicate via REST
The server side is for interaction with the client side by means of the JSF spec for it
They are backed by code that in turn calls services. In this you can call a rest service, database or whatever
If, like I suspect, you want a non-jsf related $.ajax to call some rest service
You totally can, but read How to implement JAX-RS RESTful service in JSF framework
You can do anything with it to update the client side html
You can even update the html generated by JSF components
When doing 3, do NOT expect them to still work al the time, JSF is not to blame here
So after all this, your session id problem is not a jsf related problem. You'd have the same problem when using plain jsp or even plain html that has triggered a session.
HINT
So a better title would have been "jsessionid in url not added to ajax call in jquery" I posted this one in a search engine and added site:stackoverflow.com: jsessionid in url not added to ajax call in jquery site:stackoverflow.com
One of the results is effectively a duplicate of your question:
Appending my jsession ID at every ajax call by Jquery
I use JSF and I want to avoid a same session sharing 2 tabs in my browser. I think an easy way is to force url rewriting instead of using cookies.
Can anyone tell me how I can force the url rewriting with JSF?
Thanks.
Stéphane
I want to avoid a same session sharing 2 tabs in my browser
Sorry, but this makes no sense. This is not something which you can control from the server side on. All browsers use the same session in all opened tabs/windows (expect of anonymized tabs/windows like Chrome Ingognito via Ctrl+Shift+N). That's just how all browsers work and completely beyond your control.
If you're having a problem with it, then you should absolutely solve it differently than attempting to disable session sharing in multiple browser tabs/windows (which simply isn't possible). It sounds much like as if you're incorrectly storing request or view scoped data in a session scoped bean. You should not do that. You should store request scoped data in a request scoped bean and view scoped data in a view scoped bean. The session scope should only be used for session scoped data, such as the logged-in user and its preferences like language settings.
I think the view scope is actually what you're looking for; it lives as long as you're interacting with the very same view (read: the very same browser window/tab) by postbacks and it it not shared in other browser windows/tabs.
See also:
How to choose the right bean scope?
Currently, I am creating a web application for an online shopping cart and I need to maintain session on each jsf page..
My questions are :
How can I create and destroy session in managed bean
How can I access value stored in session variable? Like this?
FacesContext.getCurrentInstance().getExternalContext().getSessionMap.put("key",object);
How can I destroy a session in jsf
I also need to destroy the session using session.invalidate() but i am failed !!
How can I create and destroy session in managed bean
You don't need to create it yourself. The servletcontainer will do it automatically for you on demand. In other words, whenever you (or JSF) need to set an object in the session scope, then the servletcontainer will automatically create the session. In a JSF web application, this will happen when you
Reference a #SessionScoped or #ViewScoped managed bean for the first time.
Obtain the session by ExternalContext#getSession(), passing true for the first time.
Store an object in session map by ExternalContext#getSessionMap() for the first time.
Return a page with a <h:form> for the first time while the state saving method is set to "server".
You can destroy the session by ExternalContext#invalidateSession(). E.g.
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "login?faces-redirect=true";
}
Remember to send a redirect afterwards, because the session objects are still available in the response of the current request, but not anymore in the next request.
How can I access value stored in session variable?
Just make it a property of a #SessionScoped managed bean. Alternatively, you can also manually manipulate the ExternalContext#getSessionMap(), yes.
How can I destroy a session in jsf
This is already answered in the first question.
See also:
How do servlets work? Instantiation, sessions, shared variables and multithreading
Basic steps for starting session in jsf
How to choose the right bean scope?
I'm working on a JSF (v1.2) application. In my application I need a generic servlet which could serve any resource (PDF, Images, Excel, etc). My idea is to ask the caller to send the required information so that I can find out the correct delegator class using some configurations.
This delegator class will take care of serving the correct resource.
For example this is the request url
http://example.com/servlet?delegatorid=abcd
My Servlet code is something like this.
protected void doGet(HttpServletRequest request, HttpServletResponse response){
String delegatorID=request.getParameter("delegatorid");
//Get the configuration from Configuration table
configuration=getConfiguration(delegatorID);
//invoke the method of the delegator class based on this configuration
Object result=invokeMethod(configuration);
//write the response to the stream
}
My question is what is the best way to do this in a JSF project?
Should I completely avoid JSF dependency in this operation? I can find the delegator method and class and invoke it using reflection. Will there be any potential restrictions in future if I avoid JSF dependency. [One problem which I can think about is, in one of the code, I need to get the user information from session. I'm doing this through FacesContext. Since FacesContext is not available, it will fail, I should have another option to get the session.
If I have to introduce JSF dependency, how do I get the FacesContext
here? As far as I know, only the beans that are stored in
application scope can be accessed here. I don't want to do that. Is there any other way of getting it?
Instead of using a servlet, can I do this by invoking a ManagedBean
method directly using the URL? This will give me FacesContext. I
think I need to have a dummy JSP page for the managed bean method to
get invoked.
Could you please let me your thoughts on this?
The FacesContext (and ExternalContext) is just a facade over HttpServletRequest, HttpServletResponse, HttpSession, ServletContext, etcetara along with some JSF specifics which you don't need at all in a plain vanilla servlet. The ExternalContext#getSessionMap() is nothing more than an abstract mapping of HttpSession#get/setAttribute().
In a plain vanilla servlet, the session is just available by request.getSession() and the application by getServletContext() the usual way. See also among others this related question: Get JSF managed bean by name in any Servlet related class.
You can also just refactor code which needs to be shared by JSF and Servlet into an utility method which doesn't have any dependencies on javax.faces.* nor javax.servlet.* classes (or at most only javax.servlet.*) and finally let the callers each pass the necessary information through.
We have the following problem...
Application's environment:
JSF, Richfaces, a4J
Consider having the following scenario:
The user logs into the system
The user navigates to a new page which consists of an a4j form containing a4j components, the user fills into the form but doesn't submit.
The user opens a new Tab and opens the same URL and fill in the new form with new data
The user returns to his first Tab and submits the information (Note: All beans are defined are session scope)
Result:
The submitted information is the information from the second Tab but submitted from the first Tab, which is expected as long as the beans are defined as session scope.
Problem:
We need to get the behavior of a request scope (i.e: dealing with new tab as a new request although the bean is defined as a session scope).
Notes:
When defining the bean scope as a request scope the partial Ajax response from individual components in the same form, resets the other components since they are not submitted yet.
Any suggestions ?
--
Thanks so much
This is a well known problem for Web applications.
Of course you can try to solve this problem using more custom code
but my quick suggestion is to use the seam framework which solves exactly this.
Seam is a superset of JSF and introduces a new conversation scope for
beans that does exactly what you want.
Seam supports richfaces natively (both are projects of JBoss/Redhat) so
you should not expect any problems with integration.
What is the reason the bean needs to be in session scope ?
If this is only to get ajax functionality then you can change the bean to request and use the a4j:keepAlive tag.
a4j:keepAlive extends the live cycle for the request scope bean, your bean instance then acts like it is in session scope for ajax requests. When the user opens two of the same page they are using two different bean instances.