Repository: cxf Updated Branches: refs/heads/master 9c920cd52 -> b0e7be04d
CXF-5576: Initital support for CDI integration. Added more test cases and support for application singletons Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/b0e7be04 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/b0e7be04 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/b0e7be04 Branch: refs/heads/master Commit: b0e7be04d0d6ba8061831fcc26448e8e5c1c8a2e Parents: 9c920cd Author: reta <[email protected]> Authored: Sat Mar 29 12:46:09 2014 -0400 Committer: reta <[email protected]> Committed: Sat Mar 29 12:46:09 2014 -0400 ---------------------------------------------------------------------- .../cxf/cdi/JAXRSCdiResourceExtension.java | 171 ++++++++++++++----- .../org/apache/cxf/systest/jaxrs/BookStore.java | 16 +- .../cxf/systest/jaxrs/BookStoreApplication.java | 4 + .../jaxrs/BookStoreCustomApplication.java | 45 +++++ .../cxf/systest/jaxrs/cdi/AbstractCDITest.java | 24 ++- 5 files changed, 219 insertions(+), 41 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/b0e7be04/integration/cdi/src/main/java/org/apache/cxf/cdi/JAXRSCdiResourceExtension.java ---------------------------------------------------------------------- diff --git a/integration/cdi/src/main/java/org/apache/cxf/cdi/JAXRSCdiResourceExtension.java b/integration/cdi/src/main/java/org/apache/cxf/cdi/JAXRSCdiResourceExtension.java index ffe5421..2218281 100644 --- a/integration/cdi/src/main/java/org/apache/cxf/cdi/JAXRSCdiResourceExtension.java +++ b/integration/cdi/src/main/java/org/apache/cxf/cdi/JAXRSCdiResourceExtension.java @@ -19,7 +19,10 @@ package org.apache.cxf.cdi; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.ServiceLoader; import javax.enterprise.event.Observes; @@ -40,11 +43,13 @@ import javax.ws.rs.ext.Provider; import org.apache.cxf.Bus; import org.apache.cxf.bus.extension.ExtensionManagerBus; +import org.apache.cxf.feature.Feature; +import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; import org.apache.cxf.jaxrs.utils.ResourceUtils; /** - * Apache CXF portable CDI extension to support initialization of JAX-RS / JAX-WS resources. + * Apache CXF portable CDI extension to support initialization of JAX-RS resources. */ public class JAXRSCdiResourceExtension implements Extension { private Bean< ? > busBean; @@ -54,9 +59,6 @@ public class JAXRSCdiResourceExtension implements Extension { private final List< Bean< ? > > serviceBeans = new ArrayList< Bean< ? > >(); private final List< Bean< ? > > providerBeans = new ArrayList< Bean< ? > >(); - private final List< Object > services = new ArrayList< Object >(); - private final List< Object > providers = new ArrayList< Object >(); - public <T> void collect(@Observes final ProcessBean< T > event) { if (event.getAnnotated().isAnnotationPresent(ApplicationPath.class)) { applicationBeans.add(event.getBean()); @@ -71,26 +73,6 @@ public class JAXRSCdiResourceExtension implements Extension { } public void load(@Observes final AfterDeploymentValidation event, final BeanManager beanManager) { - for (final Bean< ? > bean: serviceBeans) { - services.add( - beanManager.getReference( - bean, - bean.getBeanClass(), - beanManager.createCreationalContext(bean) - ) - ); - } - - for (final Bean< ? > bean: providerBeans) { - providers.add( - beanManager.getReference( - bean, - bean.getBeanClass(), - beanManager.createCreationalContext(bean) - ) - ); - } - bus = (Bus)beanManager.getReference( busBean, busBean.getBeanClass(), @@ -104,13 +86,22 @@ public class JAXRSCdiResourceExtension implements Extension { beanManager.createCreationalContext(application) ); - // Create the JAXRSServerFactoryBean for each application we have discovered - final JAXRSServerFactoryBean factory = createFactoryInstance(instance); - factory.init(); + // If there is an application without any singletons and classes defined, we will + // create a server factory bean with all services and providers discovered. + if (instance.getSingletons().isEmpty() && instance.getClasses().isEmpty()) { + final JAXRSServerFactoryBean factory = createFactoryInstance(instance, + loadServices(beanManager), loadProviders(beanManager)); + factory.init(); + } else { + // If there is an application with any singletons or classes defined, we will + // create a server factory bean with only application singletons and classes. + final JAXRSServerFactoryBean factory = createFactoryInstance(instance); + factory.init(); + } } } - - public void injectFactories(@Observes final AfterBeanDiscovery event, final BeanManager beanManager) { + + public void injectBus(@Observes final AfterBeanDiscovery event, final BeanManager beanManager) { if (busBean == null) { final AnnotatedType< ExtensionManagerBus > busAnnotatedType = beanManager.createAnnotatedType(ExtensionManagerBus.class); @@ -123,28 +114,130 @@ public class JAXRSCdiResourceExtension implements Extension { } } - @SuppressWarnings("rawtypes") - private JAXRSServerFactoryBean createFactoryInstance(final Application application) { - - JAXRSServerFactoryBean instance = ResourceUtils.createApplication(application, false, false); + /** + * Create the JAXRSServerFactoryBean from the application and all discovered service and provider instances. + * @param application application instance + * @param services all discovered services + * @param providers all discovered providers + * @return JAXRSServerFactoryBean instance + */ + private JAXRSServerFactoryBean createFactoryInstance(final Application application, final List< ? > services, + final List< ? > providers) { + + final JAXRSServerFactoryBean instance = ResourceUtils.createApplication(application, false, false); instance.setServiceBeans(new ArrayList< Object >(services)); instance.setProviders(providers); + instance.setProviders(loadExternalProviders()); + instance.setBus(bus); + + return instance; + } + + /** + * Create the JAXRSServerFactoryBean from the objects declared by application itself. + * @param application application instance + * @return JAXRSServerFactoryBean instance + */ + private JAXRSServerFactoryBean createFactoryInstance(final Application application) { + + final JAXRSServerFactoryBean instance = ResourceUtils.createApplication(application, false, false); + final Map< Class< ? >, List< Object > > classified = classifySingletons(application.getSingletons()); + instance.setServiceBeans(classified.get(Path.class)); + instance.setProviders(classified.get(Provider.class)); + instance.setFeatures(CastUtils.cast(classified.get(Feature.class), Feature.class)); instance.setBus(bus); - + + return instance; + } + + /** + * JAX-RS application has defined singletons as being instances of any providers, resources and features. + * In the JAXRSServerFactoryBean, those should be split around several method calls depending on instance + * type. At the moment, only the Feature is CXF-specific and should be replaced by JAX-RS Feature implementation. + * @param singletons application singletons + * @return classified singletons by instance types + */ + private Map< Class< ? >, List< Object > > classifySingletons(final Collection< Object > singletons) { + final Map< Class< ? >, List< Object > > classified = + new HashMap< Class< ? >, List< Object > >(); + + classified.put(Feature.class, new ArrayList< Object >()); + classified.put(Provider.class, new ArrayList< Object >()); + classified.put(Path.class, new ArrayList< Object >()); + + for (final Object singleton: singletons) { + if (singleton instanceof Feature) { + classified.get(Feature.class).add(singleton); + } else if (singleton.getClass().isAnnotationPresent(Provider.class)) { + classified.get(Provider.class).add(singleton); + } else if (singleton.getClass().isAnnotationPresent(Path.class)) { + classified.get(Path.class).add(singleton); + } + } + + return classified; + } + + /** + * Load external providers from service loader + * @return loaded external providers + */ + @SuppressWarnings("rawtypes") + private List< Object > loadExternalProviders() { + final List< Object > providers = new ArrayList< Object >(); + final ServiceLoader< MessageBodyWriter > writers = ServiceLoader.load(MessageBodyWriter.class); for (final MessageBodyWriter< ? > writer: writers) { - instance.setProvider(writer); + providers.add(writer); } final ServiceLoader< MessageBodyReader > readers = ServiceLoader.load(MessageBodyReader.class); for (final MessageBodyReader< ? > reader: readers) { - instance.setProvider(reader); + providers.add(reader); } - return instance; + return providers; } - public Bus getBus() { - return bus; + /** + * Gets the references for all discovered JAX-RS resources + * @param beanManager bean manager instance + * @return the references for all discovered JAX-RS resources + */ + private List< Object > loadProviders(final BeanManager beanManager) { + final List< Object > providers = new ArrayList< Object >(); + + for (final Bean< ? > bean: providerBeans) { + providers.add( + beanManager.getReference( + bean, + bean.getBeanClass(), + beanManager.createCreationalContext(bean) + ) + ); + } + + return providers; + } + + /** + * Gets the references for all discovered JAX-RS providers + * @param beanManager bean manager instance + * @return the references for all discovered JAX-RS providers + */ + private List< Object > loadServices(final BeanManager beanManager) { + final List< Object > services = new ArrayList< Object >(); + + for (final Bean< ? > bean: serviceBeans) { + services.add( + beanManager.getReference( + bean, + bean.getBeanClass(), + beanManager.createCreationalContext(bean) + ) + ); + } + + return services; } } http://git-wip-us.apache.org/repos/asf/cxf/blob/b0e7be04/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java ---------------------------------------------------------------------- diff --git a/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java b/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java index c6182f1..755dbff 100644 --- a/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java +++ b/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java @@ -21,6 +21,7 @@ package org.apache.cxf.systest.jaxrs; import java.util.Collection; import javax.inject.Inject; +import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.ws.rs.FormParam; @@ -28,30 +29,43 @@ import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; @Path("/bookstore/") public class BookStore { @Inject private BookStoreService service; + @Inject private String version; @GET + @Path("/version") + @Produces(MediaType.TEXT_PLAIN) + public String getVersion() { + return version; + } + + @GET @Path("/books/{bookId}") @NotNull + @Produces(MediaType.APPLICATION_JSON) public Book getBook(@PathParam("bookId") String id) { return service.get(id); } @GET @Path("/books") - @NotNull + @NotNull @Valid + @Produces(MediaType.APPLICATION_JSON) public Collection< Book > getBooks() { return service.all(); } @POST @Path("/books") + @Produces(MediaType.APPLICATION_JSON) public Response addBook(@Context final UriInfo uriInfo, @NotNull @Size(min = 1, max = 50) @FormParam("id") String id, @NotNull @FormParam("name") String name) { http://git-wip-us.apache.org/repos/asf/cxf/blob/b0e7be04/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreApplication.java ---------------------------------------------------------------------- diff --git a/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreApplication.java b/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreApplication.java index bf3fdcc..a3e7f84 100644 --- a/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreApplication.java +++ b/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreApplication.java @@ -18,9 +18,13 @@ */ package org.apache.cxf.systest.jaxrs; +import javax.enterprise.inject.Produces; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("/api") public class BookStoreApplication extends Application { + @Produces public String version() { + return "1.0"; + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/b0e7be04/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreCustomApplication.java ---------------------------------------------------------------------- diff --git a/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreCustomApplication.java b/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreCustomApplication.java new file mode 100644 index 0000000..631470d --- /dev/null +++ b/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreCustomApplication.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.systest.jaxrs; + +import java.util.Set; + +import javax.inject.Inject; +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.google.common.collect.Sets; + +import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationFeature; +import org.apache.cxf.jaxrs.validation.ValidationExceptionMapper; + +@ApplicationPath("/custom") +public class BookStoreCustomApplication extends Application { + @Inject private BookStore bookStore; + + @Override + public Set< Object > getSingletons() { + return Sets.< Object >newHashSet( + bookStore, + new JacksonJsonProvider(), + new ValidationExceptionMapper(), + new JAXRSBeanValidationFeature()); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/b0e7be04/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/cdi/AbstractCDITest.java ---------------------------------------------------------------------- diff --git a/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/cdi/AbstractCDITest.java b/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/cdi/AbstractCDITest.java index 0627d0a..e58924a 100644 --- a/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/cdi/AbstractCDITest.java +++ b/systests/cdi/src/test/java/org/apache/cxf/systest/jaxrs/cdi/AbstractCDITest.java @@ -37,6 +37,13 @@ import org.junit.Test; public abstract class AbstractCDITest extends AbstractBusClientServerTestBase { @Test + public void testInjectedVersionIsProperlyReturned() { + Response r = createWebClient("/rest/api/bookstore/version", MediaType.TEXT_PLAIN).get(); + assertEquals(Status.OK.getStatusCode(), r.getStatus()); + assertEquals("1.0", r.readEntity(String.class)); + } + + @Test public void testResponseHasBeenReceivedWhenAddingNewBook() { Response r = createWebClient("/rest/api/bookstore/books").post( new Form() @@ -68,12 +75,27 @@ public abstract class AbstractCDITest extends AbstractBusClientServerTestBase { assertEquals(id, book.getId()); } + @Test + public void testAddOneBookWithValidation() { + final String id = UUID.randomUUID().toString(); + + Response r = createWebClient("/rest/custom/bookstore/books").post( + new Form() + .param("id", id)); + assertEquals(Status.BAD_REQUEST.getStatusCode(), r.getStatus()); + } + + protected WebClient createWebClient(final String url) { + return createWebClient(url, MediaType.APPLICATION_JSON); + } + + protected WebClient createWebClient(final String url, final String mediaType) { final List< ? > providers = Arrays.asList(new JacksonJsonProvider()); final WebClient wc = WebClient .create("http://localhost:" + getPort() + url, providers) - .accept(MediaType.APPLICATION_JSON); + .accept(mediaType); WebClient.getConfig(wc).getHttpConduit().getClient().setReceiveTimeout(10000000L); return wc;
