I am not being able to get JAX-RS working with Resteasy 2.3.5 usingh simple #ApplicationPath annotation. Here is the code I am using:
#ApplicationPath("/rest")
public class MyApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> s = new HashSet<Class<?>>();
s.add(ViewController.class);
return s;
}
}
#Path("/")
public class ViewController {
#GET
#Path("/test")
public String test() {
return "Yes!";
}
}
Requesting on "/uri-context/rest/test/" throws a 404. Using Jersey everything works seamlessly. Since this is a very trivial part of JAX-RS what's going on wrong?
Currently I am using only 4 libs of Resteasy that I would require:
async-http-servlet-3.0-2.3.5.Final.jar
jaxrs-api-2.3.5.Final.jar
resteasy-jaxrs-2.3.5.Final.jar
scannotation-1.0.3.jar
Nevertheless, putting all the libs (except for resteasy-cdi-2.3.5.Final.jar), also does not solve the problem.
Beware with Jax-RS 1.0, the path and the slash
#ApplicationPath("api") //NO slash
public class MyApplication extends Application {
}
#Path("/users") // YES slash
public class ViewController {
#GET
#Path("all") //NO slash
public String all() {
return "Yes!";
}
}
Usually taking care of slashes makes it quickly better.
UPDATE for JAX-RS 2 :
In JAX-RS 2, the spec DOES says that leading and trailing slashes in #ApplicationPath or #Path will be ignored.
(3)If the resource class URI template does not end with a ‘/’ character
then one is added during the concatenation.
For what I have tested with Jersey 2 and Resteasy, this is now respected.
Related
After upgrading Quarkus from 1.6.1.Final to 2.5.Final the following #Inject fails inside javax.ws.rs.core.Application subclass:
#ApplicationScoped
public class MyBean {
public String foo() {
retun "bar";
}
}
#ApplicationPath("/")
public class MyApplication extends Application {
#Inject
MyBean myBean;
#Override
public Set<Class<?>> getClasses() {
myBean.foo(); // Causes NPE on Quarkus 2.5.Final, worked well with 1.6.1.Final
}
}
I tried with CDI.current().select(MyBean.class).get() but got Unable to locate CDIProvider.
Any other workaround I can try? Thanks.
#Inject in JAX-RS Application classes has been since disallowed. I was able to solve my issue (registering resource classes by config) using #IfBuildProperty annotation.
In RestEasy 3.0.16.Final version PreProcessInterceptor interface is deprecated. So what is the proper replacement of this interface. In jboss eap 7 RestEasy version 3.0.16.Final is used.
Old code -
#Provider
#ServerInterceptor
#SecurityPrecedence
public class AbcInterceptor implements PreProcessInterceptor
{
public ServerResponse preProcess(final HttpRequest httpRequest, ResourceMethod resourceMethod) throws Failure,
WebApplicationException {
// auth logic
}
}
New code -
#Provider
#ServerInterceptor
#SecurityPrecedence
public class AuthenticationInterceptor
{
public ServerResponse preProcess(HttpRequest httpRequest, ResourceMethodInvoker method)
throws Failure, WebApplicationException {
// auth logic
}
}
The org.jboss.resteasy.spi.interception.PreProcessInterceptor interface is replaced by the javax.ws.rs.container.ContainerRequestFilter interface in RESTEasy 3.x.
So, you can can use the ContainerRequestFilter for the same.
I created a project to test the dependency injection offered by Google Guice in my Jax-rs resources, using Resteasy.
My intentions are:
Use multiple #ApplicationPath for the versions of my API. In each class annotated with #ApplicationPath I load a set of classes for the specific version.
Each resource have a #Inject (from Google Guice) in his constructor to inject some services.
I created two classes annotated with #ApplicationPath: ApplicationV1RS and ApplicationV2RS. In both I added the same resources classes (UserResource and HelloResource), only for my test.
My Module is configured like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
When I call http://localhost:9095/v1/hello/world or http://localhost:9095/v2/hello/world, I receive the same error:
java.lang.RuntimeException: RESTEASY003190: Could not find constructor
for class: org.jboss.resteasy.examples.guice.hello.HelloResource
Well, as I expected, this not works. The Google Guice is not "smart" to instantiate the resource classes using the construtor for me.
But I can't find a way to work. To be really honest, I'm really confuse about how the Google Guice, Jetty and Resteasy play with each other in this scenario.
If I abandon the idea of use #ApplicationPath, my resources work with Google Guice configuring my HelloModule like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(HelloResource.class);
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(UserResource.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
But in this case, I'm passing the control to register my resources (HelloResource and UserResource) to Guice. It's not flexible for me, I can't setup my multiple #ApplicationPath.
So, what I'm missing or not understanding?
I created a project with the problemetic code. Is very easy to setup and test: https://github.com/dherik/resteasy-guice-hello/tree/so-question/README.md
Thanks!
When you have getClasses method in your Application then it tries to create instance for all the registered resources using the default constructor which is missing in our Resources class. One way is to create a default constructor and Inject the dependencies through setter Injection.
And then instead of overriding getClasses in ApplicationV1RS and ApplicationV2RS you override getSingletons. Since Resources can be Singleton.
Below are the changes that I made to make it work the way you want.
ApplicationV1RS.java
#ApplicationPath("v1")
public class ApplicationV1RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV1RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
ApplicationV2RS.java
#ApplicationPath("v2")
public class ApplicationV2RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV2RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
HelloResource.java
#Path("hello")
public class HelloResource {
#Inject
private IGreeterService greeter;
public HelloResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return greeter.greet(name);
}
}
UserResource.java
#Path("user")
public class UserResource {
#Inject
private IUserService userService;
public UserResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return userService.getUser(name);
}
}
Add #Singleton to your Service Classes.
Hope it helps.
I have also pushed the code to forked repo. check it out
I have read the documentation concerning the Jersey Test framework and have successfully used JerseyTest's target method to reach a #Path annotated endpoint within my own file. Simplified code is below.
public class TestApplication extends ResourceConfig {
public TestApplication() {
registerClasses(TestService.class);
}
}
#Override
protected Application configure() {
return new TestApplication();
}
#Path("create")
public static class TestService {
#POST
#Path("testObj")
#Consumes(APPLICATION_JSON)
public static Response createTestObj(final TestObj testObj) {
return Response.ok("testObj created").build();
}
}
#Test
private void ensureObjectCreated() {
JSONObject myObj = createNewObj();
final Response response = target("create/testObj").request(APPLICATION_JSON)
.post(Entity.json(myObj.toString()));
Assert.isEqual(response.status, 200);
}
Now I want to reach a #Path annotated endpoint in other files/directories. How do I do so? The problem may be that the other files are actual production code, so I cannot make the classes static. However the endpoints in the other paths are reachable.
Just register them in the resource config, either individually (depending on the scope of the test), or specify a package to scan with the packages method.
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(SomeResource.class)
.packages("your.resource.package.to.scan");
}
The only reason the class in the example is static is because it is an inner class that needs to be instantiated by the framework.
When you access the resource, it will not include the root application path, only the #Path value on the class, and whatever sub path, just like in your code above.
Here is my bean that is trying to inject a singleton bean InformationService :
#Path("/information/{name}")
#Stateless (name="InformationResource")
public class InformationResource {
#EJB
private InformationService appService;
#GET
#Produces(MediaType.APPLICATION_XML)
public Information getInfo(#PathParam("name") String name){
return appService.getMap().get(name);
}
#PUT
#POST
#Consumes(MediaType.APPLICATION_XML)
public Information putInfo(#PathParam("name") String name, Information info){
return appService.getMap().put(name,info);
}
#DELETE
public void deleteInfo(#PathParam("name") String name){
appService.getMap().remove(name);
}
}
This is the InformationService class
#Singleton
public class InformationService {
private Map<String,Information> map;
#PostConstruct
public void init(){
map = new HashMap<String,Information>();
map.put("daud", new Information("B.Tech","Lucknow"));
map.put("anuragh", new Information("M.Sc","Delhi"));
}
public Map<String,Information> getMap(){
return map;
}
}
Its part of a very simple JAX-RS implementation and I am deploying as war in JBoss 6.1 Final. The problem is that InformationService throwing a NullPointerException when I make the proper get request. If I initialize appService explicitly, everything works fine. Why is #EJB annotation not working ?
Are you using Jersey as REST implementation? If so, EJB injection is not supported out of the box.
This link provides more information on this and also a solution.
Check that your #Singleton is javax.ejb.Singleton.
Any other exceptions before NPE ?