Tuesday, September 3, 2013

EJB 3.1 - Local and Remote business interfaces must differ

Have you ever seen something like this?
Exception during lifecycle processing
java.lang.IllegalStateException: The interface XYZ cannot be both a local and a remote business interface
 at com.sun.enterprise.deployment.archivist.Archivist.readAnnotations(Archivist.java:518)
 at com.sun.enterprise.deployment.archivist.Archivist.readAnnotations(Archivist.java:446)
 at com.sun.enterprise.deployment.archivist.Archivist.readRestDeploymentDescriptors(Archivist.java:419)
 at com.sun.enterprise.deployment.archivist.Archivist.readDeploymentDescriptors(Archivist.java:396)
 at com.sun.enterprise.deployment.archivist.Archivist.open(Archivist.java:271)
 at com.sun.enterprise.deployment.archivist.Archivist.open(Archivist.java:280)
 at com.sun.enterprise.deployment.archivist.ApplicationArchivist.readModulesDescriptors(ApplicationArchivist.java:611)
 at com.sun.enterprise.deployment.archivist.ApplicationArchivist.openWith(ApplicationArchivist.java:229)
 at com.sun.enterprise.deployment.archivist.ApplicationFactory.openWith(ApplicationFactory.java:232)
 at org.glassfish.javaee.core.deployment.DolProvider.processDOL(DolProvider.java:188)
 at org.glassfish.javaee.core.deployment.DolProvider.load(DolProvider.java:222)
 at org.glassfish.javaee.core.deployment.DolProvider.load(DolProvider.java:96)
 at com.sun.enterprise.v3.server.ApplicationLifecycle.loadDeployer(ApplicationLifecycle.java:878)
 at com.sun.enterprise.v3.server.ApplicationLifecycle.setupContainerInfos(ApplicationLifecycle.java:818)
 at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:374)
 at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:219)
 at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:491)
 at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:527)
 at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:523)
 at java.security.AccessController.doPrivileged(Native Method)
 at javax.security.auth.Subject.doAs(Subject.java:356)
 at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:522)
 at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:546)
 at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1423)
 at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1500(CommandRunnerImpl.java:108)
 at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1762)
 at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1674)
 at org.glassfish.admin.rest.resources.admin.CommandResource.executeCommand(CommandResource.java:396)
 at org.glassfish.admin.rest.resources.admin.CommandResource.execCommandSimpInMultOut(CommandResource.java:234)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
 at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:125)
 at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:152)
 at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:91)
 at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:346)
 at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:341)
 at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:101)
 at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:224)
 at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
 at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
 at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
 at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
 at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
 at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
 at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:198)
 at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:946)
 at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:331)
 at org.glassfish.admin.rest.adapter.JerseyContainerCommandService$3.service(JerseyContainerCommandService.java:165)
 at org.glassfish.admin.rest.adapter.RestAdapter.service(RestAdapter.java:181)
 at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:246)
 at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
 at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
 at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
 at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
 at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
 at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
 at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
 at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
 at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
 at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
 at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
 at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
 at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
 at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
 at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
 at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
 at java.lang.Thread.run(Thread.java:724)
Caused by: The interface XYZ cannot be both a local and a remote business interface
 at org.glassfish.ejb.deployment.annotation.handlers.AbstractEjbHandler.setBusinessAndHomeInterfaces(AbstractEjbHandler.java:471)
 at org.glassfish.ejb.deployment.annotation.handlers.StatelessHandler.setEjbDescriptorInfo(StatelessHandler.java:143)
 at org.glassfish.ejb.deployment.annotation.handlers.AbstractEjbHandler.processAnnotation(AbstractEjbHandler.java:270)
 at com.sun.enterprise.deployment.annotation.factory.SJSASFactory$LazyAnnotationHandler.processAnnotation(SJSASFactory.java:148)
 at org.glassfish.apf.impl.AnnotationProcessorImpl.process(AnnotationProcessorImpl.java:344)
 at org.glassfish.apf.impl.AnnotationProcessorImpl.process(AnnotationProcessorImpl.java:375)
 at org.glassfish.apf.impl.AnnotationProcessorImpl.processAnnotations(AnnotationProcessorImpl.java:289)
 at org.glassfish.apf.impl.AnnotationProcessorImpl.process(AnnotationProcessorImpl.java:195)
 at org.glassfish.apf.impl.AnnotationProcessorImpl.process(AnnotationProcessorImpl.java:134)
 at com.sun.enterprise.deployment.archivist.Archivist.processAnnotations(Archivist.java:626)
 at com.sun.enterprise.deployment.archivist.Archivist.readAnnotations(Archivist.java:462)
 ... 69 more
where XYZ would be the name of the interface referred as Local and Remote from some stateless session bean.

Well, once I did migration to Glassfish 4.0 I've noticed that. The message is quite clear, however the question is why that would be a problem. As having one interface serving as Local as well as Remote for stateless session EJB is quite common practice I think.

Root cause

The JBoss forum message pointed me to the right direction.

The thing is that EJB 3.1 specs (implemented in Glassfish 4.0) section 4.9.7 says:
The same business interface cannot be both a local and a remote business interface of the bean.
For those interested, see the full specs on: http://download.oracle.com/otndocs/jcp/ejb-3.1-fr-eval-oth-JSpec/

Solution

OK, but how to fix it? I recommend you to read Adam Bien's suggestion.

Basically, the idea might be the analogy to package protection present in java and easy solution can be creation of extra interface that would inherit existing one and be used as Local. Possibly having Local suffix.

No comments: