Истек срок действия конкретного экземпляра управляемого компонента через интервал времени

У меня есть 2 JSF управляемых боба A а также B и мне нужно истечь / уничтожить / уничтожить A через 2 минуты и B через 5 минут. Я проверил этот связанный вопрос Время от бобов, но у него заканчивается весь сеанс. Я не хочу, чтобы закончить весь сеанс.

Как я могу достичь этого с помощью настраиваемой области?

1 ответ

Решение

Учитывая, что вы используете средство управления bean-компонентом JSF (и, следовательно, не CDI, что потребовало бы совершенно другого ответа), вы можете достичь этого с помощью @CustomScoped, @CustomScoped значение должно относиться к Map реализация в более широкой, обычно существующей сфере.

Что-то вроде:

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

Как @CustomScoped аннотация не поддерживает передачу дополнительных аргументов, установка времени ожидания может быть выполнена только через дополнительную пользовательскую аннотацию, как показано ниже:

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

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

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

Теперь вот пример того, как #{timeoutScope} похоже, в том числе @PostConstruct поддержка (автоматически) и @PreDestroy поддержка (вручную):

@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;
    }

}

Вы видите, это сессия области и реализует Map, Что касается области действия, то таким образом она привязана к конкретному сеансу пользователя, а не ко всему приложению. Если вы на самом деле хотите совместно использовать bean-компонент во всех пользовательских сеансах в приложении, сделайте вместо этого область приложения. Что касается Map JSF всегда должен найти управляемый компонент, сначала он пытается get(), Если он вернется null (т.е. бин еще не существует), тогда он автоматически создаст экземпляр управляемого бина и выполнит put(),

Внутри put(), это вопрос извлечения и расчета времени ожидания и сохранения его на карте. Внутри get() Вы просто проверяете таймаут и возвращаетесь null чтобы указать JSF, что бин больше не существует. JSF просто создаст его автоматически и вернется на put(), так далее.

Обратите внимание, что я использую System#nanoTime() вместо System#currentTimeMillis() так как последний привязан ко времени ОС (операционной системы), а не к аппаратному времени (и, таким образом, он чувствителен к летнему времени и контролируемым пользователем изменениям во времени).

Другие вопросы по тегам