2009-05-05 26 views
39

Tengo 2 proyectos diferentes de Java, uno tiene 2 clases: dynamicbeans.DynamicBean2 y dynamic.Validator.ClassCastException al convertir a la misma clase

Por otro proyecto, me carga tanto de estas clases de forma dinámica y almacenarlos en una Object

class Form { 
    Class beanClass; 
    Class validatorClass; 
    Validator validator; 
} 

entonces seguir adelante y crear un objeto Validator usando validatorClass.newInstance() y lo almacena en validator entonces crear un grano objeto también usando beanClass.newInstance() y agréguelo a la sesión.

portletRequest.setAttribute("DynamicBean2", bean); 

Durante el ciclo de vida del proyecto Form, me llaman validator.validate() que carga el objeto bean previamente creado a partir de la sesión (Estoy corriendo Websphere Portal Server). Cuando intento devolver este objeto a DynamicBean2, falla con ClassCastException.

Cuando me tire el objeto de nuevo fuera de la sesión utilizando

faces.getApplication().createValueBinding("#{DynamicBean2}").getValue(faces); 

y comprobar la clase de ella usando .getClass() consigo dynamicbeans.DynamicBean2. Esta es la clase en la que quiero lanzarla; sin embargo, cuando intento, obtengo ClassCastException.

¿Alguna razón por la que estoy recibiendo esto?

Respuesta

47

No estoy siguiendo exactamente su descripción del flujo de programa, pero generalmente cuando obtiene ClassCastExceptions no puede explicar que ha cargado la clase con un cargador de clases y luego intenta convertirlo a la misma clase cargada por otro cargador de clases. Esto no funcionará; están representados por dos objetos de Clase diferentes dentro de la JVM y el lanzamiento fallará.

Hay un article about classloading in WebSphere. No puedo decir cómo se aplica a su aplicación, pero hay varias soluciones posibles. Puedo pensar al menos en:

  1. Cambie el cargador de clases de contexto manualmente. Requiere que realmente pueda obtener una referencia a un cargador de clases apropiado, lo que puede no ser posible en su caso.

    Thread.currentThread().setContextClassloader(...); 
    
  2. Asegúrese de que la clase está cargado por un cargador de clase superior en la jerarquía.

  3. Serializar y deserializar el objeto. (¡Yuck!)

Sin embargo, probablemente exista una forma más adecuada para su situación particular.

+3

Mi suposición (no tengo experiencia con el servidor de portal) es que está cargando la clase en diferentes portlets, cada uno con su propio cargador de clases. A continuación, coloca la instancia en un objeto de sesión y cuando la recupera del portlet incorrecto (desde el que se creó) tiene su excepción. Supongo (basado en la experiencia de WAS) que necesita colocar el contenedor con su clase en algún lugar superior de la jerarquía de carga de clases para que lo cargue un padre de ambos portlets clasificadores. – Robin

+2

es decir, el número 2, con detalles. – Robin

+1

@waxwing Esto ha pasado bastante tiempo. Pero, ¿te importaría elaborar la primera solución posible que hayas declarado? Siento que un problema que estoy teniendo se puede resolver con esto. ¡Gracias por adelantado! –

10

Los objetos de clase se cargaron en diferentes cargadores de clases, por lo tanto, las instancias creadas desde en cada una de las clases se consideran 'incompatibles'. Este es un problema común en un entorno donde se usan muchos cargadores de clases diferentes y se están pasando objetos. Estos problemas pueden surgir fácilmente en Java EE y entornos de portal.

La emisión de una instancia de una clase requiere que la clase vinculada al objeto que se va a transmitir sea la misma que la cargada por el cargador de clases de contexto de hilo actual.

+0

¿Hay una manera correcta de cargar las clases para evitar que esto suceda? –

+0

La parte misteriosa es por qué dos cargadores de clase deberían cargar la misma clase. En cualquier aplicación Java, cualquier clase siempre debe cargarse con un solo cargador de clases. Cada vez que obtienes este comportamiento, algo realmente extraño sucede con los cargadores de tu clase. –

+2

Eso depende del entorno en el que está operando. Si utiliza un singleton para almacenar información en caché y coloca datos en este caché desde dos diferentes aplicaciones web que están en el mismo EAR, cada uno puede tener su propio cargador de clases creando instancias y cuándo se recupera un elemento de este caché de la otra aplicación web, se generará una ClassCastException. Compartir en datos de memoria puede ser muy complicado en estos entornos, por lo que debe tener en cuenta dónde se cargan las clases para que funcione. – Robin

0

Obtuve el problema A2AClassCastException al intentar crear una lista de objetos desde XML utilizando Apache Commons Digester.

List<MyTemplate> templates = new ArrayList<MyTemplate>(); 
Digester digester = new Digester(); 
digester.addObjectCreate("/path/to/template", MyTemplate.class); 
digester.addSetNext("/path/to/template", "add"); 
// Set more rules... 
digester.parse(f); // f is a pre-defined File 

for(MyTemplate t : templates) { // ClassCastException: Cannot cast mypackage.MyTemplate to mypackage.MyTemplate 
    // Do stuff 
} 

Como se indicó anteriormente, la causa es que el digestor no utiliza el mismo ClassLoader como el resto del programa. Ejecuté esto en JBoss, y resultó que commons-digester.jar no estaba en el directorio lib de JBoss, sino en el directorio lib de una aplicación web. Copiar el jar en mywebapp/WEB-INF/lib también resolvió el problema. Otra solución fue casll digester.setClassLoader (MyTemplate.class.getClassLoader()), pero se siente como una solución bastante fea en este contexto.

0

Tuve el mismo problema al usar varias instancias de JBoss en diferentes máquinas. Para mal, no me encontré con esta publicación antes.
Había artefactos desplegados en diferentes máquinas, dos de ellos cargadores de clases declarados con nombre idéntico. Cambié uno de los nombres del cargador de clases y todo funcionó bien => ¡Cuidado con la copia & Pegar!

¿Por qué la ClassCastException no menciona los cargadores de clase involucrados? - Creo que sería una información muy útil.
¿Alguien sabe si habrá algo como esto disponible en el futuro? Necesitar comprobar los cargadores de la clase de 20-30 artefactos no es tan agradable. ¿O hay algo que extrañé en el texto de excepción?

EDIT: edité el archivo META-INF/jboss-app.xml y cambié el nombre del cargador, la idea es tener un nombre único. En el trabajo, utilizamos el identificador de artefactos (único) combinado con la versión insertada por maven ({$ version}) durante la compilación.
Usar campos dinámicos es solo opcional pero ayuda si desea implementar diferentes versiones de la misma aplicación.

<jboss-app> 
    <loader-repository> 
    com.example:archive=unique-archive-name-{$version} 
    </loader-repository> 
</jboss-app> 

puede encontrar alguna información aquí: https://community.jboss.org/wiki/ClassLoadingConfiguration

+0

¿Puedes mostrarnos cómo lo has hecho, algún código? – Rachel

+0

Actualicé mi respuesta, siento haber llegado tarde. – Andrei

0

que tenían el mismo problema, y ​​finalmente encontró una solución en java.net:

Copiar todos los archivos de org.eclipse.persistence jarglassfish4/glassfish/modules a WEB-INF/lib. Luego ingrese en su glassfish-web.xml y configure class-delegate en false.

¡Funcionó para mí!

0

Tuve el mismo problema en un EJB de wildfly, el EJB devolvió una lista de objetos y tiene un control remoto y una interfaz local. Utilicé la interfaz local por error, lo que funcionaba bien hasta el momento en que intentas lanzar los objetos en la lista.

interfaz remota/local:

public interface DocumentStoreService { 

    @javax.ejb.Remote 
    interface Remote extends DocumentStoreService { 
    } 

    @javax.ejb.Local 
    interface Local extends DocumentStoreService { 
    } 

El bean EJB:

@Stateless 
public class DocumentStoreServiceImpl implements DocumentStoreService.Local, DocumentStoreService.Remote { 

La correcta envoltura de la primavera en todo el EJB:

<bean id="documentStoreService" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean"> 
    <property name="jndiName" value="java:global/dpc/dpc-ejb/DocumentStoreServiceImpl!santam.apps.dpc.service.DocumentStoreService$Remote"/> 
    <property name="businessInterface" value="santam.apps.dpc.service.DocumentStoreService$Remote"/> 
    <property name="resourceRef" value="true" /> 
</bean> 

Nota del $ a distancia, puede cambiar esto a $ Local y encontrará la interfaz Local muy bien, y también ejecutará los métodos sin ningún problema (desde aplicación separada en el mismo contenedor), pero los objetos del modelo no se calculan y provienen de un cargador de clases diferente si utiliza la interfaz local por error.

0

Otra opción:

me pasó en WebLogic, pero supongo que puede suceder en otros servidores, así - si (solo) "Publicar" y por lo tanto algunas de las clases está recargado. En cambio, haz "Limpiar" para que todas las clases se vuelvan a cargar juntas.

1

Tuve el mismo problema con una búsqueda EJB de otro EJB. Resolví agregar @Remote (MyInterface.class) a la configuración de clase EJB

Cuestiones relacionadas