Given the following code
public interface Foo<T> {
T get();
}
@Remote
public interface Bar extends Foo<String> {
}
@Stateless
public class BarImpl implements Bar {
@Interceptors(ExceptionInterceptor.class)
public String get() {
throw new RuntimeException("not implemented");
}
}
public class ExceptionInterceptor {
@AroundInvoke
public Object convertExceptionForExternalSystem(InvocationContext ctx) throws RuntimeException, Error {
try
{
return ctx.proceed();
}
catch (Throwable e)
{
if (e instanceof Error)
throw new Error("Changed");
throw new RuntimeException("Changed");
}
}
}
When we call a method on the remote,
Bar bar = context.lookup(Bar.class.getName());
bar.get();
or
Foo foo = context.lookup(Bar.class.getName());
foo.get();
the interceptor is not called (using Glassfish 3.0.1).
The problem seems to be due to the fact that the compiled class file for the interface is
javap Foo
Compiled from "Foo.java"
public interface Foo{
public abstract java.lang.Object get();
}
while for BarImpl it is
javap BarImpl
Compiled from "BarImpl.java"
public class BarImpl extends java.lang.Object implements Bar{
public BarImpl();
public java.lang.String get();
public java.lang.Object get();
}
So, when we call
Bar bar = ...;
bar.get();
Internally the method
public java.lang.Object get();
is called, which will delegate to
public java.lang.String get();
Interceptors seem to only be invoked, when the latter is called directly. When I change the interface Bar to
@Remote
public interface Bar extends Foo<String> {
@Override
String get();
}
The interceptor is called in the first call (bar.get()), but not in the second call (foo.get()). Defining the interceptors on the class level might fix the proplem, but is not an option in our开发者_C百科 case.
Do we do something wrong, or is this a general problem of java-ee-6, or is this a bug in glassfish? Is there a workaround? Or should we abandon using generics in our Services at all?
Since we're dealing with Java, type erasure takes over during runtime and your business interface will ever only contain:
public Object get();
You are casting the interface to Bar
and that's ok (no runtime class cast exception) but the business method that's being called is the one present in the business interface (as that's the only method the client knows about): the Object get()
version.
Also, since, by design, only business method invocations are intercepted, Object get()
delegating on String get()
does not get intercepted (it's just a method calling another method). This means that, in your initial scenario, you're trying to intercept a method that does not belong to the business interface and thus will never be intercepted.
Since the EJB3 spec is really not clear about generics (mostly nothing is said), implementations really delegate that part on the JVM. There's no way of getting true generic support in this scenario and I would say you're stuck with class interceptors or changing your business interface.
精彩评论