Jersey doesn't find Resources : 404 error - java

I am working on a simple RESTful Api using Jersey 2.10 and running on Tomcat 8.
this is my Application class :
package com.manager.api.application;
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;
#ApplicationPath ("api")
public class Application extends ResourceConfig {
public Application () {
packages ("com.manager.api.resources");
}
}
My Resources package contains :
an interface : Resources.java
package com.manager.api.resources;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
public interface Resources {
#GET
#Produces (MediaType.APPLICATION_JSON)
public JsonObject getList ();
#GET
#Path ("value=/{id}")
#Produces (MediaType.APPLICATION_JSON)
public JsonObject get (String id);
#POST
#Path ("value=/{data}")
#Consumes (MediaType.APPLICATION_JSON)
public void post (JsonObject data);
#PUT
#Path ("value=/{data}")
#Consumes (MediaType.APPLICATION_JSON)
public void put (JsonObject data);
#DELETE
#Path ("value=/{id}")
public void delete (String id);
}
An abstract class : ResourcesImpl.java which implements the Resources interface
package com.manager.api.resources;
public abstract class ResourcesImpl implements Resources {
}
And finally a resource class which extends ResourcesImpl.java : UserResources.java
package com.manager.api.resources;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.manager.api.dao.UserDao;
#Path ("value=/users")
public class UserResources extends ResourcesImpl {
private UserDao user = new UserDao ();
#GET
#Path ("value=/test")
#Produces (MediaType.TEXT_PLAIN)
public String Test () {
return "Test";
}
#Override
public JsonObject getList () {
return user.getList ();
}
#Override
public JsonObject get (String id) {
return user.get (id);
}
#Override
public void post (JsonObject data) {
user.post (data);
}
#Override
public void put (JsonObject data) {
user.put (data);
}
#Override
public void delete(String id) {
user.delete (id);
}
}
and my web.xml contains only a <display-name> Tag :
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>manager-api</display-name>
</web-app>
I run my project successfully but I get 2 problems :
The following warnings have been detected: WARNING: A HTTP GET method, public JsonObject com.manager.api.resources.UserResources.get(java.lang.String), should not consume any entity. : Which I find strange since I put #Produces (MediaType.APPLICATION_JSON) and didn't put a #Consumes for this method.
Finally the big problem is the 404 error I get when I type : http://localhost:8080/manager-api/api/users/test when it should print Test as text.
Do you have any idea about what is the reason behind those 2 errors ? Thank you.

Make sure your pom.xml has servlet 3.0 and not 2.0. Also try giving an absolute path instead of a relative one in your #ApplicationPath annotation (such as "/api"). Also maybe your package declaration is incorrect for the resourceconfig?
Refer to this if you have more troubles:
How to set up JAX-RS Application using annotations only (no web.xml)?

Related

Registering a custom ValueParamProvider in Jersey 2.27

I realize that these are internal APIs, but if they're available internally why not make them usable by the less privileged masses, and they're also extremely useful. Even though these APIs were internal in Jersey 2.25 they could be used, and I'd like to upgrade my Jersey version without breaking my custom Jersey extensions.
It's certainly possible to extend ValueParamProvider in Jersey 2.27, but I no longer see a way to register that Provider along with it's triggering annotation. Looking at how Jersey does this for its own implementations, it now uses a BoostrapConfigurator, which seems to be internalized to such an extent that external implementations can't use the same methodology.
Maybe I'm wrong about that, and if someone has a clear description of how, that would be great. Otherwise, does anyone know of a method for doing the same thing?
This used to work...
ResourceConfig resourcceConfig = ...
resourceConfig.register(new AbstractBinder() {
#Override
protected void configure (){
bind(MyParamValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(MyParamInjectionResolver.class).to(new TypeLiteral<InjectionResolver<EntityParam>>() {
}).in(Singleton.class);
}
}
});
With appropriate implementations of AbstractValueFactoryProvider and ParamInjectionResolver.
Now it looks like you need to implement ValueParamProvider, which is easy enough, but I'm not sure how to register that properly with the Jersey framework anymore. Any help appreciated.
You don't need to use any BootstrapConfigurator. All you need to is add the services to the injector and they will be added later to the list of value providers.
To configure it, you can still use the AbstractBinder, but instead of the HK2 one, use the Jersey one. The ValueParamProvider can still be bound the same way, but for the InjectionResolver, you should make sure to implement not the HK2 resolver, but the Jersey one. Then instead of binding to TypeLiteral, bind to GenericType.
I just want to add that a misconception that people have when trying to implement parameter injection is that we also need an InjectResolver to use a custom annotation for the method parameter. This is not the case. The method parameter annotation is just a marker annotation that we should check inside ValueParamProvider#getValueProvider() method. An InjectResolver is only needed for non-method-parameter injections, for instance field and constructor injection. If you don't need that, then you don't need the InjectionResolver.
Below is a complete example using Jersey Test Framework. I didn't use an InjectionResolver, just to show that it's not needed.
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Parameter;
import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.core.Response;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
public class ParamInjectTest extends JerseyTest {
#Target({ElementType.PARAMETER, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Auth {
}
private static class User {
private String username;
public User(String username) {
this.username = username;
}
public String getUsername() {
return this.username;
}
}
public static class AuthValueParamProvider implements ValueParamProvider {
#Override
public Function<ContainerRequest, ?> getValueProvider(Parameter parameter) {
if (parameter.getRawType().equals(User.class)
&& parameter.isAnnotationPresent(Auth.class)) {
return new UserParamProvider();
}
return null;
}
private class UserParamProvider implements Function<ContainerRequest, User> {
#Override
public User apply(ContainerRequest containerRequest) {
return new User("Peeskillet");
}
}
#Override
public PriorityType getPriority() {
return Priority.HIGH;
}
}
public static class AuthFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new AbstractBinder() {
#Override
protected void configure() {
bind(AuthValueParamProvider.class)
.to(ValueParamProvider.class)
.in(Singleton.class);
}
});
return true;
}
}
#Path("test")
#Consumes("text/plain")
public static class TestResource {
#POST
#Produces("text/plain")
public Response post(String text, #Auth User user) {
return Response.ok(user.getUsername() + ":" + text).build();
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(TestResource.class)
.register(AuthFeature.class);
}
#Test
public void testIt() {
final Response response = target("test")
.request()
.post(Entity.text("Test"));
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(String.class)).isEqualTo("Peeskillet:Test");
}
}
Another thing I'll mention is that in previous versions where you extended AbstractValueFactoryProvider and implemented a ParamInjectionResolver, most people did this to follow how Jersey implemented parameter injection while still allowing for other injection points (field and constructor). If you still want to use this pattern, you can.
Below is the AuthFeature from the above test refactored
public static class AuthFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
InjectionManager im = InjectionManagerProvider.getInjectionManager(context);
AuthValueParamProvider authProvider = new AuthValueParamProvider();
im.register(Bindings.service(authProvider).to(ValueParamProvider.class));
Provider<ContainerRequest> request = () -> {
RequestProcessingContextReference reference = im.getInstance(RequestProcessingContextReference.class);
return reference.get().request();
};
im.register(Bindings.injectionResolver(new ParamInjectionResolver<>(authProvider, Auth.class, request)));
return true;
}
}
I figured this stuff out just digging through the source. All this configuration I saw in the ValueParamProviderConfigurator. You don't need to implement your own ParamInjectionResolver. Jersey has a concrete class already that we can just use, as done in the feature above.
If you change the TestResource to inject by field, it should work now
#Path("test")
#Consumes("text/plain")
public static class TestResource {
#Auth User user;
#POST
#Produces("text/plain")
public Response post(String text) {
return Response.ok(user.getUsername() + ":" + text).build();
}
}

Implement generic abstract jaxrs service

I'm struggling on implementing a generic abstract jaxrs service without duplicating several jaxrs annotations.
So, for example, here is my service and entity structure:
AbstractEntity.java
import javax.xml.bind.annotation.XmlElement;
public abstract class AbstractEntity {
#XmlElement
private String name;
public String getName() {
return name;
}
}
AbstractService.java
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
public class AbstractService<T extends AbstractEntity> {
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.TEXT_PLAIN)
#Path("/details")
public Response getEntityDetails(final T entity) {
// just return the name of this entity
return Response.ok(entity.getName()).build();
}
}
The implementation is like:
Car.java
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Car extends AbstractEntity {
#XmlElement
private String brand;
public String getBrand() {
return brand;
}
}
CarService.java
import javax.ws.rs.Path;
#Path("/cars")
public class CarService extends AbstractService<Car> {
// should provide the super getEntityDetails method with a Car entity
}
Now i want to POST my car entity to /cars/details to get the details (return the name "A5" which is implemented in the abstract service):
POST /cars/details
<car>
<brand>Audi</brand>
<name>A5</name>
</car>
Unfortunately, when I post it to my service, it says:
JAXRSUtils W .No message body reader found for request class : AbstractEntity, ContentType : application/xml.
WebApplicatio W WebApplicationException has been caught : no cause is available
I can correct it, if I implement my CarService as follows:
CarService.java
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#Path("/cars")
public class CarService extends AbstractService<Car> {
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.TEXT_PLAIN)
#Path("/details")
#Override
public Response getEntityDetails(final Car entity) {
return super.getEntityDetails(entity);
}
}
And removing all jaxrs annotations from the abstract service:
AbstractService.java
import javax.ws.rs.core.Response;
public class AbstractService<T extends AbstractEntity> {
public Response getEntityDetails(final T entity) {
// just return the name of this entity
return Response.ok(entity.getName()).build();
}
}
The point is, that I have about 60 of these CarService implementations and I don't want to repeat the getDetails method with all it's jaxrs annotation in each service, because it's always the same (the same boilerplate).
Any ideas or solutions on that?
potentially you may want to also have a look at more resource-oriented rest libraries to complement your JAX-RS codebase. They can do exactly that (path mappings, sorting, filtering, paging, etc.) without an annotation or abstract base class hell. crnk.io + jsonapi, graphql, falcor are such libraries. (disclaimer: I contribute to the first one)

Extending and customizing existing REST API

I wanted to extend or customize my existing rest apis. In my rest the Service calls the Handler which is supposed to contain business logic (as a product) and I need a way to extend or customize this (per client customization). Please advise how to go about doing this. Attached is a simple hello world setup I have, it would be great it a hook into this 'DefaultGreetingsHandler.greetUser' can be shown.
package com.myapi.greetings.rest;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
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 com.myapi.greetings.handler.GreetingsHandler;
import com.myapi.greetings.model.Greeting;
#Path("/greetings")
public class GreetingsService {
#Inject
GreetingsHandler handler;
#GET
#Path("/{name}")
public Response getCustomMessage(#PathParam("name") String name) {
String output = handler.greetUser(name);
return Response.status(200).entity(output).build();
}
}
I need a hook in the Handler.greetUser to be able to customize per client
package com.myapi.greetings.handler;
import javax.inject.Inject;
import com.myapi.greetings.dao.GreetingsDao;
public class DefaultGreetingsHandler implements GreetingsHandler {
#Inject
GreetingsDao dao;
#Override
public String greetUser(String name) {
return dao.getGreeting() + name;
}
}
Its a simple maven based rest project in java. Please refer to following link for the complete setup zip file.
https://1drv.ms/u/s!AulFHVWnSJeFaqIB0JHKzo95DbU
Appreciate the help.
Create a New Class MyApplicationBinder
import org.glassfish.hk2.utilities.binding.AbstractBinder;
public class MyApplicationBinder extends AbstractBinder {
#Override
protected void configure() {
bind(GreetingsHandler.class).to(GreetingsHandler.class);
}
}
and then Register it in the main application class
#ApplicationPath("webapi")
public class MyApp extends ResourceConfig{
public MyApp() {
register(new MyApplicationBinder());
packages(true, "com.myapi.greetings.rest");
}
}
This should make your application work .

Cannot bind a ContainerRequestFilter using Glassfish4 and JAX-RS

I am having trouble attaching a ContainerRequstFilter to a very simple JAX-RS rest application inside Glassfish4.
I believe that I've followed the instructions from both the javadoc and various other tutorial sources, but I am now at a complete loss.
The entire source (very short) is below, with the expected functionality:
(login) A user can log in at http://localhost/app/api/login/uname/password and the response is "You are logged in, uname"
(verify) User visits http://localhost/app/api/login/verify/uname and the response is a 401 unauthorized.
The second bit is meant the be implemented as a filter. What actually happens is that the first bit (login) works, and the second bit (verify) completely ignores the filter (including nothing in the logs to indicate that the filter ran). That is, the output is just "Every thing is fine, uname", rather than a 401 error.
What I want to understand is the way to get the filter attached to the verify action. For reference
I'm running glassfish 4.1 build 13
I'm compiling and deploying using gradle, with the deployment action
assassin deploy --force --contextroot /app /path/to/app.war
Glassfish reports that it's using Jersey 2.10.4
Here is the entirety of the source related to the application:
RestApp.java
package test.pack;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/api")
public class RestApp extends Application
{
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(Login.class);
return classes;
}
}
Login.java
package test.pack;
import javax.ejb.Stateless;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
#Stateless
#Path("login/")
#Produces("text/json")
public class Login
{
#GET
#Path("{username}/{password}")
public Response login(#PathParam("username") String username, #PathParam("password") String password)
{
System.out.println("Logging in");
return Response.ok("You are logged in, " + username).build();
}
#GET
#Path("/verify/{username}")
#Secured
public Response verify(#PathParam("username") String username)
{
System.out.println("Verify");
return Response.ok("Everything is fine, " + username).build();
}
Secured.java
package test.pack;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.NameBinding;
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface Secured
{
}
LoggedInFilter.java
package test.pack;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
#Secured
public class LoggedInFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
System.out.println("request");
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
Ugh. That's embarrassing.
The next tutorial I visited had the solution, which was to register the filter in the RestApp class.
RestApp.java
#ApplicationPath("/api")
public class RestApp extends Application
{
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(Login.class);
return classes;
}
#Override
public Set<Object> getSingletons()
{
Set<Object> singletons = new HashSet<Object>();
singletons.add(new LoggedInFilter());
return singletons;
}
}
I'll leave the answer here rather than deleting the question, since it was only in one of 4 tutorials that I read, and so this might be at least a little interesting.

Customize Json Output with Jersey and Jaxb

I am trying to create a simple web service which outputs using json, but am not getting the desired Json output.
POJO:
package com.rest.resource;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Track implements Serializable
{
#XmlElement
String singer = "ABC";
#XmlElement
String title = "XYZ";
}
Service:
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.JAXBException;
import com.rest.resource.Track;
#Path("/json/metallica")
public class JSONService
{
#POST
#Path("/post")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Track createTrackInJSON(final Track track)
{
return track;
}
#GET
#Path("/get")
#Produces(MediaType.APPLICATION_JSON)
public Response getTrackInJSON() throws JAXBException
{
final Track track = new Track();
return Response.status(201).entity(track).build();
}
}
On /get I get
{"singer":"ABC","title":"XYZ"}
but I want "track": {"singer":"ABC","title":"XYZ"}
I am unable yo print the root element.
I tried using a CustomJAXBContextResolver class but did not work for me? Can anyone give an example of the same?
If you want to use the ContextResolver, you'd need to use the JSONConfiguration and switch the JSON Notation. You could do that by adding a class like this:
#Provider
public class MyJAXBContextProvider implements ContextResolver<JAXBContext> {
private JSONJAXBContext trackCtx;
public MyJAXBContextProvider() throws JAXBException {
trackCtx = new JSONJAXBContext(JSONConfiguration.mappedJettison().build(), Track.class);
}
public JAXBContext getContext(Class<?> type) {
if(type == Track.class) {
return trackCtx;
}
return null;
}
}
Adding that class produced this for me:
{"track":{"singer":"ABC","title":"XYZ"}}
For more info check out the Jersey Docs
You'd have to wrap Track with another object:
public class TrackWrapper {
Track track;
TrackWrapper(Track track) {
this.track=track;
}
}
and return an instance of TrackWrapper,
#GET
#Path("/get")
#Produces(MediaType.APPLICATION_JSON)
public Response getTrackInJSON() throws JAXBException
{
final TrackWrapper trackWrapper = new TrackWrapper(new Track());
return Response.status(201).entity(trackWrapper).build();
}
}
and just in case, if you're gonna use JSON only you don't need the JAXB annotations.

Categories

Resources