users@jersey.java.net

AW: [Jersey] ContainerRequestFilter as Spring component?

From: Nico Rehwaldt <nico.rehwaldt_at_web.de>
Date: Thu, 14 Oct 2010 15:10:15 +0200

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:Paul.Sandoz_at_oracle.com]
Gesendet: Donnerstag, 14. Oktober 2010 11:40
An: users_at_jersey.dev.java.net
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#aop-auto
> proxy-force-CGLIB
>
> -----Ursprüngliche Nachricht-----
> Von: Paul Sandoz [mailto:Paul.Sandoz_at_oracle.com]
> Gesendet: Mittwoch, 13. Oktober 2010 15:19
> An: users_at_jersey.dev.java.net
> 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:Paul.Sandoz_at_oracle.com]
>> Gesendet: Donnerstag, 23. September 2010 19:23
>> An: users_at_jersey.dev.java.net
>> 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:Paul.Sandoz_at_ORACLE.COM]
>>> Gesendet: Donnerstag, 23. September 2010 00:37
>>> An: users_at_jersey.dev.java.net
>>> 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: users-unsubscribe_at_jersey.dev.java.net
>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>> <jersey-containerrequestfilter-
>> testcase
>> .zip
>>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
For additional commands, e-mail: users-help_at_jersey.dev.java.net