users@jersey.java.net

Re: [Jersey] Issue with SpringServlet

From: Peter Liu <Peter.Liu_at_Sun.COM>
Date: Wed, 09 Jul 2008 13:59:21 -0700

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