users@jersey.java.net

Re: [Jersey] Issue with SpringServlet

From: Peter Liu <Peter.Liu_at_Sun.COM>
Date: Thu, 10 Jul 2008 13:09:06 -0700

Hi Martin,

Please ignore my previous email. I found what I was missing. I need to
add the @Autowire
annotation on my subresources in order for the SpringComponentProvider to
create the Spring bean. After doing that, it works like a charm.

This is great! The code is clean and I don't have to deal with configuring
the ServiceLocatorFactoryBean.

Thanks for all your help.

Peter

Peter Liu wrote:
> Hi Martin,
>
> I just tried the ResourceContext approach as you suggested. However,
> it doesn't
> quite do what I want. The subresource returned from ResourceContext does
> have the UriInfo injected by Jersey but it doesn't seem to be managed
> by Spring.
> For example, it doesn't have the EntityManager and transaction
> injected by
> Spring. Am I missing something or is this not supported by Jersey?
>
> If not supported, it seems to me that there needs to be a Spring
> version of the ResourceContext
> that creates an instance of the Spring bean and does the Jersey
> injection. This will give the best of both world.
>
> Note that this approach is really promising because I no longer have
> to add bean
> definitions to the applicationContext.xml (including using @Autowire
> which I just discovered)
> which solves my initial problem with the bean name lookup.
> Peter
>
> Peter Liu wrote:
>> Martin Grotzke wrote:
>>> Hi Peter,
>>>
>>> I just had a short look at your sources, thanx for sending them. Now I
>>> have a better idea what you want to achieve, but I need some time to
>>> reproduce the issue and write tests for this.
>>>
>>> Just one hint what you might try alternatively...
>>>
>>> As you already mentioned, you're creating new subresources using
>>> Spring's ServiceLocatorFactoryBean.
>>>
>>> As an alternative, you might use jersey's ResourceContext, which you
>>> can
>>> inject with @Context. The ResourceContext was introduced to allow
>>> developers to get an instance of some subresource from the
>>> IoC-container
>>> so that he can use features like e.g. AOP provided by the IoC-container
>>> also for subresources.
>>>
>>> With this, you would not have to set the UriInfo "by hand" on your
>>> subresource, but the subresource could get the UriInfo injected by
>>> jersey when it's fetched from the ResourceContext. This prevents you
>>> from injecting stuff into the root resource that is only required in
>>> subresources...
>>>
>>> I don't know how the ServiceLocatorFactoryBean is working actually, but
>>> perhaps using the ResourceContext changes your situation?
>>>
>> Thanks for letting me know about the ResourceContext. I am going to
>> give it a try and I think
>> it will work. However, the main issue still remains. I still have to
>> define multiple beans for the
>> root class and subclasses and it will cause the getBeanName() to
>> throw an exception.
>>
>> Peter
>>
>>> Cheers,
>>> Martin
>>>
>>>
>>> On Wed, 2008-07-09 at 13:59 -0700, Peter Liu wrote:
>>>> Hi Martin,
>>>>
>>>> Attached is a NB project containing the source. The application
>>>> is a CRUD application that uses Spring,
>>>> Jersey and JPA. Take a look at x.service.DiscountCodeResource which
>>>> has a static inner class CustomersSubresource which extends
>>>> CustomersResource. Both CustomersSubresource
>>>> and CustomersResource have bean definitions in
>>>> applicationContext.xml. Also, take a look a
>>>> DiscountCodeResource.getCustomerCollectionResource() which is a
>>>> subresource locator method that
>>>> returns a prototype instance of CustomresSubresource via Spring's
>>>> ServiceLocatorFactoryBean mechanism.
>>>>
>>>> Peter
>>>>
>>>>
>>>> Martin Grotzke wrote:
>>>>
>>>>> Hi Peter,
>>>>>
>>>>> please can you send a complete example for this, including the
>>>>> resource
>>>>> classes? Perhaps we need to extend existing concepts of jerseys
>>>>> support
>>>>> of IoC...
>>>>>
>>>>> Thanx && cheers,
>>>>> Martin
>>>>>
>>>>>
>>>>> On Tue, 2008-07-08 at 16:32 -0700, Peter Liu wrote:
>>>>>
>>>>>> Martin Grotzke wrote:
>>>>>>
>>>>>>> Hi Peter,
>>>>>>>
>>>>>>> if I understood you correctly, you want to configure multiple
>>>>>>> beans for
>>>>>>> the same class.
>>>>>>>
>>>>>>> For this issue we introduced the "value" attribute of the @Inject
>>>>>>> annotation. With this it's possible to have multiple spring
>>>>>>> beans of the
>>>>>>> same class which can be used like this:
>>>>>>>
>>>>>>>
>>>>>>> @Inject( "bean1" )
>>>>>>> private Bean _bean1;
>>>>>>> @Inject( "bean2" )
>>>>>>> private Bean _bean2;
>>>>>>>
>>>>>>>
>>>>>>> <bean id="bean1" class="some.spring.Bean" />
>>>>>>> <bean id="bean2" class="some.spring.Bean" />
>>>>>>>
>>>>>>>
>>>>>>> Does this solve your issue, or did I get s.th. wrong?
>>>>>>>
>>>>>> Unfortunately, I can't used the @Inject mechanism. What I am
>>>>>> trying to do is to have a root
>>>>>> singleton resource return a different "prototype" instance of a
>>>>>> subresource when calling
>>>>>> the subresource locator method. on the root resource. I am
>>>>>> achieving this by using Spring's ServiceLocatorFactoryBean
>>>>>> mechanism. It just happens that the subresource class can be a
>>>>>> subclass of the root resource
>>>>>> class. For example, my configuration may look as follow:
>>>>>>
>>>>>> <bean id="rootResource" class="x.RootResource"/>
>>>>>> <bean id="subresource" class="x.SubclassOfRootResource"
>>>>>> type="prototype"/>
>>>>>>
>>>>>> This will cause the getBeanName() to throw an exception because
>>>>>> springContext.getBeanNamesForType(x.RootResource)
>>>>>> will return both rootResource and subresource. However, in this
>>>>>> case, it is really the rootResource that
>>>>>> I want.
>>>>>>
>>>>>> Peter
>>>>>>
>>>>>>
>>>>>>
>>>>>>> Cheers,
>>>>>>> Martin
>>>>>>>
>>>>>>>
>>>>>>> On Tue, 2008-07-08 at 11:53 -0700, Peter Liu wrote:
>>>>>>>
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> I have been trying out the spring-jersey integration and ran
>>>>>>>> into an issue where the
>>>>>>>> SpringServlet will not work if I configure multiple beans, one
>>>>>>>> for the root resource class and one for
>>>>>>>> each subclass of the root class. The
>>>>>>>> SpringServlet.getBeanName() will throw an exception
>>>>>>>> since it thinks that there are multiple beans configured for
>>>>>>>> the same class.
>>>>>>>>
>>>>>>>> I made some changes to SpringServlet.getBeanName() (see
>>>>>>>> attached file).
>>>>>>>> In the case where there are multiple bean names returned for a
>>>>>>>> given class,
>>>>>>>> before throwing an error, I look for the bean instance that has
>>>>>>>> the exact
>>>>>>>> same class as the one being looked up. With this change, I am
>>>>>>>> able to
>>>>>>>> make application work.
>>>>>>>>
>>>>>>>> I would like to submit this change to jersey-spring. Please let
>>>>>>>> me know if the change
>>>>>>>> is acceptable or there is a better way to solve this problem.
>>>>>>>>
>>>>>>>>
>>>>>>>> Thanks.
>>>>>>>>
>>>>>>>> Peter
>>>>>>>> plain text document attachment (SpringServlet.java)
>>>>>>>> /*
>>>>>>>> * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
>>>>>>>> * * Copyright 2007 Sun Microsystems, Inc. All rights
>>>>>>>> reserved. * * The contents of this file are subject to the
>>>>>>>> terms of the Common Development
>>>>>>>> * and Distribution License("CDDL") (the "License"). You may
>>>>>>>> not use this file
>>>>>>>> * except in compliance with the License. * * You can obtain
>>>>>>>> a copy of the License at:
>>>>>>>> * https://jersey.dev.java.net/license.txt
>>>>>>>> * See the License for the specific language governing
>>>>>>>> permissions and
>>>>>>>> * limitations under the License.
>>>>>>>> *
>>>>>>>> * When distributing the Covered Code, include this CDDL Header
>>>>>>>> Notice in each
>>>>>>>> * file and include the License file at:
>>>>>>>> * https://jersey.dev.java.net/license.txt
>>>>>>>> * If applicable, add the following below this CDDL Header,
>>>>>>>> with the fields
>>>>>>>> * enclosed by brackets [] replaced by your own identifying
>>>>>>>> information:
>>>>>>>> * "Portions Copyrighted [year] [name of copyright owner]"
>>>>>>>> */
>>>>>>>> package x.service;
>>>>>>>>
>>>>>>>> import com.sun.jersey.spi.service.ComponentContext;
>>>>>>>> import java.io.IOException;
>>>>>>>> import java.lang.annotation.Annotation;
>>>>>>>> import java.lang.reflect.Constructor;
>>>>>>>> import java.lang.reflect.InvocationTargetException;
>>>>>>>> import java.util.Arrays;
>>>>>>>> import java.util.Collection;
>>>>>>>> import java.util.Iterator;
>>>>>>>>
>>>>>>>> import javax.servlet.ServletException;
>>>>>>>> import javax.servlet.ServletRequest;
>>>>>>>> import javax.servlet.ServletResponse;
>>>>>>>>
>>>>>>>> import org.apache.commons.logging.Log;
>>>>>>>> import org.apache.commons.logging.LogFactory;
>>>>>>>> import org.springframework.aop.framework.Advised;
>>>>>>>> import org.springframework.aop.support.AopUtils;
>>>>>>>> import org.springframework.context.ApplicationContext;
>>>>>>>> import org.springframework.context.ConfigurableApplicationContext;
>>>>>>>> import org.springframework.web.context.WebApplicationContext;
>>>>>>>> import
>>>>>>>> org.springframework.web.context.support.WebApplicationContextUtils;
>>>>>>>>
>>>>>>>>
>>>>>>>> import com.sun.jersey.api.core.ResourceConfig;
>>>>>>>> import com.sun.jersey.spi.container.WebApplication;
>>>>>>>> import com.sun.jersey.spi.container.servlet.ServletContainer;
>>>>>>>> import com.sun.jersey.spi.inject.Inject;
>>>>>>>> import com.sun.jersey.spi.service.ComponentProvider;
>>>>>>>> import java.lang.reflect.Proxy;
>>>>>>>>
>>>>>>>> /**
>>>>>>>> * TODO: DESCRIBE ME<br>
>>>>>>>> * Created on: Apr 3, 2008<br>
>>>>>>>> * * @author <a
>>>>>>>> href="mailto:martin.grotzke_at_freiheit.com">Martin Grotzke</a>
>>>>>>>> * @version $Id$
>>>>>>>> */
>>>>>>>> public class SpringServlet extends ServletContainer {
>>>>>>>>
>>>>>>>> private static final long serialVersionUID =
>>>>>>>> 5686655395749077671L;
>>>>>>>> private static final Log LOG =
>>>>>>>> LogFactory.getLog(SpringServlet.class);
>>>>>>>>
>>>>>>>> public static class SpringComponentProvider implements
>>>>>>>> ComponentProvider {
>>>>>>>>
>>>>>>>> /* (non-Javadoc)
>>>>>>>> * @see
>>>>>>>> com.sun.ws.rest.spi.service.ComponentProvider#getInjectableInstance(java.lang.Object)
>>>>>>>>
>>>>>>>> */
>>>>>>>> public <T> T getInjectableInstance(T instance) {
>>>>>>>> if (AopUtils.isAopProxy(instance)) {
>>>>>>>> final Advised aopResource = (Advised) instance;
>>>>>>>> try {
>>>>>>>> @SuppressWarnings("unchecked")
>>>>>>>> final T result = (T)
>>>>>>>> aopResource.getTargetSource().getTarget();
>>>>>>>>
>>>>>>>> return result;
>>>>>>>> } catch (Exception e) {
>>>>>>>> LOG.fatal("Could not get target object from
>>>>>>>> proxy.", e);
>>>>>>>> throw new RuntimeException("Could not get
>>>>>>>> target object from proxy.", e);
>>>>>>>> }
>>>>>>>> } else {
>>>>>>>> return instance;
>>>>>>>> }
>>>>>>>> }
>>>>>>>> private ConfigurableApplicationContext springContext;
>>>>>>>>
>>>>>>>> public
>>>>>>>> SpringComponentProvider(ConfigurableApplicationContext
>>>>>>>> springContext) {
>>>>>>>> this.springContext = springContext;
>>>>>>>> }
>>>>>>>>
>>>>>>>> public <T> T getInstance(Scope scope, Class<T> clazz)
>>>>>>>> throws InstantiationException,
>>>>>>>> IllegalAccessException {
>>>>>>>> return getInstance(null, scope, clazz);
>>>>>>>> }
>>>>>>>>
>>>>>>>> public <T> T getInstance(Scope scope, Constructor<T>
>>>>>>>> constructor,
>>>>>>>> Object[] parameters)
>>>>>>>> throws InstantiationException,
>>>>>>>> IllegalArgumentException,
>>>>>>>> IllegalAccessException,
>>>>>>>> InvocationTargetException {
>>>>>>>>
>>>>>>>> return getInstance(null, scope,
>>>>>>>> constructor.getDeclaringClass());
>>>>>>>>
>>>>>>>> }
>>>>>>>>
>>>>>>>> public <T> T getInstance(ComponentContext cc, Scope
>>>>>>>> scope, Class<T> clazz)
>>>>>>>> throws InstantiationException,
>>>>>>>> IllegalAccessException {
>>>>>>>> final Autowire autowire =
>>>>>>>> clazz.getAnnotation(Autowire.class);
>>>>>>>> if (autowire != null) {
>>>>>>>> if (LOG.isDebugEnabled()) {
>>>>>>>> LOG.debug("Creating resource class " +
>>>>>>>> clazz.getSimpleName() + " annotated with @" +
>>>>>>>> Autowire.class.getSimpleName() + " as spring bean.");
>>>>>>>> }
>>>>>>>> /* use createBean to have a fully initialized
>>>>>>>> bean, including
>>>>>>>> * applied BeanPostProcessors (in contrast to
>>>>>>>> #autowire()).
>>>>>>>> */
>>>>>>>> final Object result =
>>>>>>>> springContext.getBeanFactory().createBean(clazz,
>>>>>>>> autowire.mode().getSpringCode(),
>>>>>>>> autowire.dependencyCheck());
>>>>>>>> return clazz.cast(result);
>>>>>>>> }
>>>>>>>>
>>>>>>>> final String beanName = getBeanName(cc, clazz,
>>>>>>>> springContext);
>>>>>>>> if (beanName == null) {
>>>>>>>> return null;
>>>>>>>> }
>>>>>>>>
>>>>>>>> /* if the scope is null, this means that jersey
>>>>>>>> simply doesn't know what's
>>>>>>>> * the scope of this dependency, so it's left to
>>>>>>>> the application...
>>>>>>>> */
>>>>>>>> if (scope == Scope.Undefined || scope ==
>>>>>>>> Scope.Singleton && springContext.isSingleton(beanName) || scope
>>>>>>>> == Scope.PerRequest && springContext.isPrototype(beanName)) {
>>>>>>>> if (LOG.isDebugEnabled()) {
>>>>>>>> LOG.debug("Retrieving bean '" + beanName +
>>>>>>>> "' for resource class " + clazz.getSimpleName() + " from
>>>>>>>> spring.");
>>>>>>>> }
>>>>>>>> Object result = springContext.getBean(beanName);
>>>>>>>>
>>>>>>>> if (result instanceof Proxy && result
>>>>>>>> instanceof Advised) {
>>>>>>>> try {
>>>>>>>> result = ((Advised)
>>>>>>>> result).getTargetSource().getTarget();
>>>>>>>> } catch (Exception ex) {
>>>>>>>> return null;
>>>>>>>> }
>>>>>>>> }
>>>>>>>> return clazz.cast(result);
>>>>>>>> } else {
>>>>>>>> return null;
>>>>>>>> }
>>>>>>>>
>>>>>>>> }
>>>>>>>>
>>>>>>>> public void inject(Object instance) {
>>>>>>>> }
>>>>>>>> };
>>>>>>>>
>>>>>>>> @Override
>>>>>>>> protected void initiate(ResourceConfig rc, WebApplication
>>>>>>>> wa) {
>>>>>>>> try {
>>>>>>>> final WebApplicationContext springContext =
>>>>>>>> WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
>>>>>>>>
>>>>>>>>
>>>>>>>> wa.initiate(rc, new
>>>>>>>> SpringComponentProvider((ConfigurableApplicationContext)
>>>>>>>> springContext));
>>>>>>>> } catch (RuntimeException e) {
>>>>>>>> LOG.error("Got exception while trying to
>>>>>>>> initialize", e);
>>>>>>>> throw e;
>>>>>>>> }
>>>>>>>> }
>>>>>>>>
>>>>>>>> /* (non-Javadoc)
>>>>>>>> * @see
>>>>>>>> javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest,
>>>>>>>> javax.servlet.ServletResponse)
>>>>>>>> */
>>>>>>>> @Override
>>>>>>>> public void service(ServletRequest req, ServletResponse
>>>>>>>> res) throws ServletException, IOException {
>>>>>>>> LOG.debug("Starting...");
>>>>>>>> try {
>>>>>>>> super.service(req, res);
>>>>>>>> } catch (RuntimeException e) {
>>>>>>>> LOG.error("Caught exception.", e);
>>>>>>>> }
>>>>>>>> LOG.debug("Finished.");
>>>>>>>> }
>>>>>>>>
>>>>>>>> private static String getBeanName(ComponentContext cc,
>>>>>>>> Class<?> c, ApplicationContext springContext) {
>>>>>>>> System.out.println("getBeanName() c = " + c + " cc = "
>>>>>>>> + cc);
>>>>>>>>
>>>>>>>> boolean annotatedWithInject = false;
>>>>>>>> if (cc != null) {
>>>>>>>> final Inject inject =
>>>>>>>> getAnnotation(cc.getAnnotations(), Inject.class);
>>>>>>>> if (inject != null) {
>>>>>>>> annotatedWithInject = true;
>>>>>>>>
>>>>>>>> if (inject.value() != null &&
>>>>>>>> !inject.value().equals("")) {
>>>>>>>> System.out.println("inject.value = " +
>>>>>>>> inject.value());
>>>>>>>> return inject.value();
>>>>>>>> }
>>>>>>>>
>>>>>>>> }
>>>>>>>> }
>>>>>>>>
>>>>>>>> final String names[] =
>>>>>>>> springContext.getBeanNamesForType(c);
>>>>>>>>
>>>>>>>> if (names.length == 0) {
>>>>>>>> return null;
>>>>>>>> } else if (names.length == 1) {
>>>>>>>> return names[0];
>>>>>>>> } else {
>>>>>>>> for (int i = 0; i < names.length; i++) {
>>>>>>>> String name = names[i];
>>>>>>>> Object bean = springContext.getBean(name);
>>>>>>>> Class clazz = null;
>>>>>>>>
>>>>>>>> if (bean instanceof Advised) {
>>>>>>>> clazz = ((Advised)
>>>>>>>> bean).getTargetSource().getTargetClass();
>>>>>>>> } else {
>>>>>>>> clazz = bean.getClass();
>>>>>>>> }
>>>>>>>>
>>>>>>>> if (clazz == c) {
>>>>>>>> return name;
>>>>>>>> }
>>>>>>>> }
>>>>>>>> final StringBuilder sb = new StringBuilder();
>>>>>>>> sb.append("There are multiple beans configured in
>>>>>>>> spring for the type ").append(c.getName()).append(".");
>>>>>>>>
>>>>>>>>
>>>>>>>> if (annotatedWithInject) {
>>>>>>>> sb.append("\nYou should specify the name of the
>>>>>>>> preferred bean at @Inject: Inject(\"yourBean\").");
>>>>>>>> } else {
>>>>>>>> sb.append("\nAnnotation information was not
>>>>>>>> available, the reason might be because you're not using " +
>>>>>>>> "@Inject. You should use @Inject and
>>>>>>>> specifiy the bean name via Inject(\"yourBean\").");
>>>>>>>> }
>>>>>>>>
>>>>>>>> sb.append("\nAvailable bean names:
>>>>>>>> ").append(toCSV(names));
>>>>>>>>
>>>>>>>> throw new RuntimeException(sb.toString());
>>>>>>>> }
>>>>>>>> }
>>>>>>>>
>>>>>>>> private static <T extends Annotation> T
>>>>>>>> getAnnotation(Annotation[] annotations,
>>>>>>>> Class<T> clazz) {
>>>>>>>> if (annotations != null) {
>>>>>>>> for (Annotation annotation : annotations) {
>>>>>>>> if (annotation.annotationType().equals(clazz)) {
>>>>>>>> return clazz.cast(annotation);
>>>>>>>> }
>>>>>>>> }
>>>>>>>> }
>>>>>>>> return null;
>>>>>>>> }
>>>>>>>>
>>>>>>>> static <T> String toCSV(T[] items) {
>>>>>>>> if (items == null) {
>>>>>>>> return null;
>>>>>>>> }
>>>>>>>> return toCSV(Arrays.asList(items));
>>>>>>>> }
>>>>>>>>
>>>>>>>> static <I> String toCSV(Collection<I> items) {
>>>>>>>> return toCSV(items, ", ", null);
>>>>>>>> }
>>>>>>>>
>>>>>>>> static <I> String toCSV(Collection<I> items, String
>>>>>>>> separator, String delimiter) {
>>>>>>>> if (items == null) {
>>>>>>>> return null;
>>>>>>>> }
>>>>>>>> if (items.isEmpty()) {
>>>>>>>> return "";
>>>>>>>> }
>>>>>>>> final StringBuilder sb = new StringBuilder();
>>>>>>>> for (final Iterator<I> iter = items.iterator();
>>>>>>>> iter.hasNext();) {
>>>>>>>> if (delimiter != null) {
>>>>>>>> sb.append(delimiter);
>>>>>>>> }
>>>>>>>> final I item = iter.next();
>>>>>>>> sb.append(item);
>>>>>>>> if (delimiter != null) {
>>>>>>>> sb.append(delimiter);
>>>>>>>> }
>>>>>>>> if (iter.hasNext()) {
>>>>>>>> sb.append(separator);
>>>>>>>> }
>>>>>>>> }
>>>>>>>> return sb.toString();
>>>>>>>> }
>>>>>>>> }
>>>>>>>>
>>>>>>>> ---------------------------------------------------------------------
>>>>>>>>
>>>>>>>> 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
>>>>>>
>>>>>
>>>> fd
>>>> ---------------------------------------------------------------------
>>>> 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
>