users@jersey.java.net

Re: [Jersey] Issue with SpringServlet

From: Peter Liu <Peter.Liu_at_Sun.COM>
Date: Thu, 10 Jul 2008 12:49:01 -0700

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
>