2010-09-23 12 views
8

No estoy seguro de si lo que estoy haciendo es incorrecto, o si me perdí una anotación o elemento de configuración en alguna parte. Aquí está la situación:JSF: el bean administrado con el alcance de la sesión no tiene reinyecciones de dependencias en la deserialización de la sesión

Tengo una aplicación JSF con un bean con ámbito de sesión llamado SessionData. Este bean tiene una referencia de bean con ámbito de aplicación (del tipo ApplicationData) inyectado en el momento de la creación. Esto funciona bien cuando la sesión se crea por primera vez. La inyección de dependencias se realiza con <managed-bean> elementos en el archivo faces-config.xml como se muestra aquí:

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

Debido a que no tiene sentido tener mi objetivo SessionData incluyen el objeto ApplicationData cuando es serializado, He marcado la referencia ApplicationData como transitorio en mi objeto SessionData:

transient private ApplicationData applicationData; 

Todo es bueno hasta que se detuvo la aplicación web (en mi contenedor Tomcat 6.x) y las sesiones se efectúan en serie. Cuando reinicio la aplicación y las sesiones se deserializan, mi referencia a ApplicationData no es reinyectada por JSF. Sé que se supone que la deserialización deja campos transitorios sin valor. ¿Hay alguna manera de indicarle a JSF que este objeto con ámbito de sesión requiere que sus dependencias se vuelvan a establecer después de la deserialización?

Estoy usando MyFaces JSF 1.2 y Tomcat 6.0.26 como mi contenedor de aplicaciones web.

+1

Se sugirió que suministre un método readObject() y configure manualmente el objeto ApplicationData durante la deserialización utilizando FacesContext. No creo que eso funcione, ya que FacesContext solo está disponible durante la vigencia de una solicitud. La deserialización ocurre al inicio de la aplicación. –

+2

correcto, es por eso que eliminé mi respuesta. Parece más complicado (de ahí que +1 para la pregunta) – Bozho

Respuesta

5

Aunque la solución ofrecida por Bozho podría funcionar, no quiero introducir objetos proxy en una aplicación que no los está utilizando actualmente. Mi solución es menos que ideal, pero hace el trabajo bien.

me dejó el campo transitoria en su lugar:

transient private ApplicationData _applicationData; 

También me dejó la incubadora en su lugar para JSF puede establecer inicialmente la referencia cuando se crea el objeto SessionData primera vez:

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

El cambio que hice estaba en el método getter. Los métodos en el objeto SessionData ahora necesitan detener el acceso directo al campo _applicationData y obtener la referencia a través del getter. El captador comprobará primero para una referencia nula. Si es nulo, el bean administrado se obtiene a través del FacesContext. La restricción aquí es que el FacesContext solo está disponible durante la vigencia de una solicitud.

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

Si a alguien le interesa, aquí es el código de mi método 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()+ 
       "]"); 
     } 
    } 
} 

Y no se meten con mi Validar llama. ¡Los sacaré después de que haya terminado con el desarrollo! :)

+4

Hay un atajo en ['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). Ver también [esta respuesta] (http://stackoverflow.com/questions/2633112/jsf-get-managed-bean-by-name/2633733#2633733). – BalusC

1

puede agregar un método:

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

Y en initializeApplicationData se puede utilizar un objeto proxy dinámico. El uso de CGLIB o javassist crea un proxy que, antes de cada invocación de método, establece un campo interno: el ApplicationData real. Si se trata de null, a continuación, obtener la corriente FacesContext (que será accesible en ese momento) y obtener el bean gestionado desde allí por:

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

y delegar en sus métodos.

Esta es una solución fea, pero creo que funcionará.

+0

Esta será la primera vez que uso javassist, así que voy a necesitar algo de tiempo para digerir esa parte de tu solución. Gracias. –

+0

es bastante fácil. Simplemente siga el tutorial: su caso de proxy es el más simple :) – Bozho

Cuestiones relacionadas