JSF - управляемый EJB-компонент Session-scoped не имеет зависимостей, повторно вводимых при десериализации сеанса - PullRequest
8 голосов
/ 23 сентября 2010

Я не уверен, что то, что я делаю, неправильно, или я просто пропустил где-то аннотацию или элемент конфигурации.Вот ситуация:

У меня есть приложение JSF с сессионным компонентом с именем SessionData.Этот bean-компонент имеет ссылку на bean-объект области применения (типа ApplicationData), внедренную в него во время создания.Это работает нормально, когда сессия создается впервые.Внедрение зависимостей выполняется с помощью элементов <managed-bean> в файле faces-config.xml, как показано здесь:

<managed-bean>
    <managed-bean-name>sessionData</managed-bean-name>
    <managed-bean-class>my.package.SessionData</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
        <property-name>applicationData</property-name>
        <property-class>my.package.ApplicationData</property-class>
        <value>#{applicationData}</value>
    </managed-property>
</managed-bean>
<managed-bean>
    <managed-bean-name>applicationData</managed-bean-name>
    <managed-bean-class>my.package.ApplicationData</managed-bean-class>
    <managed-bean-scope>application</managed-bean-scope>
</managed-bean>

Поскольку нет смысла включать мой объект SessionData в объект ApplicationData, когдаэто сериализовано, я пометил ссылку ApplicationData как переходную в моем SessionData объекте:

transient private ApplicationData applicationData;

Все хорошо, пока веб-приложение не остановлено (в моем контейнере Tomcat 6.x) исеансы сериализуются.Когда я перезапускаю приложение и сеансы десериализуются, моя ссылка на ApplicationData не вводится повторно JSF.Я знаю, что десериализация должна оставить переходные поля без значения. Есть ли способ сообщить JSF, что этот объект в области сеанса требует повторной установки его зависимостей после десериализации?

Я использую MyFaces JSF 1.2 и Tomcat 6.0.26 в качестве контейнера веб-приложения.

Ответы [ 2 ]

5 голосов
/ 23 сентября 2010

Хотя решение, предлагаемое Bozho, может работать, я не хочу вводить прокси-объекты в приложение, которое в настоящее время не использует их.Мое решение не идеально, но оно выполняет свою работу.

Я оставил переходное поле на месте:

transient private ApplicationData _applicationData;

Я также оставил установщик на месте, чтобы JSF мог изначально установитьссылка при первом создании объекта SessionData:

public void setApplicationData(ApplicationData applicationData) {
    _applicationData = applicationData;
}

Изменение, которое я сделал, было в методе getter.Методы в объекте SessionData теперь должны прекратить прямой доступ к полю _applicationData и получить ссылку через геттер.Получатель сначала проверит нулевую ссылку.Если он нулевой, то управляемый компонент получается через FacesContext.Ограничение здесь заключается в том, что FacesContext доступен только в течение срока действия запроса.

/**
 * Get a reference to the ApplicationData object
 * @return ApplicationData
 * @throws IllegalStateException May be thrown if this method is called
 *  outside of a request and the ApplicationData object needs to be
 *  obtained via the FacesContext
 */
private ApplicationData getApplicationData() {
    if (_applicationData == null) {
        _applicationData = JSFUtilities.getManagedBean(
            "applicationData",  // name of managed bean
            ApplicationData.class);
        if (_applicationData == null) {
            throw new IllegalStateException(
                "Cannot get reference to ApplicationData object");
        }
    }
    return _applicationData;
}

Если кому-то интересно, вот код для моего getManagedBean() метода:

/**
 * <p>Retrieve a JSF managed bean instance by name.  If the bean has
 * never been accessed before then it will likely be instantiated by
 * the JSF framework during the execution of this method.</p>
 * 
 * @param managedBeanKey String containing the name of the managed bean
 * @param clazz Class object that corresponds to the managed bean type
 * @return T
 * @throws IllegalArgumentException Thrown when the supplied key does
 *  not resolve to any managed bean or when a managed bean is found but
 *  the object is not of type T
 */
public static <T> T getManagedBean(String managedBeanKey, Class<T> clazz)
        throws IllegalArgumentException {
    Validate.notNull(managedBeanKey);
    Validate.isTrue(!managedBeanKey.isEmpty());
    Validate.notNull(clazz);
    FacesContext facesContext = FacesContext.getCurrentInstance();
    if (facesContext == null) {
        return null;
    }
    Validate.notNull(facesContext.getApplication());
    ELResolver resolver = facesContext.getApplication().getELResolver();
    Validate.notNull(resolver);
    ELContext elContext = facesContext.getELContext();
    Validate.notNull(elContext);
    Object managedBean = resolver.getValue(
        elContext, null, managedBeanKey);
    if (!elContext.isPropertyResolved()) {
        throw new IllegalArgumentException(
            "No managed bean found for key: " + managedBeanKey);
    }
    if (managedBean == null) {
        return null;
    } else {
        if (clazz.isInstance(managedBean)) {
            return clazz.cast(managedBean);
        } else {
            throw new IllegalArgumentException(
                "Managed bean is not of type [" + clazz.getName() +
                "] | Actual type is: [" + managedBean.getClass().getName()+
                "]");
        }
    }
}

И не выбирайте мои проверочные звонки.Я заберу их после того, как закончу с разработкой!:)

1 голос
/ 23 сентября 2010

Вы можете добавить метод:

private void readObject(java.io.ObjectInputStream in)
 throws IOException, ClassNotFoundException {
  in.defaultReadObject();
  applicationData = initializeApplicationData();
}

А в initializeApplicationData вы можете использовать динамический прокси-объект. Используя CGLIB или javassist, создайте прокси, который перед каждым вызовом метода устанавливает внутреннее поле - действительное ApplicationData. Если это null, то получите текущий FacesContext (который будет доступен в этой точке) и получите от него управляемый компонент:

FacesContext facesContext = FacesContext.getCurrentInstance();
originalApplicationData = (ApplicationData)facesContext.getApplication()
  .createValueBinding("#{applicationData}").getValue(facesContext);

и делегировать его методам.

Это уродливый обходной путь, но я думаю, что он сработает.

...