Skip to content

Commit 7821fdb

Browse files
committed
[RESTEASY-3256] Add check for known interfaces to ensure they are configured as CDI beans.
Signed-off-by: James R. Perkins <jperkins@redhat.com>
1 parent 795b1c3 commit 7821fdb

File tree

7 files changed

+367
-10
lines changed

7 files changed

+367
-10
lines changed

resteasy-cdi/src/main/java/org/jboss/resteasy/cdi/Utils.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,26 @@
22

33
import java.lang.annotation.Annotation;
44
import java.lang.reflect.Method;
5+
import java.util.Arrays;
6+
import java.util.List;
57

68
import javax.enterprise.inject.spi.AnnotatedType;
79
import javax.enterprise.inject.spi.BeanManager;
810
import javax.ws.rs.HttpMethod;
911
import javax.ws.rs.Path;
12+
import javax.ws.rs.container.ContainerRequestFilter;
13+
import javax.ws.rs.container.ContainerResponseFilter;
14+
import javax.ws.rs.container.DynamicFeature;
1015
import javax.ws.rs.core.Application;
16+
import javax.ws.rs.core.Feature;
17+
import javax.ws.rs.ext.ContextResolver;
18+
import javax.ws.rs.ext.ExceptionMapper;
19+
import javax.ws.rs.ext.MessageBodyReader;
20+
import javax.ws.rs.ext.MessageBodyWriter;
21+
import javax.ws.rs.ext.ParamConverterProvider;
1122
import javax.ws.rs.ext.Provider;
23+
import javax.ws.rs.ext.ReaderInterceptor;
24+
import javax.ws.rs.ext.WriterInterceptor;
1225

1326
/**
1427
* Utility methods for detecting CDI scopes and JAX-RS components.
@@ -18,6 +31,20 @@
1831
*/
1932
public class Utils
2033
{
34+
35+
private static final List<Class<?>> REST_INTERFACES = Arrays.asList(
36+
ContainerRequestFilter.class,
37+
ContainerResponseFilter.class,
38+
ContextResolver.class,
39+
DynamicFeature.class,
40+
ExceptionMapper.class,
41+
Feature.class,
42+
MessageBodyReader.class,
43+
MessageBodyWriter.class,
44+
ParamConverterProvider.class,
45+
ReaderInterceptor.class,
46+
WriterInterceptor.class
47+
);
2148
/**
2249
* Finds out if a given class is decorated with JAX-RS annotations.
2350
* Interfaces of the class are not scanned for JAX-RS annotations.
@@ -68,6 +95,12 @@ public static boolean isJaxrsResource(Class<?> clazz)
6895
{
6996
return true;
7097
}
98+
// Check if this implements any known Jakarta REST interfaces
99+
for (Class<?> intf : REST_INTERFACES) {
100+
if (intf.isAssignableFrom(clazz)) {
101+
return true;
102+
}
103+
}
71104
for (Class<?> intf : clazz.getInterfaces())
72105
{
73106
if (isJaxrsAnnotatedClass(intf))
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.jboss.resteasy.test.cdi.interceptors.resource;
22

3-
import java.util.HashSet;
43
import java.util.Set;
4+
import java.util.HashSet;
55

66
import javax.ws.rs.ApplicationPath;
77
import javax.ws.rs.core.Application;
@@ -10,15 +10,11 @@
1010
@NameBoundProxiesAnnotation
1111
public class NameBoundCDIProxiesApplication extends Application {
1212

13+
@Override
1314
public Set<Class<?>> getClasses() {
14-
Set<Class<?>> set = new HashSet<Class<?>>();
15-
set.add(NameBoundCDIProxiesResource.class);
16-
return set;
17-
}
18-
19-
public Set<Object> getSingletons() {
20-
Set<Object> set = new HashSet<Object>();
21-
set.add(new NameBoundCDIProxiesInterceptor());
22-
return set;
15+
final Set<Class<?>> classes = new HashSet<>();
16+
classes.add(NameBoundCDIProxiesResource.class);
17+
classes.add(NameBoundCDIProxiesInterceptor.class);
18+
return classes;
2319
}
2420
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
*
4+
* Copyright 2022 Red Hat, Inc., and individual contributors
5+
* as indicated by the @author tags.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package org.jboss.resteasy.test.client.cdi;
21+
22+
import java.net.URL;
23+
24+
import javax.enterprise.context.ApplicationScoped;
25+
import javax.json.JsonObject;
26+
import javax.ws.rs.GET;
27+
import javax.ws.rs.Path;
28+
import javax.ws.rs.Produces;
29+
import javax.ws.rs.WebApplicationException;
30+
import javax.ws.rs.client.Client;
31+
import javax.ws.rs.client.ClientBuilder;
32+
import javax.ws.rs.core.MediaType;
33+
import javax.ws.rs.core.Response;
34+
import javax.ws.rs.core.UriInfo;
35+
36+
import org.jboss.arquillian.test.api.ArquillianResource;
37+
import org.jboss.resteasy.test.client.cdi.resources.TestBean;
38+
import org.jboss.resteasy.utils.TestApplication;
39+
import org.jboss.resteasy.utils.TestUtil;
40+
import org.jboss.shrinkwrap.api.ShrinkWrap;
41+
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
42+
import org.jboss.shrinkwrap.api.spec.WebArchive;
43+
import org.junit.Assert;
44+
import org.junit.Test;
45+
46+
/**
47+
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
48+
*/
49+
abstract class AbstractRegistrationTest {
50+
51+
@ArquillianResource
52+
URL url;
53+
54+
/**
55+
* Tests that the returned JSON contains a non-null {@code uriInfo} and {@code testBean} entry. Each entry should
56+
* have the value of {@code /test} which is the value returned from {@link UriInfo#getPath()}.
57+
*
58+
* @throws Exception if an error occurs while testing
59+
*/
60+
@Test
61+
public void injectedBeanAndUriInfo() throws Exception {
62+
final Client client = ClientBuilder.newClient();
63+
try {
64+
final Response response = client.target(TestUtil.generateUri(url, "/test"))
65+
.request()
66+
.get();
67+
Assert.assertTrue("Failed to buffer entity", response.bufferEntity());
68+
Assert.assertEquals("Unexpected response: " + response.getEntity(), Response.Status.OK, response.getStatusInfo());
69+
final JsonObject json = response.readEntity(JsonObject.class);
70+
Assert.assertFalse("Expected \"uriInfo\" to not be null: " + json, json.isNull("uriInfo"));
71+
Assert.assertEquals("/test", json.getString("uriInfo"));
72+
Assert.assertFalse("Expected \"testBean\" to not be null: " + json, json.isNull("testBean"));
73+
Assert.assertEquals("/test", json.getString("testBean"));
74+
} finally {
75+
client.close();
76+
}
77+
}
78+
79+
static WebArchive createDeployment(final Class<? extends AbstractRegistrationTest> type) {
80+
return ShrinkWrap.create(WebArchive.class, type.getSimpleName() + ".war")
81+
.addClasses(
82+
AbstractRegistrationTest.class,
83+
FailingResource.class,
84+
TestApplication.class,
85+
TestBean.class
86+
)
87+
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
88+
}
89+
90+
/**
91+
* A resource which throws a {@link WebApplicationException} if invoked.
92+
*
93+
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
94+
*/
95+
@Path("/test")
96+
@ApplicationScoped
97+
@Produces(MediaType.APPLICATION_JSON)
98+
public static class FailingResource {
99+
100+
/**
101+
* Throws a {@link WebApplicationException}.
102+
*
103+
* @return nothing
104+
*/
105+
@GET
106+
public Response get() {
107+
throw new WebApplicationException("Should not have made it here.");
108+
}
109+
}
110+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
*
4+
* Copyright 2022 Red Hat, Inc., and individual contributors
5+
* as indicated by the @author tags.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package org.jboss.resteasy.test.client.cdi;
21+
22+
import javax.ws.rs.container.DynamicFeature;
23+
import javax.ws.rs.container.ResourceInfo;
24+
import javax.ws.rs.core.FeatureContext;
25+
import javax.ws.rs.ext.Provider;
26+
27+
import org.jboss.arquillian.container.test.api.Deployment;
28+
import org.jboss.arquillian.container.test.api.RunAsClient;
29+
import org.jboss.arquillian.junit.Arquillian;
30+
import org.jboss.resteasy.test.client.cdi.resources.ContextAndInjectionFilter;
31+
import org.jboss.shrinkwrap.api.spec.WebArchive;
32+
import org.junit.runner.RunWith;
33+
34+
/**
35+
* Tests that a {@link DynamicFeature} can inject {@link jakarta.ws.rs.core.UriInfo} via
36+
* {@link jakarta.ws.rs.core.Context @Context} and in a CDI bean. The feature is registered as a provider with
37+
* {@link Provider @Provider}.
38+
*
39+
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
40+
*/
41+
@RunWith(Arquillian.class)
42+
@RunAsClient
43+
public class DynamicFeatureContextInjectionTest extends AbstractRegistrationTest {
44+
45+
@Deployment
46+
public static WebArchive deployment() {
47+
return createDeployment(DynamicFeatureContextInjectionTest.class).addClasses(ContextAndInjectionFilter.class, ContextAndInjectionFilterFeature.class);
48+
}
49+
50+
@Provider
51+
public static class ContextAndInjectionFilterFeature implements DynamicFeature {
52+
53+
@Override
54+
public void configure(final ResourceInfo resourceInfo, final FeatureContext context) {
55+
context.register(ContextAndInjectionFilter.class);
56+
}
57+
}
58+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
*
4+
* Copyright 2022 Red Hat, Inc., and individual contributors
5+
* as indicated by the @author tags.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package org.jboss.resteasy.test.client.cdi;
21+
22+
import javax.ws.rs.core.Feature;
23+
import javax.ws.rs.core.FeatureContext;
24+
import javax.ws.rs.ext.Provider;
25+
26+
import org.jboss.arquillian.container.test.api.Deployment;
27+
import org.jboss.arquillian.container.test.api.RunAsClient;
28+
import org.jboss.arquillian.junit.Arquillian;
29+
import org.jboss.resteasy.test.client.cdi.resources.ContextAndInjectionFilter;
30+
import org.jboss.shrinkwrap.api.spec.WebArchive;
31+
import org.junit.runner.RunWith;
32+
33+
/**
34+
* Tests that a {@link Feature} can inject {@link jakarta.ws.rs.core.UriInfo} via
35+
* {@link jakarta.ws.rs.core.Context @Context} and in a CDI bean. The feature is registered as a provider with
36+
* {@link Provider @Provider}.
37+
*
38+
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
39+
*/
40+
@RunWith(Arquillian.class)
41+
@RunAsClient
42+
public class FeatureContextInjectionTest extends AbstractRegistrationTest {
43+
44+
@Deployment
45+
public static WebArchive deployment() {
46+
return createDeployment(FeatureContextInjectionTest.class)
47+
.addClasses(
48+
ContextAndInjectionFilter.class,
49+
ContextAndInjectionFilterFeature.class
50+
);
51+
}
52+
53+
@Provider
54+
public static class ContextAndInjectionFilterFeature implements Feature {
55+
@Override
56+
public boolean configure(final FeatureContext context) {
57+
context.register(ContextAndInjectionFilter.class);
58+
return true;
59+
}
60+
}
61+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
*
4+
* Copyright 2022 Red Hat, Inc., and individual contributors
5+
* as indicated by the @author tags.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package org.jboss.resteasy.test.client.cdi.resources;
21+
22+
import java.io.IOException;
23+
24+
import javax.enterprise.context.RequestScoped;
25+
import javax.inject.Inject;
26+
import javax.json.Json;
27+
import javax.json.JsonObjectBuilder;
28+
import javax.ws.rs.container.ContainerRequestContext;
29+
import javax.ws.rs.container.ContainerRequestFilter;
30+
import javax.ws.rs.core.Context;
31+
import javax.ws.rs.core.Response;
32+
import javax.ws.rs.core.UriInfo;
33+
34+
/**
35+
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
36+
*/
37+
@RequestScoped
38+
public class ContextAndInjectionFilter implements ContainerRequestFilter {
39+
40+
@Context
41+
private UriInfo uriInfo;
42+
43+
@Inject
44+
private TestBean testBean;
45+
46+
@Override
47+
public void filter(final ContainerRequestContext requestContext) throws IOException {
48+
final JsonObjectBuilder builder = Json.createObjectBuilder();
49+
if (uriInfo == null) {
50+
builder.addNull("uriInfo");
51+
} else {
52+
builder.add("uriInfo", uriInfo.getPath());
53+
}
54+
if (testBean == null) {
55+
builder.addNull("testBean");
56+
} else {
57+
builder.add("testBean", testBean.getPath());
58+
}
59+
requestContext.abortWith(Response.ok(builder.build()).build());
60+
}
61+
}

0 commit comments

Comments
 (0)