Buscar este blog

sábado, 30 de agosto de 2014

Spring MVC Validation - javax.validation

Algunas consideracciones al respecto:

Nota:
        Hay que tener puesto  <mvc:annotation-driven /> para que funcione el @Valid.

Item 1 - javax.validation

Añadiendo anotaciones de javax.validation a un objeto y recibiéndolo como un @ModelAttribute en el controller, al poner @Valid se lanza automáticamente la validación y se rellena el objeto BindingResult.

 @RequestMapping(value="/nuevo", params="accion=crear", method=RequestMethod.POST)  
      public ModelAndView formNuevoCrear(@Valid @ModelAttribute("usuarioForm") UsuarioForm usuarioForm, BindingResult result, RedirectAttributes redirectAttributes) {  
           //Anotando el model attribute con @Valid se fuerza a que se lanze su validación automática antes de entrar en el método.  
           //En caso de detectarse errores se añaden al objeto errors.  
           logger.debug("Acción de crear usuario");  
           if (result.hasErrors()) {  
                ModelAndView mav = new ModelAndView();  
                mav.addObject("tiposUsuario", tipoUsuarioService.getAll());  
                mav.addObject("roles", roleService.getAll());  
                mav.addObject("usuarioForm", usuarioForm);  
                mav.setViewName("app/usuarios/nuevoUsuario");  
                return mav;  
           }  
           Usuario usuario = usuarioForm.getUsuario();  
           usuario = usuarioService.save(usuario);  
           //Mostrar un aviso en el listado indicando que el usuario se creó correctamente  
           redirectAttributes.addFlashAttribute("usuarioCreado", true);   
           ModelAndView mav = new ModelAndView();            
           mav.setViewName("redirect:/usuarios");  
           return mav;  
      }  

En este caso se recibe un Objeto de tipo UsuarioForm que se validará automáticamente.

Item 2 - Validaciones anidadas

Si lo que se recibe de la request es un objeto Form (Bean o POJO adaptador para no usar directamente el objeto de negocio) que, a su vez, contiene un objeto de negocio con anotaciones de validación, hay que anotar con @Valid esa propiedad. De este modo se lanzan las validaciones anidadas.

 public class UsuarioForm {  
      @Valid  
      private Usuario usuario = new Usuario();  
      public Usuario getUsuario() {  
           return usuario;  
      }  
      public void setUsuario(Usuario usuario) {  
           this.usuario = usuario;  
      }       
 }  

 @Entity  
 @Table(name="USUARIO")  
 public class Usuario {  
      @Id @GeneratedValue  
      private Long id;  
      @NotEmpty  
      private String nombre;  
 ...  
 }  


Item 3 - Crear un validador de spring

Se puede usar el objeto de validación propio de spring mvc org.springframework.validation.Validator.
En este caso hay varias opciones para lanzarlo:
  1. Se puede registrar dentro del métod intBinder del controller, con lo que aplicaría a todos los métodos del mismo. Esta suele ser una opción no deseada, porque obliga a tener un validador para cada tipo de objeto recibido como parámetro.
  2. Se puede registrar dentro de un método propio, también con el initBinder. En este caso sólo aplicará a aquellos métodos del controller que tengan un parámetro como el especificado.
     /**  
           * Se restringe el initBinder para que únicamente aplique a los métodos del controller que tenga  
           * un atributo de ese nombre  
           * @param binder  
           */  
          @InitBinder("usuarioForm")  
          protected void initBinderUsuarioForm(WebDataBinder binder) {  
               //Registro el/los validadores manuales aplicables a usuarioForm  
               binder.setValidator(new UsuarioFormValidator());  
          }  
    
  3. Se puede no declarar en ningún lado y ejecutarlo manualmente al entrar en el controller.


Item 4 - Validaciones combinadas

Se pueden combinar validaciones de spring con las de javax, pero dentro de objeto validador de spring.
La idea es invocar manualmente las validaciones javax, y realizar luego el resto de validaciones de negocio más complejas.


 public class UsuarioFormValidator implements Validator {  
      /**  
       * Logger  
       */  
      private final Logger logger = LoggerFactory.getLogger(getClass());  
      @Override  
      public boolean supports(Class<?> clazz) {  
           return UsuarioForm.class.equals(clazz);  
      }  
      @Override  
      public void validate(Object target, Errors errors) {  
           logger.debug("validando usuarioForm");  
           UsuarioForm usuarioForm = (UsuarioForm) target;            
           Usuario usuario = usuarioForm.getUsuario();  
           realizarValidacionesJavax(usuarioForm, errors);            
           realizarValidacionesComplejasNegocio(usuario, errors);            
           logger.debug("UsuarioForm valdiado con {} errores detectados", errors.getErrorCount());  
      }  
      /**  
       * Validaciones generales de javax validation  
       * @param usuarioForm  
       * @param errors  
       */  
      private void realizarValidacionesJavax(UsuarioForm usuarioForm, Errors errors) {  
           ValidatorFactory factory = Validation.buildDefaultValidatorFactory();  
           javax.validation.Validator validator = factory.getValidator();  
        Set<ConstraintViolation<UsuarioForm>> constraintViolations = validator.validate(usuarioForm);        
        for (ConstraintViolation<UsuarioForm> violation : constraintViolations) {  
                errors.rejectValue(violation.getPropertyPath().toString(),  
                          violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(),  
                          violation.getConstraintDescriptor().getAttributes().values().toArray(),  
                          violation.getMessage());  
           }  
      }  
      /**  
       * Validaciones más específicas relacionadas con el negocio  
       * @param usuario  
       * @param errors  
       */  
      private void realizarValidacionesComplejasNegocio(Usuario usuario, Errors errors) {  
           if (usuario.getRoles() != null && usuario.getRoles().size() > 2) {  
                errors.rejectValue("usuario.roles", "error.ususarios.maxRoles", "Sólo se permiten dos roles por usuario");  
           }            
      }  
 }  

Item 5 - De ConstraintViolation a Errors

Al hacer las validaciones de javax manuales, hay que hacer una transformación entre las constraint violation de javax y el objeto errors de spring.
Para ello se recorren los errores detectados y se extrare la información necesaria para meter un rejectValue.

Item 6 - Prevalencia

Si se tiene un @Valid en el controller y un validador registrado de spring, sólo se ejecutarán las validaciones de spring. Las de javax no se ejecutarán.

Item 7 - Internacionalización

Para internacionalizar los mensajes de spring se necesita un messageSource que referencie a los ficheros properties con los códigos de error.

Para internacionalizar los mensajes de javax.validation se necesita un messageInterpolator. El funcionamiento por defecto es buscar en el classpath un fichero ValidationMessages.properties con todos los textos, pero esto presenta el problema de cómo reconocer los cambios de idioma.
Para solucionarlo, spring proporciona una clase LocalValidatorFactoryBean a la que se le puede setear un messageSource, que es el que contendrá todos los códigos de error para los diferentes idiomas.

La configuración resultante será la siguiente (también se ponen los beans para detectar los cambios de idioma y setearlos):

      <!-- ***************************** -->  
      <!-- Mensajes y etiquetas de spring-->  
      <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
        <property name="cacheSeconds" value="60"/>            
           <property name="basenames">  
         <list>                  
          <value>classpath:bundles/errores</value>           
         </list>  
        </property>       
           <property name="defaultEncoding" value="UTF-8" />  
      </bean>  
      <!-- Para la internacionalización de los mensajes de javax.validation se emplea un inter -->  
      <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">  
        <property name="validationMessageSource" ref="messageSource"/>  
       </bean>  
      <!-- ***************************** -->  
      <!-- ***************************** -->  
      <!-- Locale resolver e interceptor -->  
      <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">  
           <property name="defaultLocale" value="gl" />  
      </bean>  
      <mvc:interceptors>  
           <bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">  
                <property name="paramName" value="idioma" />  
           </bean>  
      </mvc:interceptors>  
      <!-- ***************************** -->  

También hay que tener en cuenta que, en vez de crear el validator a mano, ahora hay que recibir el inyectado por spring en las clases de validación.


 @Component("usuarioFormValidator")  
 public class UsuarioFormValidator implements Validator {  
      /**  
       * Logger  
       */  
      private final Logger logger = LoggerFactory.getLogger(getClass());  
       @Autowired  
       private javax.validation.Validator validator;  
      ...  
 }  

viernes, 29 de agosto de 2014

Fiddler corporate proxy autologin

Cómo hacer que fiddler se autentique automáticamente ante un proxy corporativo.

Visto en http://stackoverflow.com/questions/2989466/configuring-fiddler-to-use-company-networks-proxy


  1. Started Fiddler with it's standard configuration.
  2. Started IE and made a HTTP-request to an external web-site.
  3. The proxy authorization dialogue popped up, where I entered my credentials.
  4. In Fiddler searched the request headers for "Proxy-Authorization".
  5. Copied the header value which looked like "Basic sOMeBASE64eNCODEdSTRING=" to the clipboard.
  6. Altered the CustomRules.js with the following line within OnBeforeRequest:
    oSession.oRequest["Proxy-Authorization"] = "Basic sOMeBASE64eNCODEdSTRING=";

Aclaraciones:
  • Lo que se hace es inyectar una parámetro en la cabecera de la request con el nombre de usuario y password "encriptados" en base64.
  • El fichero de CustomRules.js se puede abrir manualmente desde ${ruta_instalacion}/Fiddler2/Scripts
  • Para aceptar conexiones remotas hay que ir a:
    • Tools
    • Fidler Options
    • Connections
    • Marcar el check de Allow remote computers to connect

sábado, 23 de agosto de 2014

java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/ejb/embeddable/EJBContainer

Problema al intentar hacer un test JUnit de un EJB.

Código:


 package es.pruebas.j2ee6.ejb;  
 import static org.junit.Assert.*;  
 import javax.ejb.embeddable.EJBContainer;  
 import javax.naming.Context;  
 import org.junit.After;  
 import org.junit.Before;  
 import org.junit.Test;  
 import es.pruebas.j2ee6.ejb.service.Calculadora;  
 /**  
  * @author EMILIO  
  *  
  */  
 public class TestCalculadora {  
      private EJBContainer ec;  
   private Context ctx;  
      @Before  
      public void setUp() {  
        ec = EJBContainer.createEJBContainer();  
        ctx = ec.getContext();  
      }  
      @After  
      public void tearDown() {  
           if (ec != null) {  
                ec.close();  
           }  
      }  
   @Test  
   public void testReturnMessage() throws Exception {  
        Calculadora calculadora = (Calculadora) ctx.lookup("java:global/classes/CalculadoraBean");  
        Float valor = calculadora.sumar(50f, 30f);  
        assertEquals(new Float(80), valor);  
   }  
 }  

Excepción generada:


 java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/ejb/embeddable/EJBContainer  
      at java.lang.ClassLoader.defineClass1(Native Method)  
      at java.lang.ClassLoader.defineClass(ClassLoader.java:791)  
      at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)  
      at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)  
      at java.net.URLClassLoader.access$100(URLClassLoader.java:71)  
      at java.net.URLClassLoader$1.run(URLClassLoader.java:361)  
      at java.net.URLClassLoader$1.run(URLClassLoader.java:355)  
      at java.security.AccessController.doPrivileged(Native Method)  
      at java.net.URLClassLoader.findClass(URLClassLoader.java:354)  
      at java.lang.ClassLoader.loadClass(ClassLoader.java:423)  
      at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)  
      at java.lang.ClassLoader.loadClass(ClassLoader.java:356)  
      at java.lang.Class.getDeclaredFields0(Native Method)  
      at java.lang.Class.privateGetDeclaredFields(Class.java:2317)  
      at java.lang.Class.getDeclaredFields(Class.java:1762)  
      at org.junit.runners.model.TestClass.<init>(TestClass.java:44)  
      at org.junit.runners.ParentRunner.<init>(ParentRunner.java:73)  
      at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:55)  
      at org.junit.internal.builders.JUnit4Builder.runnerForClass(JUnit4Builder.java:13)  
      at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)  
      at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:29)  
      at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)  
      at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:24)  
      at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.<init>(JUnit4TestReference.java:33)  
      at org.eclipse.jdt.internal.junit4.runner.JUnit4TestClassReference.<init>(JUnit4TestClassReference.java:25)  
      at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:48)  
      at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:38)  
      at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)  
      at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)  
      at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)  
      at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)  

Solución

Problema mágico y místico que se soluciona al reordenar las dependencias de maven. Hay que poner glassfish-embedded-all de primera.

 <dependencies>  
      <dependency>  
           <groupId>org.glassfish.main.extras</groupId>  
           <artifactId>glassfish-embedded-all</artifactId>  
           <version>4.0</version>  
           <scope>test</scope>  
      </dependency>  
      <dependency>  
           <groupId>javax</groupId>  
           <artifactId>javaee-api</artifactId>  
           <version>6.0</version>  
           <scope>provided</scope>  
      </dependency>  
      <dependency>  
           <groupId>junit</groupId>  
           <artifactId>junit</artifactId>  
           <version>4.10</version>  
           <scope>test</scope>  
      </dependency>  
 </dependencies>  

javax.ejb.EJBException: java.rmi.MarshalException: CORBA MARSHAL 1330446343 No;


Excepción producida al invocar a un EJB remoto.

Código de invocación:

 Carrito carrito = null;  
 try {  
      InitialContext ctx = new InitialContext();  
      carrito = (Carrito) ctx.lookup("java:global/ejb001/CarritoBean");  
      System.out.println(carrito);  
 } catch (Exception e) {  
      e.printStackTrace();  
      System.exit(-1);  
 }  
 carrito.addItem(new Item());  
 System.out.println(carrito.getItems().size());  

Excepción generada:

 Exception in thread "main" javax.ejb.EJBException: java.rmi.MarshalException: CORBA MARSHAL 1330446343 No; nested exception is:  
     org.omg.CORBA.MARSHAL: ----------BEGIN server-side stack trace----------  
 org.omg.CORBA.MARSHAL: FINE: 00810007: Underflow in BufferManagerReadStream after last fragment in message vmcid: OMG minor code: 7 completed: No  
     at com.sun.proxy.$Proxy157.endOfStream(Unknown Source)  
     at com.sun.corba.ee.impl.encoding.BufferManagerReadStream.underflow(BufferManagerReadStream.java:122)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_1.grow(CDRInputStream_1_1.java:111)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_2.alignAndCheck(CDRInputStream_1_2.java:126)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_0.read_long(CDRInputStream_1_0.java:432)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_0.readValueTag(CDRInputStream_1_0.java:1682)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_0.read_value(CDRInputStream_1_0.java:928)  
     at com.sun.corba.ee.impl.encoding.CDRInputObject.read_value(CDRInputObject.java:518)  
     at com.sun.corba.ee.impl.presentation.rmi.DynamicMethodMarshallerImpl$14.read(DynamicMethodMarshallerImpl.java:383)  
     at com.sun.corba.ee.impl.presentation.rmi.DynamicMethodMarshallerImpl.readArguments(DynamicMethodMarshallerImpl.java:450)  
     at com.sun.corba.ee.impl.presentation.rmi.ReflectiveTie._invoke(ReflectiveTie.java:171)  
     at com.sun.corba.ee.impl.protocol.ServerRequestDispatcherImpl.dispatchToServant(ServerRequestDispatcherImpl.java:528)  
     at com.sun.corba.ee.impl.protocol.ServerRequestDispatcherImpl.dispatch(ServerRequestDispatcherImpl.java:199)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleRequestRequest(MessageMediatorImpl.java:1549)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleRequest(MessageMediatorImpl.java:1425)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleInput(MessageMediatorImpl.java:930)  
     at com.sun.corba.ee.impl.protocol.giopmsgheaders.RequestMessage_1_2.callback(RequestMessage_1_2.java:213)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleRequest(MessageMediatorImpl.java:694)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.dispatch(MessageMediatorImpl.java:496)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.doWork(MessageMediatorImpl.java:2222)  
     at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:497)  
     at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:540)  
 ----------END server-side stack trace---------- vmcid: OMG minor code: 7 completed: No  
     at es.pruebas.j2ee6.ejb.service._Carrito_Wrapper.addItem(es/pruebas/j2ee6/ejb/service/_Carrito_Wrapper.java)  
     at es.pruebas.j2ee6.ejb.Main.main(Main.java:26)  
 Caused by: java.rmi.MarshalException: CORBA MARSHAL 1330446343 No; nested exception is:  
     org.omg.CORBA.MARSHAL: ----------BEGIN server-side stack trace----------  
 org.omg.CORBA.MARSHAL: FINE: 00810007: Underflow in BufferManagerReadStream after last fragment in message vmcid: OMG minor code: 7 completed: No  
     at com.sun.proxy.$Proxy157.endOfStream(Unknown Source)  
     at com.sun.corba.ee.impl.encoding.BufferManagerReadStream.underflow(BufferManagerReadStream.java:122)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_1.grow(CDRInputStream_1_1.java:111)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_2.alignAndCheck(CDRInputStream_1_2.java:126)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_0.read_long(CDRInputStream_1_0.java:432)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_0.readValueTag(CDRInputStream_1_0.java:1682)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_0.read_value(CDRInputStream_1_0.java:928)  
     at com.sun.corba.ee.impl.encoding.CDRInputObject.read_value(CDRInputObject.java:518)  
     at com.sun.corba.ee.impl.presentation.rmi.DynamicMethodMarshallerImpl$14.read(DynamicMethodMarshallerImpl.java:383)  
     at com.sun.corba.ee.impl.presentation.rmi.DynamicMethodMarshallerImpl.readArguments(DynamicMethodMarshallerImpl.java:450)  
     at com.sun.corba.ee.impl.presentation.rmi.ReflectiveTie._invoke(ReflectiveTie.java:171)  
     at com.sun.corba.ee.impl.protocol.ServerRequestDispatcherImpl.dispatchToServant(ServerRequestDispatcherImpl.java:528)  
     at com.sun.corba.ee.impl.protocol.ServerRequestDispatcherImpl.dispatch(ServerRequestDispatcherImpl.java:199)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleRequestRequest(MessageMediatorImpl.java:1549)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleRequest(MessageMediatorImpl.java:1425)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleInput(MessageMediatorImpl.java:930)  
     at com.sun.corba.ee.impl.protocol.giopmsgheaders.RequestMessage_1_2.callback(RequestMessage_1_2.java:213)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleRequest(MessageMediatorImpl.java:694)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.dispatch(MessageMediatorImpl.java:496)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.doWork(MessageMediatorImpl.java:2222)  
     at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:497)  
     at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:540)  
 ----------END server-side stack trace---------- vmcid: OMG minor code: 7 completed: No  
     at com.sun.corba.ee.impl.javax.rmi.CORBA.Util.mapSystemException(Util.java:266)  
     at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:211)  
     at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:150)  
     at com.sun.corba.ee.impl.presentation.rmi.codegen.CodegenStubBase.invoke(CodegenStubBase.java:226)  
     at es.pruebas.j2ee6.ejb.service.__Carrito_Remote_DynamicStub.addItem(es/pruebas/j2ee6/ejb/service/__Carrito_Remote_DynamicStub.java)  
     ... 2 more  
 Caused by: org.omg.CORBA.MARSHAL: ----------BEGIN server-side stack trace----------  
 org.omg.CORBA.MARSHAL: FINE: 00810007: Underflow in BufferManagerReadStream after last fragment in message vmcid: OMG minor code: 7 completed: No  
     at com.sun.proxy.$Proxy157.endOfStream(Unknown Source)  
     at com.sun.corba.ee.impl.encoding.BufferManagerReadStream.underflow(BufferManagerReadStream.java:122)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_1.grow(CDRInputStream_1_1.java:111)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_2.alignAndCheck(CDRInputStream_1_2.java:126)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_0.read_long(CDRInputStream_1_0.java:432)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_0.readValueTag(CDRInputStream_1_0.java:1682)  
     at com.sun.corba.ee.impl.encoding.CDRInputStream_1_0.read_value(CDRInputStream_1_0.java:928)  
     at com.sun.corba.ee.impl.encoding.CDRInputObject.read_value(CDRInputObject.java:518)  
     at com.sun.corba.ee.impl.presentation.rmi.DynamicMethodMarshallerImpl$14.read(DynamicMethodMarshallerImpl.java:383)  
     at com.sun.corba.ee.impl.presentation.rmi.DynamicMethodMarshallerImpl.readArguments(DynamicMethodMarshallerImpl.java:450)  
     at com.sun.corba.ee.impl.presentation.rmi.ReflectiveTie._invoke(ReflectiveTie.java:171)  
     at com.sun.corba.ee.impl.protocol.ServerRequestDispatcherImpl.dispatchToServant(ServerRequestDispatcherImpl.java:528)  
     at com.sun.corba.ee.impl.protocol.ServerRequestDispatcherImpl.dispatch(ServerRequestDispatcherImpl.java:199)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleRequestRequest(MessageMediatorImpl.java:1549)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleRequest(MessageMediatorImpl.java:1425)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleInput(MessageMediatorImpl.java:930)  
     at com.sun.corba.ee.impl.protocol.giopmsgheaders.RequestMessage_1_2.callback(RequestMessage_1_2.java:213)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.handleRequest(MessageMediatorImpl.java:694)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.dispatch(MessageMediatorImpl.java:496)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.doWork(MessageMediatorImpl.java:2222)  
     at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:497)  
     at com.sun.corba.ee.impl.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:540)  
 ----------END server-side stack trace---------- vmcid: OMG minor code: 7 completed: No  
     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)  
     at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)  
     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)  
     at java.lang.reflect.Constructor.newInstance(Constructor.java:525)  
     at com.sun.corba.ee.impl.protocol.giopmsgheaders.MessageBase.getSystemException(MessageBase.java:813)  
     at com.sun.corba.ee.impl.protocol.giopmsgheaders.ReplyMessage_1_2.getSystemException(ReplyMessage_1_2.java:131)  
     at com.sun.corba.ee.impl.protocol.MessageMediatorImpl.getSystemExceptionReply(MessageMediatorImpl.java:594)  
     at com.sun.corba.ee.impl.protocol.ClientRequestDispatcherImpl.processResponse(ClientRequestDispatcherImpl.java:519)  
     at com.sun.corba.ee.impl.protocol.ClientRequestDispatcherImpl.marshalingComplete(ClientRequestDispatcherImpl.java:393)  
     at com.sun.corba.ee.impl.protocol.ClientDelegateImpl.invoke(ClientDelegateImpl.java:272)  
     at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:198)  
     ... 5 more  

Solución:

El problema está en que todos los parámetros intercambiados en a través de un EJB remoto tienen que ser serializables, porque se envían a través de RMI.

Hay que hacer que todo objeto implemente el interface java.io.Serializable.