2010-09-23 11 views
8

Nie jestem pewien, czy to, co robię, jest błędne, czy też właśnie pominięto adnotację lub element konfiguracji. Oto sytuacja:JSF - Komponent bean o ograniczonym zakresie sesji nie ma ponownie wziętych zależności w przypadku deserializacji sesji

Mam aplikację JSF z ziarnem o zakresie sesji o nazwie SessionData. Ten komponent bean ma odwołanie do komponentu bean o zasięgu aplikacji (typu ApplicationData), które zostało wstrzyknięte do niego podczas tworzenia. Działa to dobrze, gdy sesja jest tworzona po raz pierwszy. Zastrzyk zależność odbywa się <managed-bean> elementów w pliku faces-config.xml jak pokazano tutaj:

<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> 

Bo to nie ma sensu mieć mój SessionData obiekt to obiekt ApplicationData kiedy to w odcinkach, mam zaznaczone odwołanie ApplicationData jako przejściowy w moim SessionData obiektu:

transient private ApplicationData applicationData; 

Wszystko jest dobrze dopóki aplikacja internetowa jest zatrzymany (w moim 6.x kontenera Tomcat) i sesje są w odcinkach. Gdy ponownie uruchomię aplikację i sesje zostaną deserialized, moje odniesienie do ApplicationData nie zostanie ponownie wstrzyknięte przez JSF. Wiem, że deserializacja ma pozostawiać pola przejściowe bez wartości. Czy istnieje sposób sygnalizowania JSF, że obiekt o zakresie sesji wymaga, aby jego zależności zostały ustawione ponownie po deserializacji?

Używam MyFaces JSF 1.2 i Tomcat 6.0.26 jako mojego kontenera aplikacji WWW.

+1

Zasugerowano, że dostarczam metodę readObject() i ręcznie ustawiam obiekt ApplicationData podczas deserializacji za pomocą FaceContext. Nie sądzę, że to zadziała, ponieważ FaceContext jest dostępny tylko w czasie trwania żądania. Deserializacja ma miejsce podczas uruchamiania aplikacji. –

+2

poprawne, dlatego usunąłem odpowiedź. Wydaje się być bardziej skomplikowany (stąd +1 dla pytania). – Bozho

Odpowiedz

5

Mimo że rozwiązanie oferowane przez Bozho mogło działać, nie chcę wprowadzać obiektów proxy do aplikacji, która aktualnie ich nie używa. Moje rozwiązanie jest mniej niż idealne, ale wykonuje swoją pracę.

opuściłem pole przejściowy w lokalu:

transient private ApplicationData _applicationData; 

ja też lewy rozgrywający się w miejscu tak JSF może początkowo ustawić odniesienie kiedy po raz pierwszy przedmiotem SessionData tworzony jest:

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

Zmiana, którą wprowadziłem, była metodą gettera. Metody w obiekcie SessionData muszą teraz przerwać bezpośredni dostęp do pola _applicationData, a zamiast tego uzyskać odwołanie za pomocą pobierającego. Getter najpierw sprawdzi zerową wartość odniesienia. Jeśli ma wartość NULL, zarządzana fasola jest uzyskiwana za pośrednictwem FacesContext. Ograniczenie polega na tym, że FacesContext jest dostępny tylko w czasie trwania żądania.

/** 
* 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; 
} 

Jeśli ktoś dba, Oto kod dla mojego sposobu 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()+ 
       "]"); 
     } 
    } 
} 

I nie czepiać nazywa mój zatwierdź. Wyniosę je po zakończeniu pracy nad rozwojem! :)

+4

Istnieje skrót w ['Application # evaluateExpressionGet()'] (http://download.oracle.com/javaee/6/api/javax/faces/application/ Application.html # evaluateExpressionGet% 28javax.faces.context.FacesContext,% 20java.lang.String,% 20java.lang.Class% 29). Zobacz także [tę odpowiedź] (http://stackoverflow.com/questions/2633112/jsf-get-managed-baby-by-name/2633733#2633733). – BalusC

1

można dodać metodę:

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

I initializeApplicationData można użyć dynamicznego obiektu proxy. Za pomocą CGLIB lub javassist utwórz proxy, przed którym każde wywołanie metody ustawia wewnętrzne pole - prawdziwe ApplicationData. Jeśli jest null, a następnie uzyskać prąd FacesContext (który będzie dostępny w tym momencie) i uzyskać zarządzanego fasoli stamtąd przez:

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

i przekazać jej metod.

To brzydkie rozwiązanie, ale myślę, że zadziała.

+0

To będzie mój pierwszy raz, kiedy korzystam z javassisty, więc potrzebuję trochę czasu na przetrawienie tej części twojego rozwiązania. Dziękuję Ci. –

+0

to całkiem proste. Wystarczy postępować zgodnie z samouczkiem - Twój przypadek proxy jest najprostszy :) – Bozho

Powiązane problemy