users@jersey.java.net

Re: [Jersey] Issue with SpringServlet

From: Martin Grotzke <martin.grotzke_at_freiheit.com>
Date: Thu, 10 Jul 2008 09:08:08 +0200

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?

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