Expire specific managed bean instance after time interval

耗尽温柔 提交于 2019-12-03 08:03:12

Given that you're using JSF bean management facility (and thus not CDI, which would require a completely different answer), you can achieve this with @CustomScoped. The @CustomScoped value must refer a Map implementation in a broader, usually existing, scope.

Something like:

@ManagedBean
@CustomScoped("#{timeoutScope}")
public class TimeoutBean {}

As the @CustomScoped annotation doesn't support passing additional arguments, setting the timeout can only be done via an additional custom annotation like below:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Timeout {

    /** Minutes. */
    int value();

}
@ManagedBean
@CustomScoped("#{timeoutScope}")
@Timeout(5) // Expires after 5 minutes.
public class TimeoutBean {}

Now, here's a kickoff example of how the #{timeoutScope} looks like, including @PostConstruct support (automatically) and @PreDestroy support (manually):

@ManagedBean
@SessionScoped
public class TimeoutScope extends HashMap<String, Object> {

    private static final long serialVersionUID = 1L;

    @Override
    public Object put(String name, Object bean) {
        Timeout timeout = bean.getClass().getAnnotation(Timeout.class);

        if (timeout == null) {
            throw new IllegalArgumentException("@Timeout annotation is required on bean " + name);
        }

        Long endtime = System.nanoTime() + (timeout.value() * (long) 6e10);
        Object[] beanAndEndtime = new Object[] { bean, endtime };
        return super.put(name, beanAndEndtime);
    }

    @Override
    public Object get(Object key) {
        Object[] beanAndEndtime = (Object[]) super.get(key);

        if (beanAndEndtime == null) {
            return null;
        }

        Object bean = beanAndEndtime[0];
        Long endtime = (Long) beanAndEndtime[1];

        if (System.nanoTime() > endtime) {
            String name = (String) key;
            ScopeContext scope = new ScopeContext("timeoutScope", Collections.singletonMap(name, bean));
            FacesContext context = FacesContext.getCurrentInstance();
            context.getApplication().publishEvent(context, PreDestroyCustomScopeEvent.class, scope);
            return null;
        }

        return bean;
    }

}

You see, it's session scoped and implements Map. As to the scope, this way it's tied to a specific user session, not to the whole application. If you actually want to share the bean across all user sessions in the application, then make it application scoped instead. As to the Map, henever JSF needs to find a managed bean, it first tries get(). If it returns null (i.e. bean doesn't exist yet), then it will auto-create the managed bean instance and perform a put().

Inside the put(), it's a matter of extracting and calculating the timeout and store it in the map. Inside the get(), you just check the timeout and return null to indicate JSF that bean doesn't exist anymore. JSF will then simply auto-create it and come back at put(), etc.

Do note that I'm using System#nanoTime() instead of System#currentTimeMillis() as the latter is tied to OS (operating system) time, not to hardware time (and it's thus sensitive to a.o. DST and enduser-controlled changes in time).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!