Summary
#AroundInvoke interceptor is called twice on a #WebService class,
if the intercepted method is called from outside of the application via endpoint as a SOAP web service.
If the very same method is called internally from another bean, it's called only once (as I would expect).
The intercepted method itself is always called only once!
Question 1: Can I make the interceptor to be called only once?
Question 2: If I cannot, is there a transferable (server independent) way to decide in which interceptor I am, so I can ignore the redundant one?
Question 3: Is this behaviour common (and defined and described in some documentation),
or is it dependent on my specific environment (JBoss EAP 6.4.0)?
Observation:
The two calls are not in the same interceptor chain.
It is not the same instance of the interceptor class.
The implementation class of the InvocationContext is different for both the calls.
It's funny that one of the contextData, the InvocationContext's field for passing data along the interceptor chain, is not an instance of the HashMap, but WrappedMessageContext, but it does not wrap the other contextData anyway.
Minimal reproducible code
(I removed the package name.)
MyEndpoint interface
import javax.jws.WebService;
#WebService
public interface MyEndpoint {
public static final String SERVICE_NAME = "MyEndpointService";
public String getHello();
}
MyEndpointImpl class
import javax.interceptor.Interceptors;
import javax.jws.WebService;
#WebService(endpointInterface = "MyEndpoint", serviceName = MyEndpoint.SERVICE_NAME)
#Interceptors({TestInterceptor.class})
public class MyEndpointImpl implements MyEndpoint {
#Override
public String getHello() {
System.out.println("MyEndpointImpl.getHello() called");
return "Hello";
}
}
TestInterceptor class
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
public class TestInterceptor {
#AroundInvoke
private Object countCalls(InvocationContext ic) throws Exception {
System.out.println("Interceptor called");
return ic.proceed();
}
}
Output
Interceptor called
Interceptor called
MyEndpointImpl.getHello() called
More details
To get more runtime information, I added more logging.
MyEndpointImpl class
import java.lang.reflect.Method;
import java.util.Map;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestInterceptor {
private static Logger logger = LoggerFactory.getLogger(TestInterceptor.class);
private static int callCnt = 0;
#AroundInvoke
private Object countCalls(InvocationContext ic) throws Exception {
final String interceptorClass = this.toString();
final String invocationContextClass = ic.getClass().getName();
final Method method = ic.getMethod();
final String calledClass = method.getDeclaringClass().getName();
final String calledName = method.getName();
final String message = String.format(
"%n INTERCEPTOR: %s%n InvocationContext: %s%n %s # %s()",
interceptorClass, invocationContextClass, calledClass, calledName);
logger.info(message);
final int call = ++callCnt;
final Map<String, Object> contextData = ic.getContextData();
contextData.put("whoami", call);
logger.info("BEFORE PROCEED {}, {}", call, contextData);
final Object ret = ic.proceed();
logger.info("AFTER PROCEED {}, {}", call, contextData);
return ret;
}
}
Output
INTERCEPTOR: TestInterceptor#74c90b72
InvocationContext: org.jboss.invocation.InterceptorContext$Invocation
MyEndpointImpl # getHello()
BEFORE PROCEED 1, org.apache.cxf.jaxws.context.WrappedMessageContext#2cfccb1d
INTERCEPTOR: TestInterceptor#5226f6d8
InvocationContext: org.jboss.weld.interceptor.proxy.InterceptorInvocationContext
MyEndpointImpl # getHello()
BEFORE PROCEED 2, {whoami=2}
MyEndpointImpl.getHello() called
AFTER PROCEED 2, {whoami=2}
AFTER PROCEED 1, org.apache.cxf.jaxws.context.WrappedMessageContext#2cfccb1d
I cannot answer your questions directly, but maybe some clarification about the contexts may help you.
The Java EE JAX-WS implementation varies from server to server. For Example Glassfish uses Metro and JBoss uses Apache CXF.
There are different kind of interceptors chains which allow to control programmatically the conditions before and after the request/response processing.
The interceptors for the SOAP web service calls are SOAP handlers and logical handlers (See Oracle documentation). Both can access SOAP message on different levels (the whole or only the payload).
My assumption is that your the interceptor called twice, once for accessing through HTTP/SOAP, and once for access over RMI.
In the first interceptor invocation, what you see as context is org.apache.cxf.jaxws.context.WrappedMessageContext which is a Map implementation. See WarppedMessageContext, Apache CXF web service context. It is invoked for HTTP/SOAP access.
The second invocation is what you expect when using the RMI (probably triggered from Apache CXF, after the SOAP message is processed).
To avoid this you can use third class for logic implementation with interceptor defined. The existing web service implementation class will only delegate to it and will not contain interceptor annotation anymore.
Example code can be seen here: OSCM Project
I had the exact same problem and found a solution.
If you instead of using the #Interceptors style binding use a #InterceptorBinding style binding, then the interceptor is only instantiated and invoked once (at least in my case on WildFly 10.1.0.Final).
This is what your example would look like using #InterceptorBinding style.
Your custom interceptor binding annotation:
import javax.interceptor.InterceptorBinding;
...
#Inherited
#InterceptorBinding
#Retention(RUNTIME)
#Target({METHOD, TYPE})
public #interface MyInterceptorBinding {
Your endpoint:
#WebService(endpointInterface = "MyEndpoint", serviceName =
MyEndpoint.SERVICE_NAME)
#MyInterceptorBinding
public class MyEndpointImpl implements MyEndpoint {
Your interceptor:
import javax.interceptor.Interceptor;
import javax.annotation.Priority;
...
#Interceptor
#MyInterceptorBinding
#Priority(Interceptor.Priority.APPLICATION) //we use #Priority to enable this interceptor application-wide, without having to use beans.xml in every module.
public class TestInterceptor {
#AroundInvoke
private Object countCalls(InvocationContext ic) throws Exception {
System.out.println("Interceptor called");
return ic.proceed();
}
I never figured out exactly what the problem is, but I suspect that the #Interceptors style binding is valid for multiple types of interceptors (EJB and CDI) while the #InterceptorBinding style is maybe only valid for CDI interceptors.
Maybe a JAX-WS #WebService is both an EJB and a CDI bean?
Related
I have a simple class service and it is being injected beautifully on my application. However, I am trying to inject the messages api to read a few keys on my message files but I am getting the same error:
1) Could not find a suitable constructor in play.i18n.Messages.
Classes must have either one (and only one) constructor annotated with
#Inject or a zero-argument constructor that is not private. at
play.i18n.Messages.class(Messages.java:61)
public class SampleServiceImpl implements SampleService {
private MessagesApi messages;
#Inject
public SampleServiceImpl(MessagesApi messages){
this.messages = messages;
}
}
#ImplementedBy(SampleServiceImpl.class)
public interface SampleService {
}
Is the a way to do that by DI?
Edit:
I was able to get the value by doing this but it does not look elegant, any options ?
messages.get(new Lang(new Locale("en")), "ticket.form.title")
The reason of such "non-elegancy" is that language (and Messages) depends on the request.
The Default behavior is that Messages detect current language on the base of cookie, available languages and default language.
Someware under the hood: Messages messages = messagesApi.preferred(request());
Will select a language from the request, based on the languages
available, and fallback to the default language if none of the
candidates are available.
Fortunately, there is a special method that you can use to initialize Messages with the language you want:
import play.i18n.MessagesApi;
import play.i18n.Messages;
import play.i18n.Lang;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
...
Locale englishLocale = new Locale("en");
Lang englishLang = new Lang(englishLocale);
List<Lang> prefferedLangs = Arrays.asList(englishLang);
Messages messagesCustom = messagesApi.preferred(prefferedLangs);
// the time for elegancy
messages.at("ticket.form.title");
I advise you to create tiny MessagesApiCustom service, that will do this few strings of code in the initialization time and then will proxy the at method to the messages.at, so it will look like:
public class SampleServiceImpl implements SampleService {
private MessagesApiCustom messages;
#Inject
public SampleServiceImpl(MessagesApiCustom messages){
this.messages = messages;
}
private void doSomeStuff(){
Strign message = messages.at("message.key")
}
}
You can go further, and implement language selection based on annotation:
#Named("FR")
private MessagesApiCustom messages;
Of course, if you need the dynamic language selection, then just use the method that is already present in Play.
I have a class like this:
public class MyResource(){
public MyResource(#Context ServletContext context){
context.setAttribute("someAttribute","someVal");
}
#PUT
public void someMethod(){
...
}
}
and I would like to this using annotations (i.e. JAX-RS/Jersey reads the value of the annotation and writes it into ServletContext so that I can access this value somewhere else where I inject the ServletContext in the request scope.)
#MyCustomAnnotation(name="someVal")
public class MyResource(){
}
Annotation needs to be treated by some code.
You need to create a filter, that processes your custom annotation, before your method is called.
see : https://jersey.java.net/documentation/latest/filters-and-interceptors.html
Creating a filter, should be fairly easy, but it is not enough. It will get called, but won't know in what context it will be called. By context, I mean which class / method will be called right after the filter is executed. In this example I assumed your annotation (called MyCustomAnnotation) can be applied to class / method.
For this, you need to create a "Dynamic Feature" that will bind a different instance of the filter, for each possible context.
In details :
For a given JAX-RS class :
#MyCustomAnnotation(name="someVal")
class MyClass{
#GET
#MyCustomAnnotation(name="someConfig")
public Object myMethod(){
...
}
#GET
#MyCustomAnnotation(name="otherConfig")
public Object myOtherMethod(){
...
}
}
First, create your annotation (I guess you know, but just to be clear) :
#Target({ ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface MyCustomAnnotation {
String name() default "";
}
Then, create a filter.
Notice the special constructor. A different instance of the filter will be created for each possible context. The right instance of the filter will be used in a specific context. This way it will know in what context (Class / Method) is is called. This way, using intro-spectation, your filter can behave however you like, based of the annotation you used on your target class and/or method :
#Priority(Priorities.AUTHORIZATION - 1)
public class MyFilter implements ContainerRequestFilter {
private final Class<?> _class;
private final Method method;
private MyCustomAnnotation classAnnotation;
private MyCustomAnnotation methodAnnotation;
public MyFilter(Class<?> _class, Method method) {
this._class = _class;
this.method = method;
this.classAnnotation = _class.getAnnotation(MyCustomAnnotation.class);
this.methodAnnotation = method.getAnnotation(MyCustomAnnotation.class);
}
#Override
public void filter(ContainerRequestContext requestContext) {
// your code goes here!
// based on classAnnotation and/or methodAnnotation,
// add whatever you want to the requestContext
}
}
Ok, so now we have an annotation, a filter that process this annotation, now we need to bind dynamically to class / methods that are annotated
public class MyFilterDynamicFeature implements DynamicFeature {
#Override
public void configure(final ResourceInfo resourceInfo, final FeatureContext configuration) {
//if the class or the method is annotated, bind a new instance of our filter to this method
if(resourceInfo.getResourceClass().getAnnotation(MyCustomAnnotation.class)!=null || resourceInfo.getResourceMethod().getAnnotation(MyCustomAnnotation.class)!=null){
configuration.register(new MyFilter(resourceInfo.getResourceClass(), resourceInfo.getResourceMethod()));
}
}
}
In your JAX-RS configuration... register your new DynamicFeature
public class MyRestConfig extends ResourceConfig {
public RestConfig() {
// your configs...
packages("com.yourpackage.rest");
// ...
// handle #MyCustomAnnotation annotations
register(MyFilterDynamicFeature.class);
// ...
}
}
I hope this is clear. Recap of what you need to do
create your annotation
annotate your JAX-RS class / method with your annotation
create a filter that will process your annotation
create a dynamic feature, that will bind a different instance of the filter for each different context (method / class combination, where at least one or the other is annotated with your annotation)
register the dynamic feature in your rest config
----Update--------
Rather than using the Dynamic Feature, you should be able to inject the ressource info at runtime
#Context
private ResourceInfo resourceInfo;
I have a simple Dropwizard 0.8.1 REST service that pulls in Jersey 2.17. Upstream of the REST/Jetty service I have some authentication service that adds some nice authorization information to the HTTP Header that gets passed to my Dropwizard app.
I would love to be able to create a custom annotation in my Resource that hides all the messy header-parsing-to-POJO garbage. Something like this:
#Path("/v1/task")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class TaskResource {
#UserContext // <-- custom/magic annotation
private UserContextData userContextData; // <-- holds all authorization info
#GET
public Collection<Task> fetch() {
// use the userContextData to differentiate what data to return
}
I've spent the last day looking around stackoverflow and found several other people who had the same issue and appeared (?) to get some satisfaction, but I can't seem to avoid getting a "Not inside a request scope" stack trace when I try to do this.
So I stashed all my changes and tried to implement the example provided in sections 22.1 and 22.2 by the Jersey documentation directly: https://jersey.java.net/documentation/2.17/ioc.html
Following along with their example (but in my Dropwizard app), I'm trying to get a "#SessionInject" annotation in my Resource, but it also blows up with "Not inside a request scope" stack trace each time. What am I doing wrong here?
Resource:
#Path("/v1/task")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class TaskResource {
private final TaskDAO taskDAO;
#Context
private HttpServletRequest httpRequest;
#SessionInject
private HttpSession httpSession;
public TaskResource(TaskDAO taskDAO) {
this.taskDAO = taskDAO;
}
#GET
public Collection<Task> fetch(#SessionInject HttpSession httpSession) {
if (httpSession != null) {
logger.info("TOM TOM TOM httpSession isn't null: {}", httpSession);
}
else {
logger.error("TOM TOM TOM httpSession is null");
}
return taskDAO.findAllTasks();
}
The SessionInjectResolver:
package com.foo.admiral.integration.jersey;
import com.foo.admiral.integration.core.SessionInject;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpSession;
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SessionInjectResolver implements InjectionResolver<SessionInject> {
private static final Logger logger = LoggerFactory.getLogger(HttpSessionFactory.class);
#Inject
#Named(InjectionResolver.SYSTEM_RESOLVER_NAME)
InjectionResolver<Inject> systemInjectionResolver;
#Override
public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
if (HttpSession.class == injectee.getRequiredType()) {
return systemInjectionResolver.resolve(injectee, handle);
}
return null;
}
#Override
public boolean isConstructorParameterIndicator() {
return false;
}
#Override
public boolean isMethodParameterIndicator() {
return false;
}
}
The HttpSessionFactory:
package com.foo.admiral.integration.jersey;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.glassfish.hk2.api.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Singleton
public class HttpSessionFactory implements Factory<HttpSession> {
private static final Logger logger = LoggerFactory.getLogger(HttpSessionFactory.class);
private final HttpServletRequest request;
#Inject
public HttpSessionFactory(HttpServletRequest request) {
logger.info("Creating new HttpSessionFactory with request");
this.request = request;
}
#Override
public HttpSession provide() {
logger.info("Providing a new session if one does not exist");
return request.getSession(true);
}
#Override
public void dispose(HttpSession t) {
}
}
The annotation:
package com.foo.admiral.integration.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD})
public #interface SessionInject {
}
And, finally, the binding in the Dropwizard Application class:
#Override
public void run(TodoConfiguration configuration, Environment environment) throws Exception {
...
environment.jersey().register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(HttpSessionFactory.class).to(HttpSession.class);
bind(SessionInjectResolver.class)
.to(new TypeLiteral<InjectionResolver<SessionInject>>() { })
.in(Singleton.class);
}
});
Ye old stack trace:
Caused by: java.lang.IllegalStateException: Not inside a request scope.
at jersey.repackaged.com.google.common.base.Preconditions.checkState(Preconditions.java:149)
at org.glassfish.jersey.process.internal.RequestScope.current(RequestScope.java:233)
at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:158)
at org.jvnet.hk2.internal.MethodInterceptorImpl.invoke(MethodInterceptorImpl.java:74)
at org.jvnet.hk2.internal.MethodInterceptorInvocationHandler.invoke(MethodInterceptorInvocationHandler.java:62)
at com.sun.proxy.$Proxy72.getSession(Unknown Source)
at com.foo.admiral.integration.jersey.HttpSessionFactory.provide(HttpSessionFactory.java:29)
at com.foo.admiral.integration.jersey.HttpSessionFactory.provide(HttpSessionFactory.java:14)
Some clues that may be useful:
1) I'm noticing is that the logging statements in my HttpSessionFactory are never getting fired, so I don't think the Factory is correctly identified to DropWizard.
2) If I change the annotation to be a Parameter instead of a Field and move the use of the annotation into the fetch( ) method signature like this, it doesn't throw the stack trace (but the httpSession is still null, presumably because the Factory isn't firing...)
public Collection<Task> fetch(#SessionInject HttpSession httpSession) {
3) It doesn't appear to matter if I "register" the binder with environment.jersey().register() or environment.jersey().getResourceConfig().register()... they appear to do the same thing.
Do you see any obvious problems? Thanks in advance!
This is weird behavior. But what looks like is going on is the following
You have registered TaskResource as an instance and not as a .class. This I'm pretty sure of (though you have not mentioned).
register(new TaskResource());
/* instead of */
register(TaskResource.class);
Doing the former, it set the resource in a singleton scope. The latter in a request scope (unless annotated otherwise - see below)
When the resource model is loading it sees the TaskResource is a singleton, and that the HttpServletRequest is in a request scope. Either that or that the factory is in a per request scope. I'm guessing one of the two.
I thought that it might actually be a scope issue, as mentioned in the error message, but what I'm pretty sure of is that at runtime, it will get handled with a thread local proxy, because of the lesser scope.
You can see it fixed by registering the TaskResource as a class, and then annotating the TaskResource with #Singleton. This is if you actually do want the resource class to be a singleton. If not, then just leave off the #Singleton.
The odd thing to me is that it the fact that it fails on startup when the resource is explicitly instantiated on startup, but works when the framework loads on the first request (which is what happens when you register it as a class). They are both still in a singleton scope.
One thing you might want to take into consideration is whether you actually want the resource to be a singleton or not. You do have to worry about thread safety issues with singletons, and there are are some other limitations. Personally, I prefer to keep them in a request scope. You would have to do some performance testing to see if there is much of an impact for your application.
UPDATE
For parameter injection you may want to take a look at this post
UPDATE 2
See Also
jersey 2 context injection based upon HttpRequest without singleton. My answer should shed some more light.
I have this in src/main/groovy/...
package com.mycompany.web;
// imports....
#Controller
class GroovyController {
#RequestMapping("/status_groovy")
public #ResponseBody String getStatus() {
return "Hello World from groovy!";
}
}
Using maven 3 and spring 3.1 (Milestone). Spring MVC works perfectly well for java controllers and everything is set up fine. The groovy class compiles fine and can be found in the classes directory along with the java controller classes.
I have similar controller written in java (JavaController) in same package but under src/main/java and its getting picked up properly by spring and mapped and I can see the response on screen when I hit the url.
package com.mycompany.web;
// imports....
#Controller
class JavaController {
#RequestMapping("/status")
public #ResponseBody String getStatus() {
return "Hello World!";
}
}
Jetty starts normally with no error in log but in I dont see groovy url getting mapped whereas i can see the java one.
2011-09-23 16:05:50,412 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/status],methods=[],params=[],headers=[],consumes=[],produces=[]}" onto public java.lang.String com.mycompany.web.JavaController.getStatus()
All the setting are fine as other parts of app are working just fine with annotations (component-scan etc.), Just that I can not get the url mapped in GroovyController
Can anyone explain what needs to be done in order to get Controllers written in groovy working?
PS: I am avoiding GroovyServlet to run the scripts because it has major downside when it comes to bean injection and url path mappings.
With all due respect to Ben (whom I work with), the problem here isn't that Spring is creating a cglib proxy. Rather, it's creating a dynamic JDK (or interface-based) proxy. This method of creating proxies can only implement methods declared in the target's implemented interfaces. You actually want Spring to create a cglib proxy, which creates a proxy that is a subclass of the target object and can therefore recreate all of its public methods. Unless you specify otherwise, Spring will create a cglib proxy if the target object doesn't implement any interfaces, and an interface-based proxy otherwise. Since all Groovy objects implement GroovyObject, you're getting an interface-based proxy, even though you didn't explicitly implement any interfaces in your Groovy controller. Ben's solution is correct in that if you create an interface with all your controller methods, you'll get the expected behavior. An alternative is to create a BeanFactoryPostProcessor which instructs Spring to create cglib proxies for classes that implement GroovyObject and only GroovyObject. Here's the code:
/**
* Finds all objects in the bean factory that implement GroovyObject and only GroovyObject, and sets the
* AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE value to true. This will, in the case when a proxy
* is necessary, force the creation of a CGLIB subclass proxy, rather than a dynamic JDK proxy, which
* would create a useless proxy that only implements the methods of GroovyObject.
*
* #author caleb
*/
public class GroovyObjectTargetClassPreservingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private static final Logger logger = LoggerFactory.getLogger(GroovyObjectTargetClassPreservingBeanFactoryPostProcessor.class);
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanDefName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanDefName);
//ignore abstract definitions (parent beans)
if (bd.isAbstract())
continue;
String className = bd.getBeanClassName();
//ignore definitions with null class names
if (className == null)
continue;
Class<?> beanClass;
try {
beanClass = ClassUtils.forName(className, beanFactory.getBeanClassLoader());
}
catch (ClassNotFoundException e) {
throw new CannotLoadBeanClassException(bd.getResourceDescription(), beanDefName, bd.getBeanClassName(), e);
}
catch (LinkageError e) {
throw new CannotLoadBeanClassException(bd.getResourceDescription(), beanDefName, bd.getBeanClassName(), e);
}
Class<?>[] interfaces = beanClass.getInterfaces();
if (interfaces.length == 1 && interfaces[0] == GroovyObject.class) {
logger.debug("Setting attribute {} to true for bean {}", AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, beanDefName);
bd.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, true);
}
}
}
}
Just include a bean of this type in your context, and voila! You can have Groovy controllers without needing to define interfaces.
I beg to differ. There is no need to implement an interface. The problem here is that the default AnnotationMethodHandlerAdapter does not read annotations from proxies. Hence we would have to create this proxy aware AnnotationMethodHandlerAdapter which extends the default AnnotationMethodHandlerAdapter of spring. We also need to instantiate a bean for this ProxyAwareAnnotationMethodHandlerAdapter in the Spring Configuration xml file.
Note: This feature is not available in Spring 3.x but since spring 4.0 would support groovy beans, this feature should be covered.
//ProxyAwareAnnotationMethodHandlerAdapter.java
package name.assafberg.spring;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
/**
* Add proxy awareness to <code>AnnotationMethodHandlerAdapter</code>.
*
* #author assaf
*/
public class ProxyAwareAnnotationMethodHandlerAdapter extends AnnotationMethodHandlerAdapter {
/**
* #param request
* #param response
* #param handler
* #return
* #throws Exception
* #see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
*/
#Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
handler = unwrapHandler(handler);
return super.handle(request, response, handler);
}
/**
* #param handler
* #return
* #see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#supports(java.lang.Object)
*/
#Override
public boolean supports(Object handler) {
handler = unwrapHandler(handler);
return super.supports(handler);
}
/**
* Attempt to unwrap the given handler in case it is an AOP proxy
*
* #param handler
* #return Object
*/
private Object unwrapHandler(Object handler) {
if (handler instanceof Advised) {
try {
TargetSource targetSource = ((Advised) handler).getTargetSource();
return targetSource.getTarget();
} catch (Exception x) {
throw new RuntimeException(x);
}
} else {
return handler;
}
}
}
The spring configuration XML file must have the following. Instead of creating a bean of AnnotationMethodHandlerAdapter we must create a ProxyAwareAnnotationMethodHandlerAdapter bean.
<beans .........
...
...
<bean class="full.qualified.name.of.ProxyAwareAnnotationMethodHandlerAdapter" />
...
...
<lang:groovy script-source="classpath:com/example/mysample.groovy refresh-check-delay="1000" />
</beans>
Also Spring parses the configuration XML file using a SAX parser (based on event occurence). So, in order for spring to understand the annotations within the groovy scripts, the groovy beans (using tag) must be created after the ProxyAwareAnnotationMethodHandlerAdapter.
Hope than helps
Reference: http://forum.springsource.org/showthread.php?47271-Groovy-Controller
Unfortunately, if you want to get this running in Groovy you'll have to create an interface for your Controller class and annotate the method definitions as well. Spring creates a proxy for your class using Cglib. However, without creating a custom interface for your controller Spring is proxying on groovy.lang.GroovyObject because all Groovy objects implement that interface by default.
interface GroovyControllerInterface {
#RequestMapping("/status_groovy")
#ResponseBody String getStatus()
}
#Controller
class GroovyController implements GroovyControllerInterface {
#RequestMapping("/status_groovy")
public #ResponseBody String getStatus() {
return "Hello World from groovy!";
}
}
I'm trying to create an EJB factory class, which works like this: You have a method which takes as argument a class of an EJB, then it checks whether the EJB has a remote interface (if not throw an exception) and if it does, it returns the concerning EJB.
The code below does exactly this. However the object it returns is of the type of the remote interface of the concerning bean and not of the bean itself. How can I change this? Is there a way to tell Java that the generic type T is of the same type as the class passed to the methods.
import java.util.Properties;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.naming.*;
public class EJBFactory
{
private InitialContext ctx;
public EJBFactory() throws NamingException {
ctx = new InitialContext();
}
public EJBFactory(String host, String port) throws NamingException {
Properties props = new Properties();
props.setProperty("org.omg.CORBA.ORBInitialHost", host);
props.setProperty("org.omg.CORBA.ORBInitialPort", port);
ctx = new InitialContext(props);
}
.
// To improve: The object returned should be of the type ejbClass
// instead of the remote interface, which it implements
public <T> T createEJB(Class ejbClass) throws NamingException
{
Class remoteInterface = null;
for(Class interface_: ejbClass.getInterfaces()) {
if(interface_.isAnnotationPresent(Remote.class))
remoteInterface = interface_;
}
if(remoteInterface == null)
throw new IllegalArgumentException(
"EJB Requires a remote interface");
// Get the stateless annotation, then get the jndiName
Stateless stateless =
(Stateless)ejbClass.getAnnotation(Stateless.class);
String jndiName = stateless.mappedName();
T ejbObj = (T) ctx.lookup(jndiName);
return ejbObj;
}
}
Example of a unit test which uses the Factory.
import junit.framework.TestCase;
public class SimpleEJBTest extends TestCase
{
TestRemote testBean;
#Override
protected void setUp() throws Exception {
super.setUp();
EJBFactory ejbFactory = new EJBFactory();
testBean = ejbFactory.createEJB(TestBean.class);
}
public void testSayHello() {
assertEquals("Hello", testBean.sayHello());
}
}
Note: The example works with Glassfish, I didn't test it with any other app server.
Clients of EJBs interact with them through the local/ remote interface that the EJBs implement. Client applications never have direct access to an actual session bean class instance. This is done to make instance pooling possible, where the container can reuse EJB instances to service different requests.
I'm not sure why you need to access the actual bean's object (since obviously I dont know your requirement). But if you still need to create an instance of that you can do it as follows using reflection Class.forName(className).newInstance(); Again the instance that you create like this is not an EJB. It is just a POJO thats all.
EDIT - after your comment regarding junit testing: When you access business methods from a JavaSE as follows, you are actually calling the methods in the EJB - just that you interact thru the interface. So if you want to test any business methods you can still do it from an object got thru a JNDI lookup in a Junit test.
//MyGreatBean implements MyGreat. MyGreat has #Remote, MyGreatBean has #Stateless
ref = jndiContext.lookup("MyGreatBean/remote");
MyGreat bean = (MyGreat) ref;
String retValue = bean.businessMethod();
assertEquals("Success", retValue);
From an earlier comment, I get a feeling you want to check what kind of annotations have been added to the actual EJB class - if you want to do that kind of checking without actually running the business methods, you can create an instance using Class.forName... like I mentioned above. When you create an instance like this you can only call methods that don't do any "Java EE" stuff. For example you can call a method in the EJB class that is as follows
public String someMethod(){
return "I am a POJO but I look like an EJB";
}
I do not think that you can get the EJB object. You can only get the interface. The createEJB should be called with the interface and it returns the interface.
try replacing
public <T> T createEJB(Class ejbClass) throws NamingException
with
public <T> T createEJB(Class<T> ejbClass) throws NamingException
Can you try this?
Create a interface. Make it have #Remote. Your ejb that is annotated with #Stateless should implement the above created interface. Now try to do the same thing that you are doing I think it should give you the desired result. Typing it down here without copying from an ide so excuse any errors. But you should get the drift I guess.
#Remote
public interface Example{
String some();
}
#stateless
public class ExampleBean implements Example{
}