users@jersey.java.net

Re: [Jersey] Issue with SpringServlet

From: Peter Liu <Peter.Liu_at_Sun.COM>
Date: Thu, 10 Jul 2008 11:42:38 -0700

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
>>