开发者

@PostConstruct called multiple time for @ConversationScoped bean

I have a @C开发者_开发百科onversationScoped 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() {}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜