Quantcast

AW: [Jersey] ContainerRequestFilter as Spring component?

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

AW: [Jersey] ContainerRequestFilter as Spring component?

Nico Rehwaldt-2
I did some research and came up with that:
When the jersey app starts and the spring context is loaded, evaluating

        BeanFactoryUtils.beansOfTypeIncludingAncestors(springContext,
test.MyFilter.class);

yields an empty LinkedHashMap whereas

       BeanFactoryUtils.beansOfTypeIncludingAncestors(springContext,
com.sun.jersey.spi.container.ContainerRequestFilter.class);

yields a LinkedHashMap with our test.MyFilter instance inside.

That’s why the
         Final String names[] =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(springContext,
test.MyFilter.class); fails. If it would be a call to
         BeanFactoryUtils.beanNamesForTypeIncludingAncestors(springContext,
com.sun.jersey.spi.container.ContainerRequestFilter.class);
It would succeed.

Seems as if you have to call the
BeanFactoryUtils#beanNamesForTypeIncludingAncestors for the class itself and
all interfaces implemented by it to get this working.

A patch would be replacing the line

        final String names[] =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(springContext, c);

in SpringComponentProviderFactory#getBeanName with

        List<String> beanNames = new ArrayList();
 
beanNames.addAll(Arrays.asList(BeanFactoryUtils.beanNamesForTypeIncludingAnc
estors(springContext, c)));

        for (Class i: c.getInterfaces()) {
 
beanNames.addAll(Arrays.asList(BeanFactoryUtils.beanNamesForTypeIncludingAnc
estors(springContext, i)));
        }

        String[] names = beanNames.toArray(new String[0]);

Hope this helps.

Nico

-----Ursprüngliche Nachricht-----
Von: Paul Sandoz [mailto:[hidden email]]
Gesendet: Mittwoch, 20. Oktober 2010 17:19
An: [hidden email]
Betreff: Re: AW: [Jersey] ContainerRequestFilter as Spring component?

Hi Nico,

I can reproduce now.

The problem is the call to:

         final String names[] =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(springContext, c);

It is return a zero length array when java.lang.reflect.Proxy is used.  
Any ideas?

Paul.

On Oct 14, 2010, at 3:10 PM, Nico Rehwaldt wrote:

> I attached a new test case. Don’t know, what happened to the last one
> but the point cut was incorrect.
> This one should work. I am using Glassfish to deploy it but Tomcat
> will do it as well.
>
> Nico
>
> -----Ursprüngliche Nachricht-----
> Von: Paul Sandoz [mailto:[hidden email]]
> Gesendet: Donnerstag, 14. Oktober 2010 11:40
> An: [hidden email]
> Betreff: Re: AW: AW: AW: [Jersey] ContainerRequestFilter as Spring
> component?
>
>
> On Oct 13, 2010, at 11:54 PM, Nico Rehwaldt wrote:
>
>> Hey Paul,
>>
>> Part 6.6 "Proxying mechanisms" in the spring reference documentation
>> ([1])
>> states
>> "Spring AOP uses either JDK dynamic proxies or CGLIB to create the
>> proxy for a given target object. (JDK dynamic proxies are preferred
>> whenever you have a choice)."
>>
>> "Having a choice" could mean that spring uses the proxy if the
>> interface mirrows the api of a class (which is the case for MyFilter
>> in my test setup).
>>
>
> Thanks, understand better now.
>
>
>> Jersey itself (as I understood it) uses the Spring component provider
>> factory to get an instance of the filter class from spring which
>> returns the java.lang.Proxy. However it cannot be handled by the
>> SpringComponentProviderFactory (which returns null then) and the
>> Filter will be instantiated for a second time (without injection
>> taking place).
>>
>
> Jersey will call the following to get an instance of the filter,
> passing in the (unproxied) Class of the filter:
>
>     public IoCComponentProvider getComponentProvider(ComponentContext
> cc, Class c) {
>         final Autowire autowire = (Autowire)
> c.getAnnotation(Autowire.class);
>         if (autowire != null) {
>             if (LOGGER.isLoggable(Level.FINEST)) {
>                 LOGGER.finest("Creating resource class " +
>                         c.getSimpleName() +
>                         " annotated with @" +
>                         Autowire.class.getSimpleName() +
>                         " as spring bean.");
>             }
>             return new SpringInstantiatedComponentProvider(c,
> autowire);
>         }
>
>         final String beanName = getBeanName(cc, c, springContext);
>         if (beanName == null) {
>             return null;
>         }
>
>         final String scope = findBeanDefinition(beanName).getScope();
>         return new
> SpringManagedComponentProvider(getComponentScope(scope), beanName, c);
>     }
>
>
>> That is the only reasonable answer for me at the moment.
>> Could you reproduce my findings with the supplied test case?
>>
>
> No. It worked for <aop:aspectj-autoproxy /> and <aop:aspectj-autoproxy
> proxy-target-class="true" />.
>
> I don't think the proxying is occurring. The following is not printed
> out in the logs:
>
>   Filter executed!
>
> by the MyAspect. Is there something config missing from your attached
> app?
>
> I am using Tomcat 6.0 to deploy. What are you using?
>
> Paul.
>
>> Nico
>>
>> [1]:
>>
> http://static.springsource.org/spring/docs/2.5.x/reference/aop.html#ao
> p-auto
>> proxy-force-CGLIB
>>
>> -----Ursprüngliche Nachricht-----
>> Von: Paul Sandoz [mailto:[hidden email]]
>> Gesendet: Mittwoch, 13. Oktober 2010 15:19
>> An: [hidden email]
>> Betreff: Re: AW: AW: [Jersey] ContainerRequestFilter as Spring
>> component?
>>
>> Hi Nico,
>>
>> I cannot reproduce. The sample you attach works for me  if i:
>>
>> 1) <aop:aspectj-autoproxy/> and/or
>>
>> 2) comment out the cglib dependency from the pom.
>>
>> I don't understand why a call to the Jersey Spring
>> SpringComponentProviderFactory.getComponentProvider would pass in a
>> class that is of java.lang.Proxy. Since the resource filter class
>> name is registered is in the web.xml that is the class name that will
>> be used to load the class and that class will be passed to the
>> pringComponentProviderFactory.getComponentProvider method. Only the
>> instance looked up should be of a proxyied class.
>>
>> java.lang.Proxy only works for interfaces, so presumably it is
>> applied to interfaces that are implemented by MyFilter?
>>
>> Paul.
>>
>> On Sep 24, 2010, at 10:58 AM, Nico Rehwaldt wrote:
>>
>>> Hey Paul,
>>>
>>> I confused the filter registration with the registration of resource
>>> classes, of course I register them programmatically (via
>>> com.sun.jersey.spi.container.ContainerRequestFilters init parameter
>>> in web.xml).
>>>
>>> You are right, the filter does not have to be registered as a
>>> resource class to make it work as a spring bean. The only thing
>>> which has to be changed is, that IF spring creates a proxy for a
>>> registered bean, in our case the filter, THEN it must be a CGLib
>>> proxy.
>>>
>>> In my case this happens, because I annotated the filter method with
>>> the @Transactional annotation to safely perform some database access
>>> on it.
>>> Thus, spring will create a proxy around it.
>>>
>>> I attached a test case for the problem. It contains a filter
>>> (test.MyFilter), a resource class which is injected into the filter
>>> (test.MyResource) and an Aspect which contains a pointcut on the
>>> filter method. The least is needed to force spring to create a proxy
>>> of the filter instance.
>>>
>>> If you jump into the spring configuration in src/main/resources/
>>> spring.xml you can enable usage of CGLib proxies (instead of java
>>> ones) for <aop:aspect-autoproxy /> (see [1]). With CGLib proxies
>>> used the filter works as expected, without it fails with a null
>>> pointer exception.
>>>
>>> The problem originates in the fact, that com .sun .jersey
>>> .spi.spring.container.SpringComponentProviderFactory#getBeanNa
>>> me returns null for instances of java.lang.reflect.Proxy and that
>>> therefore no IoCComponentProvider is returned for the proxy
>>> instance.
>>>
>>> Hope this helps!
>>>
>>> Nico
>>>
>>> [1]:
>>> http://static.springsource.org/spring/docs/2.5.x/reference/aop.html
>>>
>>> -----Ursprüngliche Nachricht-----
>>> Von: Paul Sandoz [mailto:[hidden email]]
>>> Gesendet: Donnerstag, 23. September 2010 19:23
>>> An: [hidden email]
>>> Betreff: Re: AW: [Jersey] ContainerRequestFilter as Spring
>>> component?
>>>
>>>
>>> On Sep 22, 2010, at 4:46 PM, Nico Rehwaldt wrote:
>>>
>>>> Hey Paul,
>>>>
>>>> the filter is recognized via package scanning (not
>>>> programmatically).
>>>
>>> Filters need to be explicitly registered, as described here:
>>>
>>>
>>> https://jersey.dev.java.net/nonav/apidocs/latest/jersey/com/sun/
>>> jersey
>>> /api/c
>>> ontainer/filter/package-summary.html
>>>
>>> because ordering of filters is important. It should not be necessary
>>> for the filter to be registered in the set of classes of the
>>> resource config. So i am not sure what is going on exactly.
>>>
>>> If it is possible to wrap something up in a simple reproducible test
>>> case that would help a lot.
>>>
>>>
>>>
>>>> As the filter is annotated (and instantiated) as a spring bean, I
>>>> wondered, why it does not have any fields injected.
>>>>
>>>> However, I did some research and found out, what the problem is:  
>>>> The
>>>> filter gets instantiated as a spring bean and has all fields
>>>> injected.
>>>> When Jersey wants to "use it" it will try to obtain it using the
>>>> SpringComponentProviderFactory which is registered in the
>>>> SpringServlet.
>>>> This fails and Jersey will instantiate the class (again), but on
>>>> its own and with no injection taking place. I found two reasons for
>>>> this behavior.
>>>>
>>>> * At first, the filter class is not registered as a ResourceClass
>>>> in the Jersey app (I think this should happen in
>>>> SpringComponentProviderFactory#register but it only registers
>>>> provider and root resource classes). I changed the method to
>>>> accomplish this:
>>>>
>>>> private void register(ResourceConfig rc,
>>>> ConfigurableApplicationContext
>>>> springContext) {
>>>>     String[] names =
>>>> BeanFactoryUtils.beanNamesIncludingAncestors(springContext);
>>>>
>>>>     for (String name : names) {
>>>>         Class<?> type =
>>>> ClassUtils.getUserClass(springContext.getType(name));
>>>>         if (ResourceConfig.isProviderClass(type)) {
>>>>             LOGGER.info("Registering Spring bean, " + name +
>>>>                     ", of type " + type.getName() +
>>>>                     " as a provider class");
>>>>             rc.getClasses().add(type);
>>>>         } else if (ResourceConfig.isRootResourceClass(type)) {
>>>>             LOGGER.info("Registering Spring bean, " + name +
>>>>                     ", of type " + type.getName() +
>>>>                     " as a root resource class");
>>>>             rc.getClasses().add(type);
>>>>         } else if
>>>> (ContainerRequestFilter.class.isAssignableFrom(type))
>>>> {
>>>>             LOGGER.info("Registering Spring bean, " + name +
>>>>                     ", of type " + type.getName() +
>>>>                     " as a filter class");
>>>>             rc.getClasses().add(type);
>>>>         }
>>>>     }
>>>> }
>>>>
>>>
>>> Instead of modifying the above annotate your filter with @Provider.
>>>
>>> Paul.
>>>
>>>> * As a second problem, the filter was an instance of
>>>> java.lang.reflect.Proxy (the class name was something like $Proxy..
>>>> $).
>>>> As I read somewhere it is due to the fact, that spring uses the
>>>> java proxies in favor of the CGLib ones, if the class has an
>>>> interface present (and the filter has). A java.lang.reflect.Proxy
>>>> isn't recognized as a bean in SpringComponentProviderFactory
>>>> #getComponentProvider(ComponentContext cc, Class c), as its bean
>>>> name will be null. Therefore null instead of an injection provider
>>>> is returned and jersey creates the filter on its own (again).
>>>>
>>>> Everything works fine now after I forced spring to use CGLib
>>>> proxies and made the above changes to the
>>>> SpringComponentProviderFactory.
>>>>
>>>> Nico.
>>>>
>>>> -----Ursprüngliche Nachricht-----
>>>> Von: Paul Sandoz [mailto:[hidden email]]
>>>> Gesendet: Donnerstag, 23. September 2010 00:37
>>>> An: [hidden email]
>>>> Betreff: Re: [Jersey] ContainerRequestFilter as Spring component?
>>>>
>>>> Hi Nico,
>>>>
>>>> How are you registering the SecurityFilter with Jersey? are you
>>>> instantiating it yourself and programatically registering it?
>>>>
>>>> Paul.
>>>>
>>>> On Sep 20, 2010, at 1:16 AM, Nico Rehwaldt wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> I am using Jersey 1.4 ea together with Spring 3.0 and the jersey-
>>>>> spring integration. Integrating Jersey and Spring works fine for
>>>>> resource classes. However I created an instance of
>>>>> ContainerRequestFilter to do some pre-processing of requests. The
>>>>> filter should be a spring component and should handle setting up
>>>>> the security context.
>>>>>
>>>>> Basically I want to have access to the database (the ORM-Layer)
>>>>> and I tried to accomplish this in two ways:
>>>>>
>>>>> @Component
>>>>> public class SecurityFilter implements ContainerRequestFilter {
>>>>>
>>>>> // UserManager is a declared spring component // Injecting it
>>>>> should work somehow @Autowired private UserManager userManager;
>>>>>
>>>>> // Entity Manager would help, too
>>>>> @PersistenceContext
>>>>> private EntityManager em;
>>>>>
>>>>> @Override
>>>>> public ContainerRequest filter(ContainerRequest request) {
>>>>>     System.out.println(userManager);
>>>>>     // prints out null on request
>>>>> }
>>>>> }
>>>>>
>>>>> I registered the Filter as a Spring compontent, but it does not
>>>>> seem as if it is recognized as such.
>>>>> Both injection of EntityManager and UserManager (a Spring bean as
>>>>> well) do not work.
>>>>>
>>>>> Any suggestions what I might be doing wrong?
>>>>>
>>>>> Greetings
>>>>> Nico
>>>>
>>>>
>>>> -------------------------------------------------------------------
>>>> -- To unsubscribe, e-mail: [hidden email]
>>>> For additional commands, e-mail: [hidden email]
>>>>
>>>>
>>>> -------------------------------------------------------------------
>>>> -- To unsubscribe, e-mail: [hidden email]
>>>> For additional commands, e-mail: [hidden email]
>>>>
>>>
>>>
>>> --------------------------------------------------------------------
>>> - To unsubscribe, e-mail: [hidden email]
>>> For additional commands, e-mail: [hidden email]
>>> <jersey-containerrequestfilter-
>>> testcase
>>> .zip
>>>> -------------------------------------------------------------------
>>>> --
>>> To unsubscribe, e-mail: [hidden email]
>>> For additional commands, e-mail: [hidden email]
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: [hidden email]
>> For additional commands, e-mail: [hidden email]
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: [hidden email]
>> For additional commands, e-mail: [hidden email]
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
> <jersey-containerrequestfilter-
> testcase
> .zip
> >---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Loading...