I've been working with Seam recently and temporary conversations still confuse me.
In our project we're using ExternalContext.redirect() to redirect the user to the response page. From what I've read, a Seam conversation ends when the render response phase is called.
But then I read somewhere else that: Seam transparently propagates the conversation context (including the temporary conversation context) across JSF postbacks and redirects.
So what happens when I redirect to the same page is that commandLinks have the same conversationId appended to its action url always. I've tried to end the temp conversation with <f:param name="conversationPropagation" value="none"/>, as the page says, but that causes that when the redirected page is rendered, the conversation context is already fred and a bean we where using can't be no longer used in the response.
So what I want to know, is that if there's a way to end the conversation with a redirect and mantain the context until the rendering of the response?
If not, when does a temp conversation really end? Because since the conversation context is propagated through redirects and postbacks, then it seams to me it will never end.
I thing CONVERSATION scope is automatic maintain by a SEAM Frame work throw UI if you want to create a new conversation then you can do like this.
<h:commandButton id="conId" value="New Conversation"
action="#{conversationManager.clearConversation('/views/Hello.seam')}">
</h:commandButton>
i am not sure but if you want to close a CONVERSATION then you can try like this.
getConversation().end();
i am not sure it will work or not.
Related
I have a web app that uses a Java Bean for login functions right now and all of the JSP pages check the Bean to make sure the user is logged in. I am also introducing some AJAX functionality now with servlets and I see that of course those exchanges don't check authentication. I'm wondering how I should handle this. For example, I don't want someone to be able to logout, hit back button, then submit something with the AJAX functions successfully.
I can't access the bean from the servlet to check the login (totally wrong context and static vs non-static). I guess I could set a flag with the user entry in the database table denoting logged in or not. Then I can detect timeout logoffs and update the flag as well. But that way would require extra database accesses every time something is done. It would duplicate functionality in some way, but I guess I could perhaps use that just for the AJAX stuff. One difference with that would be the user would not be able to be logged in on multiple places at once as currently.
How is this kind of thing normally done?
Thanks for any help!
You could use session to store that flag instead of the database, and when the user logs out you should remove that flag and destroy the session. In login method
HttpSession session = req.getSession(true);
session.setAttribute("loggedIn",true)
And in your AJAX code
if(eq.getSession(true).getAttribute("loggedIn")==true)
doWork();
else
error("not logged in");
The webcontainer will handle timeouts for you, keep track of each user and his session, and so on.
But I would recommend that you use a standard for managing authntication
Bean values are not getting removed when I come back to same screen.
I create user details and insert the data
I view all the listed users available in database
I search for particular user
when I come back to Create user page, I see data of searched user
How do I remove old data from bean? Managed bean is in session scope.
You need a good reason why you'd want to put a list of users into a session scoped bean. It may cause some trouble and in the end reflect outdated data in a multiuser environment. Make the bean #ViewScoped instead, and load all users, or a subset of users, in a #PostConstruct method.
As to the coherence when using a session scoped bean, my guess is that you didn't update a list of current users. Basically you need to double the operation in this case: the first operation is a database operation, and the second one is a list update operation. Like in the following code:
public void deleteUser() {
yourEJB.remove(user);
listOfUsers.remove(user);
}
Also, to have your UI updated, be sure to make a postback, by returning void/null from an action method, or update the needed component via AJAX, by specifying its client id in render attribute of <f:ajax> tag.
I think you're thinking about #Remove annotation, which should be on specific method (or I understand your problem wrong).
Reference: http://docs.oracle.com/javaee/5/api/javax/ejb/Remove.html
A couple things here: first, post a simple example: it allows us to run it to help you. But here's what is (probably) happening. Your bean is session scoped which means it will live for the entire time the user has a session. Unless you specifically destroy the session change the value it is going to "exist" for that seesion.
Using the JSF2 annotations try this:
#ManagedBean(name="seesionScopedBean")
#SessionScoped
public class SessionScopedBean { private String data;//create getter/setter}
#ManagedBean(name="viewScopedBean")
#ViewScoped
public class ViewScopedBean { private String data;}
#ManagedBean(name="requestScopedBean")
#RequestScoped
public class RequestScopedBean { private String data;}
Assuming your three scopes make a page that is basically
<f:view>
<h:form>
<h:inputText value="#{sessionScopedBean.data}" />
<h:inputText value="#{viewScopedBean.data}" />
<h:inputText value="#{requestScopedBean.data}" />
<!-- whatever listener/action to set things -->
</h::form>
</f:view>
You'll notice that the "SessionScoped" data stays until you log out, the view will stay as long as you interact with this view (not totally correct, but that's the gist of it. And the request scope lives in each request.
Basically your create user bean feels like it should have a Request or View scope (as stated in a previous answer). Hopefully this helps. It takes a bit of time to wrap your head around the life-cycle but JSF is rewarding to work with once you get the hang of it.
I'm developing a single page jQuery & Backbone.js web app. The backend is a JBoss 6 application server.
Until now we had the following structure:
There is only one servlet (front controller). Every request from the JavaScript client goes through here.
In the servlet - at the first request of a certain JS client - I make a look p to a stateful session bean. For the next requests of this client, I store the result of the look up in an HTTP session container. So every JS client has exactly one stateful session bean. This connection is kept by a session cookie.
Now I have an additional requirement:
When the user has two browser tabs (in one browser), they should have two isolated instances of the web app in every browser tab. Because of that I have a problem with session cookies because this session cookie is for all browser tabs.
I have to change the structure so that:
The servlet has to generate a new session ID for the first request of a certain JS client. This session ID is communicated to the client.
With every POST to the backend the JS client has to send this session ID.
My question is:
Until now I saved the result of the look up in an HTTP Session object and I hadn't to think about generating a session ID. But now I have to store this somewhere else, where?
Has anybody experience with this kind of setting and can help me?
Update:
Thank you BalusC for this very interesting approach.
When I understood you well, this means:
All individual JS clients of the tabs of one browser share one HTTP session object. And in this HTTP session object, every tab has its own entry point. That sounds really good. So I still can use the whole HTTP session infrastructure and don't have to reinvent the wheel.
Autogenerate an unique value on the initial GET request which you store and pass around on every subsequent postback as a hidden input value. Use this unique value as identifier of the session attribute representing the view-scoped data.
During the 1st request on a brand new session, do:
Map<String, ViewData> viewScope = new HashMap<String, ViewData>();
session.setAttribute("viewScope", viewScope);
(the ViewData represents the view-specific data you'd like to track across postbacks on the same view)
During every GET request, do:
String viewDataId = UUID.randomUUID().toString();
viewScope.put(viewDataId, new ViewData());
request.setAttribute("viewDataId", viewDataId);
During generating the HTML, do:
<input type="hidden" name="viewDataId" value="${viewDataId}" />
During every POST request, do:
ViewData viewData = viewScope.get(request.getParameter("viewDataId"));
// Get/set view-specific data in there.
Make sure that jQuery also passes this hidden input around (which shouldn't be a big problem if you already properly use $(form).serialize() or e.g. AjaxForm plugin to ajaxify the forms).
If you're familiar with Java EE's MVC framework JSF, then it may be useful to know that its #ViewScoped annotation works roughly the same as described above. See also a.o. How to choose the right bean scope?
You can use session tracking with URL rewriting. See here:
Session shared in between tabs
I have a #ConversationScoped bean, with a start method, like so:
#PostConstruct
public void start() {
if (conversation.isTransient()) {
conversation.begin();
log.debug("conversation.getId(): " + conversation.getId());
}
}
My problem is that every time the page is refreshed a new conversation is started, a new conversation is also started every time I have an AJAX call to a method in the bean (which is my main problem).
What I really want to happen is for the sam conversation to hang around until I manually call conversation.end(). What am I missing here?
Slightly off-topic, but hopefully valuable:
I'm not 100% sure that #PostConstruct is the right place to start a conversation. I'd rather use a faces-event like this:
<f:metadata>
<f:event type="javax.faces.event.PreRenderViewEvent"
listener="#{myBean.init}" />
</f:metadata>
and start the conversation if you are sure that you are not in a JSF-postback request.
public void init() {
if (!FacesContext.getCurrentInstance().isPostback() && conversation.isTransient()) {
conversation.begin();
}
}
If you use Seam 3, it's even easier:
<f:metadata>
<s:viewAction action="#{myBean.init}" if="#{conversation.transient}" />
</f:metadata>
Did you checked that the (AJAX) calls include the conversation ID parameter (cid)?
If that's missing, a new conversation is expected to start for each call.
The concept of the JSR-299 builtin Conversation is a bit broken. At least for JSF apps. Using the #PreRenderViewEvent would basically drop all the benefits of this approach as it would make every #ConversationScoped bean longRunning.
You might try to use Apache MyFaces CODI #ConversationScoped instead. CODI is a CDI Extension library which is known to work well with Apache OpenWebBeans and also with Weld.
It additionally also provides #ViewScoped, #ViewAccessScoped (kind of an auto-conversation) and #WindowScoped contexts.
More at: https://cwiki.apache.org/confluence/display/EXTCDI/Index
It's all in the docs:
The conversation scope is active:
during all standard lifecycle phases of any JSF faces or non-faces request.
The conversation context provides access to state associated with a particular conversation. Every JSF request has an associated conversation. This association is managed automatically by the container according to the following rules:
Any JSF request has exactly one associated conversation.
The conversation associated with a JSF request is determined at the beginning of the restore view phase and does not change during the request.
Any conversation is in one of two states: transient or long-running.
By default, a conversation is transient
A transient conversation may be marked long-running by calling Conversation.begin()
A long-running conversation may be marked transient by calling Conversation.end()
All long-running conversations have a string-valued unique identifier, which may be set by the application when the conversation is marked long-running, or generated by the container.
If the conversation associated with the current JSF request is in the transient state at the end of a JSF request, it is destroyed, and the conversation context is also destroyed.
If the conversation associated with the current JSF request is in the long-running state at the end of a JSF request, it is not destroyed. Instead, it may be propagated to other requests according to the following rules:
The long-running conversation context associated with a request that renders a JSF view is automatically propagated to any faces request (JSF form submission) that originates from that rendered page.
The long-running conversation context associated with a request that results in a JSF redirect (a redirect resulting from a navigation rule or JSF NavigationHandler) is automatically propagated to the resulting non-faces request, and to any other subsequent request to the same URL. This is accomplished via use of a GET request parameter named cid containing the unique identifier of the conversation.
The long-running conversation associated with a request may be propagated to any non-faces request via use of a GET request parameter named cid containing the unique identifier of the conversation. In this case, the application must manage this request parameter.
When no conversation is propagated to a JSF request, the request is associated with a new transient conversation. All long-running conversations are scoped to a particular HTTP servlet session and may not cross session boundaries. In the following cases, a propagated long-running conversation cannot be restored and reassociated with the request:
When the HTTP servlet session is invalidated, all long-running conversation contexts created during the current session are destroyed, after the servlet service() method completes.
The container is permitted to arbitrarily destroy any long-running conversation that is associated with no current JSF request, in order to conserve resources.
Author:
Gavin King, Pete Muir
IMHO CDI conversations are broken by design and struberg pointed out a promising alternative. In my App I've the same problem and currently I'm refactoring it to CDI + CODI 1 and it just feels right. #ConversationScoped solves all those problems. While refactoring my App I could solve a lot of nasty cases with #ViewAccessScoped. Thank you struberg for pointing us to it!
Oddly enough, if you add an event listener to your Facelet, even if it calls an empty method, the form action of the generated source will have the 'cid' parameter and therefore, the AJAX call will not create a new conversation. Without the event listener, the 'cid' is missing the in the form action.
<f:metadata>
<f:event listener="#{myBean.dummy}" type="preRenderView" />
</f:metadata>
MyBean.java
public void dummy() {}
i want a mechanism in my web app as described below:
user will enter mydomain.com/CompanyName , depending upon the CompanyNameit will show its logo and its customized page, and i will take that parsed parameter in session again upon each request i will compare the parsed CompanyName and one stored in session , and if they matched then application will show the requested page with user's data.else it will be redirected to login page.
and the main thing is i want this thing in JSF arch.
i tried taking a servlet that will resolve all request and it will parse and then will dispatch the request to prefered servlet,but problem is it goes in loop as again it resolves to the same controller servlet,
You can do this via a phase listener. You can define a global one in faces-config.xml (or using annotations, if JSF 2.0 is used)
<lifecycle>
<phase-listener>com.yourcompany.CompanyPhaseListener</phase-listener>
</lifecycle>
There you have access to the FacesContext, from which you can obtain the current request URI. Parse it and store the appropriate attributes in the request, which you can later read on your pages.
The phase listener is executed on the specified phase(s), and perhaps you should choose RENDER_RESPONSE
For affecting the way your URLs appear in the browser, check PrettyFaces.
i tried taking a servlet that will resolve all request and it will parse and then will dispatch the request to prefered servlet,but problem is it goes in loop as again it resolves to the same controller servlet,
Use a Filter instead. It by default doesn't listen on forwarded requests, so you won't get an infinite loop on forwarding.