Setting CDI conversation timeout globally
Asked Answered
C

4

6

Is it possible to set conversation timeout globally for all conversation objects injected into @Named beans?

I have several @ConversationScoped beans, e.g.:

import javax.annotation.PostConstruct;
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Inject;
import javax.inject.Named;

@Named
@ConversationScoped
public class SomeBean1 {

    @Inject
    private Conversation conversation;

    @PostConstruct
    private void init() {
        if (conversation.isTransient()) {
            conversation.begin();
        }
    }
}

@Named
@ConversationScoped
public class SomeBean2 {

    @Inject
    private Conversation conversation;

    @PostConstruct
    private void init() {
        if (conversation.isTransient()) {
            conversation.begin();
        }
    }
}        

The default timeout for these conversations is 600000 ms. I want to know if there is any way to set conversations' timeouts globally or I need to set it in each bean by

if (!conversation.isTrainsient()) {
    conversation.setTimeout(MY_CUSTOM_TIMEOUT);
}

(the problem is that there is a lot of CDI beans and setting timeout manually in each of them is not the best solution)

Cetinje answered 1/10, 2013 at 6:36 Comment(1)
As some of the answers below have demonstrated, there isn't a standard way of doing it. Anything you do (short of stasal's answer) is not going to be portable.Opulence
C
2

So, here is the solution I used (Oracle WebLogic 12c, WELD 1.1.Final):

import org.jboss.weld.context.http.HttpConversationContext;

import javax.inject.Inject;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@WebListener
public class SessionListener implements HttpSessionListener {

    @Inject
    private HttpConversationContext conversationContext;

    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        if (conversationContext != null) {
            final long DEFAULT_TIMEOUT = 2 * 60 * 60 * 1000;
            if (conversationContext.getDefaultTimeout() < DEFAULT_TIMEOUT){
                conversationContext.setDefaultTimeout(DEFAULT_TIMEOUT);
            }
        }
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {}
}

Context is injected into the listener and the timeout is set when user starts the session.

Cetinje answered 1/10, 2013 at 17:59 Comment(2)
When is sessionCreated called? Should this class be configured in web.xml?Cromwell
The sessionCreated method is called when new client session is created. This class should be either configured in web.xml descriptor or annotated as in the code example above (but "metadata-complete" attribute in web.xml should be omitted or equal to "false"). More about hadling servlet lifecycle events is written here: docs.oracle.com/javaee/7/tutorial/servlets002.htm#BNAFJCetinje
B
2

With Seam/Weld you should be able to do something like the following:

@Inject
private HttpConversationContext conversationContext;

public void observePreload(@Observes PreloadCompleteEvent event) {
    //Set global conversation timout to 60000 ms
    conversationContext.setDefaultTimeout(60000);
}

Otherwise I believe you will have to set it for each conversation.

EDIT: Note I used a custom event, the same can be accomplished with:

public void observePreload(@Observes @Started WebApplication webapp) {
Barina answered 1/10, 2013 at 15:5 Comment(3)
Thanks for your hint, implemented this in same way, will post the solution now:)Cetinje
What is PreloadCompleteEvent? Couldn't find anything about it in the (Weld 2.x) documentation. Thus, I use a similar observer method: public void observeAppInit(@Observes @Initialized(ApplicationScoped.class) Object event) and do the work inside this method, which works as suggested.Pulpit
PreloadCompleteEvent is a custom event, to use a default you could also do public void observePreload(@Observes @Started WebApplication webapp) { or what you have suggestedBarina
C
2

So, here is the solution I used (Oracle WebLogic 12c, WELD 1.1.Final):

import org.jboss.weld.context.http.HttpConversationContext;

import javax.inject.Inject;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@WebListener
public class SessionListener implements HttpSessionListener {

    @Inject
    private HttpConversationContext conversationContext;

    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        if (conversationContext != null) {
            final long DEFAULT_TIMEOUT = 2 * 60 * 60 * 1000;
            if (conversationContext.getDefaultTimeout() < DEFAULT_TIMEOUT){
                conversationContext.setDefaultTimeout(DEFAULT_TIMEOUT);
            }
        }
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {}
}

Context is injected into the listener and the timeout is set when user starts the session.

Cetinje answered 1/10, 2013 at 17:59 Comment(2)
When is sessionCreated called? Should this class be configured in web.xml?Cromwell
The sessionCreated method is called when new client session is created. This class should be either configured in web.xml descriptor or annotated as in the code example above (but "metadata-complete" attribute in web.xml should be omitted or equal to "false"). More about hadling servlet lifecycle events is written here: docs.oracle.com/javaee/7/tutorial/servlets002.htm#BNAFJCetinje
S
1

This can easily be done in a portable manner with CDI 1.1.

import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.enterprise.context.Initialized;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.servlet.ServletRequest;

public class ConversationTimeoutDefaultSetter {

    @Inject
    private Conversation conversation;

    public void conversationInitialized(
            @Observes @Initialized(ConversationScoped.class)
            ServletRequest payload) {
        conversation.setTimeout(1800000L); // 30 minutes
    }

}

Another portable option would be to decorate conversation. (Warning: untested.)

import static javax.interceptor.Interceptor.Priority.APPLICATION;

import javax.annotation.Priority;
import javax.decorator.Decorator;
import javax.decorator.Delegate;
import javax.enterprise.context.Conversation;
import javax.inject.Inject;

@Decorator
@Priority(APPLICATION)
public class ConversationTimeoutDefaultSetter implements Conversation, Serializable {

    private static final long serialVersionUID = 1L;

    @Inject
    @Delegate
    private Conversation delegate;

    private void setDefaultTimeout() {
        delegate.setTimeout(1800000L); // 30 minutes
    }

    @Override
    public void begin() {
        setDefaultTimeout();
        delegate.begin();
    }

    @Override
    public void begin(String id) {
        setDefaultTimeout();
        delegate.begin(id);
    }

    @Override
    public void end() {
        delegate.end();
    }

    @Override
    public String getId() {
        return delegate.getId();
    }

    @Override
    public long getTimeout() {
        return delegate.getTimeout();
    }

    @Override
    public void setTimeout(long milliseconds) {
        delegate.setTimeout(milliseconds);
    }

    @Override
    public boolean isTransient() {
        return delegate.isTransient();
    }

}
Satang answered 8/8, 2014 at 17:27 Comment(2)
You should've listed the imports for better understanding I think. javax.enterprise.context.Initialized is a part of java-ee-api 7, but we, for example, use java-ee 6, so the only way is to depend on jboss cdi-api which is more portable than my solution but still not 100% I guessCetinje
@stasal, yes, CDI 1.1 is actually required for this. I amended my answer.Satang
C
0

You can also synchronize the timeout of your conversation bean with the session timeout of your current http request:

if (conversation.isTransient()) {
        conversation.setTimeout(((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext()
                .getRequest()).getSession().getMaxInactiveInterval()*1000);
        conversation.begin();
    }
Cylinder answered 22/9, 2016 at 9:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.