Index: contribs/spring/src/test/java/com/sun/jersey/spring25/SpringScopedResource.java =================================================================== --- contribs/spring/src/test/java/com/sun/jersey/spring25/SpringScopedResource.java (revision 0) +++ contribs/spring/src/test/java/com/sun/jersey/spring25/SpringScopedResource.java (revision 0) @@ -0,0 +1,55 @@ +/* + * 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 com.sun.jersey.spring25; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import com.sun.jersey.spi.resource.Singleton; + +/** + * A resource class that is only annotated with spring's {@link Scope} annotation + * and no jersey lifecycle annotation. + * + * @author Martin Grotzke + * @version $Id$ + */ + @Path( "SpringScopedResource" ) + @Component + @Scope( "singleton" ) +public class SpringScopedResource { + + public SpringScopedResource() { + } + + @GET + @Path( "hello" ) + @Produces( "text/plain" ) + public String sayHello() { + return "hello"; + } + +} Property changes on: contribs/spring/src/test/java/com/sun/jersey/spring25/SpringScopedResource.java ___________________________________________________________________ Name: svn:keywords + Id Index: contribs/spring/src/test/java/com/sun/jersey/spring25/SpringScopedResourceTest.java =================================================================== --- contribs/spring/src/test/java/com/sun/jersey/spring25/SpringScopedResourceTest.java (revision 0) +++ contribs/spring/src/test/java/com/sun/jersey/spring25/SpringScopedResourceTest.java (revision 0) @@ -0,0 +1,55 @@ +/* + * 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 com.sun.jersey.spring25; + +import org.springframework.context.annotation.Scope; +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.spring.AbstractResourceTest; + +/** + * Test support for resource classes that are only annotated with a spring + * {@link Scope} and no jersey lifecycle annotation. + * + * @author Martin Grotzke + * @version $Id$ + */ +@Test +public class SpringScopedResourceTest extends AbstractResourceTest { + + private String _resourcePath; + + public SpringScopedResourceTest() { + _resourcePath = "SpringScopedResource"; + } + + @Test + public void testSayHello() { + final WebResource itemResource = resource( _resourcePath + "/hello" ); + final String hello = itemResource.get( String.class ); + Assert.assertNotNull( hello ); + + } + +} Property changes on: contribs/spring/src/test/java/com/sun/jersey/spring25/SpringScopedResourceTest.java ___________________________________________________________________ Name: svn:keywords + Id Index: contribs/spring/src/main/java/com/sun/jersey/spi/spring/container/servlet/SpringResourceProvider.java =================================================================== --- contribs/spring/src/main/java/com/sun/jersey/spi/spring/container/servlet/SpringResourceProvider.java (revision 0) +++ contribs/spring/src/main/java/com/sun/jersey/spi/spring/container/servlet/SpringResourceProvider.java (revision 0) @@ -0,0 +1,187 @@ +/* + * 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 com.sun.jersey.spi.spring.container.servlet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import com.sun.jersey.api.core.HttpContext; +import com.sun.jersey.api.core.ResourceConfig; +import com.sun.jersey.api.model.AbstractResource; +import com.sun.jersey.impl.container.servlet.PerSessionProvider; +import com.sun.jersey.impl.resource.PerRequestProvider; +import com.sun.jersey.impl.resource.SingletonProvider; +import com.sun.jersey.spi.resource.ResourceProvider; +import com.sun.jersey.spi.service.ComponentProvider; + +/** + *

+ * This {@link ResourceProvider} is configured as the DefaultResourceProviderClass + * (see {@link ResourceConfig#PROPERTY_DEFAULT_RESOURCE_PROVIDER_CLASS}) by the + * {@link SpringServlet} if there's not already another DefaultResourceProviderClass is + * configured and if spring 2.5+ is used (determined by the availability of + * the {@link Component} annotation). + *

+ *

+ * For resource classes annotated with {@link Component}, this {@link ResourceProvider} + * uses the value of the {@link Scope} annotation (provided by spring >= 2.5) on the + * resource class. According to the value of the {@link Scope} annotation it creates + * an appropriate {@link ResourceProvider} implementation and delegates all further work to this. + *

+ *

+ * These spring scopes are supported: {@link SupportedSpringScopes}. + *

+ *

+ * This {@link ResourceProvider} depends on spring 2.5 + *

+ * + * @author Martin Grotzke + * @version $Id$ + */ +public class SpringResourceProvider implements ResourceProvider { + + private static final Log LOG = LogFactory.getLog( SpringResourceProvider.class ); + + /** + * This enum defines the mapping of spring scopes to the jersey lifecycle/scopes + */ + public enum SupportedSpringScopes { + + /** + * Maps the spring scope "singleton" to the {@link SingletonProvider} + */ + SINGLETON ( "singleton", SingletonProvider.class ), + /** + * Maps the spring scope "prototype" to the {@link PerRequestProvider} + */ + PROTOTYPE( "prototype", PerRequestProvider.class ), + /** + * Maps the spring scope "request" to the {@link PerRequestProvider} + */ + REQUEST( "request", PerRequestProvider.class ), + /** + * Maps the spring scope "session" to the {@link PerSessionProvider} + */ + SESSION( "session", PerSessionProvider.class ); + + private final String _springScope; + private final Class _resourceProviderClass; + + private SupportedSpringScopes( String springScope, + Class resourceProviderClass ) { + _springScope = springScope; + _resourceProviderClass = resourceProviderClass; + } + + public Class createResourceProviderClass() { + return _resourceProviderClass; + } + + public String getSpringScope() { + return _springScope; + } + + /** + * Selects the matching SupportedSpringScopes item or null. + * @param springScope the spring scope + * @return the matching SupportedSpringScopes item or null. + */ + public static SupportedSpringScopes valueOfSpringScope( String springScope ) { + for( SupportedSpringScopes scope : values() ) { + if ( scope.getSpringScope().equals( springScope ) ) { + return scope; + } + } + return null; + } + + public static String getSpringScopesAsCSV() { + final StringBuilder sb = new StringBuilder(); + for ( int i = 0; i < values().length; i++ ) { + final SupportedSpringScopes item = values()[i]; + sb.append( item.getSpringScope() ); + if ( i < values().length - 1 ) { + sb.append( ", " ); + } + } + return sb.toString(); + } + + } + + private ResourceProvider _resourceProvider; + + public void init( ComponentProvider provider, ComponentProvider resourceProvider, AbstractResource resource ) { + final Class resourceClass = resource.getResourceClass(); + final Class resourceProviderClass; + /* This resource provider is only responsible for spring beans + * annotated with @Component, for all other resource classes + * the default resource provider is used (see ResourceProviderFactory#createProvider) + */ + if ( resourceClass.getAnnotation( Component.class ) == null ) { + resourceProviderClass = PerRequestProvider.class; + } + else { + /* This resource provider is only used if the resource class + * does not have any jersey lifecycle annotation + * -> we do not have to check this + * -> if no supported spring scope is used we cannot handle this + */ + final Scope scope = resourceClass.getAnnotation( Scope.class ); + if ( scope != null ) { + final SupportedSpringScopes springScope = SupportedSpringScopes.valueOfSpringScope( scope.value() ); + if ( springScope != null ) { + resourceProviderClass = springScope.createResourceProviderClass(); + } + else { + throw new RuntimeException( "No jersey lifecycle annotation specified on" + + " resource class " + resourceClass.getName() + + " and also no valid spring scope (valid scopes: "+ SupportedSpringScopes.getSpringScopesAsCSV() +")" ); + } + } + else { + throw new RuntimeException( "No jersey lifecycle annotation specified on" + + " resource class " + resourceClass.getName() + + " and also no spring scope." ); + } + } + try { + _resourceProvider = (ResourceProvider)provider.getInstance( + ComponentProvider.Scope.PerRequest, resourceProviderClass ); + _resourceProvider.init( provider, resourceProvider, resource ); + } catch( RuntimeException e ) { + LOG.error( "Could not initialize resource provider for resource class " + resourceClass.getName() ); + throw e; + } catch ( Exception e ) { + LOG.error( "Could not initialize resource provider for resource class " + resourceClass.getName() ); + e.printStackTrace(); + throw new RuntimeException( "Could not initialize resource provider for resource class ", e ); + } + } + + public Object getInstance( ComponentProvider provider, HttpContext context ) { + return _resourceProvider.getInstance( provider, context ); + } + +} Index: contribs/spring/src/main/java/com/sun/jersey/spi/spring/container/servlet/SpringServlet.java =================================================================== --- contribs/spring/src/main/java/com/sun/jersey/spi/spring/container/servlet/SpringServlet.java (revision 1359) +++ contribs/spring/src/main/java/com/sun/jersey/spi/spring/container/servlet/SpringServlet.java (working copy) @@ -161,6 +161,11 @@ final WebApplicationContext springContext = WebApplicationContextUtils. getRequiredWebApplicationContext(getServletContext()); + if ( rc.getProperty( ResourceConfig.PROPERTY_DEFAULT_RESOURCE_PROVIDER_CLASS ) == null + && springComponentAnnotationAvailable() ) { + rc.getProperties().put( ResourceConfig.PROPERTY_DEFAULT_RESOURCE_PROVIDER_CLASS, SpringResourceProvider.class ); + } + wa.initiate(rc, new SpringComponentProvider((ConfigurableApplicationContext) springContext)); } catch( RuntimeException e ) { LOG.error( "Got exception while trying to initialize", e ); @@ -168,6 +173,17 @@ } } + private boolean springComponentAnnotationAvailable() { + try { + Class.forName( "org.springframework.stereotype.Component" ); + LOG.info( "The spring Component annotation is present, we're using spring >= 2.5" ); + return true; + } catch ( ClassNotFoundException e ) { + LOG.info( "The spring Component annotation is not present, we're using spring < 2.5" ); + return false; + } + } + private static String getBeanName( ComponentContext cc, Class c, ApplicationContext springContext ) { boolean annotatedWithInject = false; Index: jersey-server/src/main/java/com/sun/jersey/impl/container/servlet/PerSessionProvider.java =================================================================== --- jersey-server/src/main/java/com/sun/jersey/impl/container/servlet/PerSessionProvider.java (revision 1359) +++ jersey-server/src/main/java/com/sun/jersey/impl/container/servlet/PerSessionProvider.java (working copy) @@ -70,7 +70,7 @@ public void init(ComponentProvider provider, - AbstractResource abstractResource) { + ComponentProvider resourceProvider, AbstractResource abstractResource) { this.c = abstractResource.getResourceClass(); this.rci = new ResourceClassInjector(ipc, Scope.PerRequest, Index: jersey-server/src/main/java/com/sun/jersey/impl/resource/PerRequestProvider.java =================================================================== --- jersey-server/src/main/java/com/sun/jersey/impl/resource/PerRequestProvider.java (revision 1359) +++ jersey-server/src/main/java/com/sun/jersey/impl/resource/PerRequestProvider.java (working copy) @@ -69,7 +69,7 @@ private List constructorInjectableParams; public void init(ComponentProvider provider, - AbstractResource abstractResource) { + ComponentProvider resourceProvider, AbstractResource abstractResource) { this.c = abstractResource.getResourceClass(); this.rci = new ResourceClassInjector(ipc, Scope.PerRequest, Index: jersey-server/src/main/java/com/sun/jersey/impl/resource/SingletonProvider.java =================================================================== --- jersey-server/src/main/java/com/sun/jersey/impl/resource/SingletonProvider.java (revision 1359) +++ jersey-server/src/main/java/com/sun/jersey/impl/resource/SingletonProvider.java (working copy) @@ -60,7 +60,7 @@ private Object resource; public void init(ComponentProvider provider, - AbstractResource abstractResource) { + ComponentProvider resourceProvider, AbstractResource abstractResource) { Class c = abstractResource.getResourceClass(); ResourceClassInjector rci = new ResourceClassInjector( @@ -73,7 +73,7 @@ Scope.Singleton); try { if (cip == null || cip.is.size() == 0) { - resource = provider.getInstance(Scope.Singleton, c); + resource = resourceProvider.getInstance(Scope.Singleton, c); } else { Object[] params = new Object[cip.is.size()]; int i = 0; @@ -81,9 +81,9 @@ if (injectable != null) params[i++] = injectable.getValue(null); } - resource = provider.getInstance(Scope.Singleton, cip.con, params); + resource = resourceProvider.getInstance(Scope.Singleton, cip.con, params); } - rci.inject(null, provider.getInjectableInstance(resource)); + rci.inject(null, resourceProvider.getInjectableInstance(resource)); } catch (InvocationTargetException ex) { throw new ContainerException("Unable to create resource", ex); } catch (InstantiationException ex) { Index: jersey-server/src/main/java/com/sun/jersey/spi/resource/ResourceProvider.java =================================================================== --- jersey-server/src/main/java/com/sun/jersey/spi/resource/ResourceProvider.java (revision 1359) +++ jersey-server/src/main/java/com/sun/jersey/spi/resource/ResourceProvider.java (working copy) @@ -57,11 +57,11 @@ /** * Specifies the class of the resource that the provider * instance will manage access to. - * * @param provider the component provider + * @param resourceProvider the component provider for resource classes * @param resource the abstract resource */ - void init(ComponentProvider provider, AbstractResource resource); + void init(ComponentProvider provider, ComponentProvider resourceProvider, AbstractResource resource); /** * Called to obtain an instance of a resource class. Index: jersey-server/src/main/java/com/sun/jersey/spi/resource/ResourceProviderFactory.java =================================================================== --- jersey-server/src/main/java/com/sun/jersey/spi/resource/ResourceProviderFactory.java (revision 1359) +++ jersey-server/src/main/java/com/sun/jersey/spi/resource/ResourceProviderFactory.java (working copy) @@ -129,7 +129,7 @@ try { ResourceProvider r = (ResourceProvider)provider. getInstance(ComponentProvider.Scope.PerRequest, providerClass); - r.init(resourceProvider, resource); + r.init(provider, resourceProvider, resource); return r; } catch (IllegalAccessException ex) { throw new ContainerException("Unable to create resource provider", ex);