I'm trying to create a webservice using a Jersey HttpServer instead of Tomcat. The server is working when using simple GET request or plaintext, but when I try to send JSON objects I only get a server answer telling me that the media type is not supported (HTTP Error 415).
I think the problem is, that the JSON objects can't be deserialized like it's described in this post. But as I'm using a locally created HttpServer the solution of this answer didn't work for me.
So the question is: How can I configure my Jersey HttpServer to work for JSON objects?
The server implementation I'm using is:
import java.io.IOException;
import java.net.URI;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import com.sun.net.httpserver.HttpServer;
public class JsonRpcServer {
private static final Logger LOGGER = LogManager.getLogger(JsonRpcServer.class);
private static final String BASE_URI = "http://localhost:4711/";
private static final String BASE_PACKAGE = "com.picavi.json_rpc_server.service";
public static void main(String[] args) {
ResourceConfig config = new ResourceConfig().packages(BASE_PACKAGE);
HttpServer server = JdkHttpServerFactory.createHttpServer(URI.create(BASE_URI), config);
LOGGER.info("Starting server at {}", BASE_URI);
//server.start() is called as side effect
System.out.println("Press <Enter> to stop the server");
try {
System.in.read();
}
catch (IOException e) {
e.printStackTrace();
}
//stop the server after the user has made an input
LOGGER.info("Stopping server\n\n\n");
server.stop(0);
System.exit(0);
}
}
The service class:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
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;
import javax.ws.rs.core.Response.Status;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.picavi.json_rpc_server.backend.LoginException;
import com.picavi.json_rpc_server.backend.OrderPicking;
import com.picavi.json_rpc_server.backend.SystemAutentification;
import com.picavi.json_rpc_server.model.Configuration;
import com.picavi.json_rpc_server.model.Credentials;
import com.picavi.json_rpc_server.model.JsonRpcError;
import com.picavi.json_rpc_server.model.JsonRpcLoginAnswer;
import com.picavi.json_rpc_server.model.JsonRpcRequest;
import com.picavi.json_rpc_server.model.JsonRpcResponse;
import com.picavi.json_rpc_server.model.Picklist;
import com.picavi.json_rpc_server.model.PicklistRequestParameters;
@Path("/")
public class JsonRpcService {
private static final Logger LOGGER = LogManager.getLogger(JsonRpcService.class);
private static final String jsonRPC = "2.0";
@POST
@Path("/")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response processRequest(JsonRpcRequest request) {
LOGGER.info("received (synchrone) request: {}", request);
//execute the requested method
switch (request.getMethod()) {
case "system.login":
return processLogin(request);
case "system.logout":
return processLogout(request);
}
//if the method is none of the above return an error
LOGGER.warn("the request could not be processed, because the method name is unknown: {}", request.getMethod());
return createMethodNotFoundErrorResponse(request.getId(), request.getMethod());
}
@POST
@Path("/async")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response processRequestAsync(JsonRpcRequest request) {
LOGGER.info("received (asynchrone) request: {}", request);
//execute the requested method
try {
switch (request.getMethod()) {
case "system.login":
return CompletableFuture.supplyAsync(() -> processLogin(request)).get();
case "system.logout":
return CompletableFuture.supplyAsync(() -> processLogout(request)).get();
}
}
catch (InterruptedException | ExecutionException e) {
LOGGER.error("The asynchrone execution of the request failed", e);
//return an unknown error because this exceptions should never occur
return createErrorResponse(request.getId());
}
//if the method is none of the above return an error
LOGGER.warn("the request could not be processed, because the method name is unknown: {}", request.getMethod());
return createMethodNotFoundErrorResponse(request.getId(), request.getMethod());
}
private Response processLogin(JsonRpcRequest request) {
LOGGER.info("processing login request");
//get the login information
Credentials credentials;
try {
credentials = (Credentials) request.getParams();
}
catch (ClassCastException cce) {
return createIllegalParameterErrorResponse(request.getId(), request.getParams());
}
String user = credentials.getUser();
String password = credentials.getPassword();
//login the user
SystemAutentification systemProcessor = SystemAutentification.getInstance();
String sessionId;
try {
sessionId = systemProcessor.login(user, password);
}
catch (LoginException le) {
return createLoginErrorResponse(request.getId());
}
//create the response
JsonRpcResponse response = new JsonRpcResponse();
response.setId(request.getId());
response.setJsonRpc(jsonRPC);
response.setError(JsonRpcError.OK);
response.setResult(new JsonRpcLoginAnswer(sessionId, new Configuration("DE", "right")));
//return the response
return Response.status(Status.OK).entity(response).build();
}
private Response processLogout(JsonRpcRequest request) {
LOGGER.info("Processing logout request");
//get the information form the request
String sessionId;
try {
sessionId = (String) request.getParams();
}
catch (ClassCastException cce) {
return createIllegalParameterErrorResponse(request.getId(), request.getParams());
}
//logout the user
SystemAutentification systemProcessor = SystemAutentification.getInstance();
boolean logoutSucessful = systemProcessor.logout(sessionId);
//create the response
JsonRpcResponse response = new JsonRpcResponse();
response.setId(request.getId());
response.setJsonRpc(jsonRPC);
response.setError(JsonRpcError.OK);
response.setResult(Boolean.valueOf(logoutSucessful));
//return the response
return Response.status(Status.OK).entity(response).build();
}
//... some eror handling and response creation is left out here
}
My web.xml (although I don't think that it is even used because I use the local jersey http server):
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Picavi RPC</display-name>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.picavi.json_rpc_server.service</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
And a client log file that told me there is an HTTP 415 error:
[INFO ] 2019-06-30 19:16:27.244 [JavaFX Application Thread] SimpleRpcClient - Sending POST request to url: http://localhost:4711/; request: {
"jsonRpc" : "2.0",
"method" : "system.login",
"params" : {
"user" : "name",
"password" : "secret",
"station" : "",
"deviceIdent" : ""
},
"id" : "1"
}
[INFO ] 2019-06-30 19:16:27.429 [JavaFX Application Thread] SimpleRpcClient - Server sent response code: 415
Edit: My Maven dependencies are:
<repositories>
<repository>
<id>snapshot-repository.java.net</id>
<name>Java.net Snapshot Repository for Maven</name>
<url>https://maven.java.net/content/repositories/snapshots/</url>
<layout>default</layout>
</repository>
</repositories>
<dependencies>
<!-- some testing and logging frameworks -->
<!-- JAX-RS for REST -->
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jdk-http</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.10</version>
</dependency>
</dependencies>