Swagger documentation with JAX-RS Jersey 2 and Grizzly - rest

I have implementated a Rest web service (the function is not relevant) using JAX-RS. Now I want to generate its documentation using Swagger. I have followed these steps:
1) In build.gradle I get all the dependencies I need:
compile 'org.glassfish.jersey.media:jersey-media-moxy:2.13'
2) I documentate my code with Swagger annotations
3) I hook up Swagger in my Application subclass:
public class ApplicationConfig extends ResourceConfig {
/**
* Main constructor
* #param addressBook a provided address book
*/
public ApplicationConfig(final AddressBook addressBook) {
register(AddressBookService.class);
register(MOXyJsonProvider.class);
register(new AbstractBinder() {
#Override
protected void configure() {
bind(addressBook).to(AddressBook.class);
}
});
register(io.swagger.jaxrs.listing.ApiListingResource.class);
register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8282");
beanConfig.setBasePath("/");
beanConfig.setResourcePackage("rest.addressbook");
beanConfig.setScan(true);
}
}
However, when going to my service in http://localhost:8282/swagger.json, I get this output.
You can check my public repo here.

It's times like this (when there is no real explanation for the problem) that I throw in an ExceptionMapper<Throwable>. Often with server related exceptions, there are no mappers to handle the exception, so it bubbles up to the container and we get a useless 500 status code and maybe some useless message from the server (as you are seeing from Grizzly).
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
public class DebugMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable exception) {
exception.printStackTrace();
if (exception instanceof WebApplicationException) {
return ((WebApplicationException)exception).getResponse();
}
return Response.serverError().entity(exception.getMessage()).build();
}
}
Then just register with the application
public ApplicationConfig(final AddressBook addressBook) {
...
register(DebugMapper.class);
}
When you run the application again and try to hit the endpoint, you will now see a stacktrace with the cause of the exception
java.lang.NullPointerException
at io.swagger.jaxrs.listing.ApiListingResource.getListingJson(ApiListingResource.java:90)
If you look at the source code for ApiListingResource.java:90, you will see
Swagger swagger = (Swagger) context.getAttribute("swagger");
The only thing here that could cause the NPE is the context, which scrolling up will show you it's the ServletContext. Now here's the reason it's null. In order for there to even be a ServletContext, the app needs to be run in a Servlet environment. But look at your set up:
HttpServer server = GrizzlyHttpServerFactory
.createHttpServer(uri, new ApplicationConfig(ab));
This does not create a Servlet container. It only creates an HTTP server. You have the dependency required to create the Servlet container (jersey-container-grizzly2-servlet), but you just need to make use of it. So instead of the previous configuration, you should do
ServletContainer sc = new ServletContainer(new ApplicationConfig(ab));
HttpServer server = GrizzlyWebContainerFactory.create(uri, sc, null, null);
// you will need to catch IOException or add a throws clause
See the API for GrizzlyWebContainerFactory for other configuration options.
Now if you run it and hit the endpoint again, you will see the Swagger JSON. Do note that the response from the endpoint is only the JSON, it is not the documentation interface. For that you need to use the Swagger UI that can interpret the JSON.
Thanks for the MCVE project BTW.

Swagger fixed this issue in 1.5.7. It was Issue 1103, but the fix was rolled in last February. peeskillet's answer will still work, but so will OP's now.

Related

Issues with Restful Jersey and JSON

I have a strange issue and didn't find any information about it at all.
Having a simple POJO like (simplified..)
#XmlRootElement
public class Bill {
List<Position> positions
.. getter/setter
}
#XmlRootElement
public class Position {
.. some simple properties with getters/setters
}
I am unable to call a RESTful Service using instances of these classes. I'm getting real weird errors I don't really understand.
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.List out of START_OBJECT token
The funny thing is, when I just test serialization/deserialization using Jackson Object mapper directly, it works as expected!
ObjectMapper mapper = new ...
mapper.writeValue(stringWriter, bill);
mapper.readValue(stringWriter.toString(), Bill.class);
This works perfectly. So I guess the POJO itself is OK and Jackson is able to handle the JSON-String.
Calling the RESTful service using the same Bill instance fails with the error mentioned above. I see it is using Jackson as well, here is part of stack trace:
at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:160)
at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:198)
at org.codehaus.jackson.map.deser.CollectionDeserializer.deserialize(CollectionDeserializer.java:103)
at org.codehaus.jackson.map.deser.CollectionDeserializer.deserialize(CollectionDeserializer.java:93)
at org.codehaus.jackson.map.deser.CollectionDeserializer.deserialize(CollectionDeserializer.java:25)
at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:230)
And here is how the RESTful Application is configured:
#javax.ws.rs.ApplicationPath("rest")
public class ApplicationConfig extends Application {
public Set<Class<?>> getClasses() {
return getRestResourceClasses();
}
/**
* Do not modify this method. It is automatically generated by NetBeans REST support.
*/
private Set<Class<?>> getRestResourceClasses() {
Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
resources.add(rest.RestAPI.class);
// following code can be used to customize Jersey 1.x JSON provider:
try {
Class jacksonProvider = Class.forName("org.codehaus.jackson.jaxrs.JacksonJsonProvider");
resources.add(jacksonProvider);
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
return resources;
}
}
Do you have any idea what I'm missing?
I generated the service and the client with NetBeans. Oh and it works when I use XML instead of JSON.
Any help would be very much appreciated.
I'm sorry but after hours of testing and debugging I finally found the cause of the problem.
Still I would be very interested why this is?
Commenting out the following code did the trick:
// following code can be used to customize Jersey 1.x JSON provider:
try {
Class jacksonProvider = Class.forName("org.codehaus.jackson.jaxrs.JacksonJsonProvider");
resources.add(jacksonProvider);
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex);
}

jersey 2.0 jaxrs RI - return json string on exception

I am creating a REST service using jersey 2.0. I am extending WebApplicationException
Method raising a particular exception
if(json.equals("") || json.equals(" ")) {
throw new ArgumentException("bad post data");
}
public class ArgumentException extends RestException {
.....
public ArgumentException(String message) {
super(Status.BAD_REQUEST,message);
}
}
public class RestException extends WebApplicationException {
...........
public RestException(Status status, String message) {
super(Response.status(status)
.entity(message)
.type("text/plain")
.build());
/*
super(Response.status(status)
.entity(new ErrorBean(status.getStatusCode(),message))
.type(MediaType.APPLICATION_JSON)
.build()); */
}
ErrorBean is a POJO
The method that returns error as plain string inside RestException works (right http code 400 and message). However when I try to pass the ErrorBean POJO and use MediaType.APPLICATION_JSON in response I get an error saying "Headers have already been sent" with http error code 500 (so some internal problem with plumbing) and empty response.
I have also looked at this question Returning JSON or XML for Exceptions in Jersey
How can I return the exception with code and message as a JSON like
{"code" : 400, "message" : .... }
Update
I have received answer on SO as well as jersey users mailing list. steps are
A non AJXB POJO does not need any annotations
Register JacksonFeature in your application
ResourceConfig rc = new ResourceConfig().packages("test").register(JacksonFeature.class);
You need to register JacksonFeature in your Application/ResourceConfig, i.e.:
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.jackson")
.register(JacksonFeature.class)
// No need to register this provider if no special configuration is required.
.register(MyObjectMapperProvider.class);
Take a look at the documentation for Jackson support in Jersey and also at the example.

Play framework 2 + JPA with multiple persistenceUnit

I'm struggling with Play and JPA in order to be able to use two different javax.persistence.Entity model associated to two different persistence units (needed to be able to connect to different DB - for example an Oracle and a MySQL db).
The problem come from the Transaction which is always bind to the default JPA persitenceUnit (see jpa.default option).
Here is two controller actions which show the solution I found to manually define the persistence :
package controllers;
import models.Company;
import models.User;
import play.db.jpa.JPA;
import play.db.jpa.Transactional;
import play.mvc.Controller;
import play.mvc.Result;
public class Application extends Controller {
//This method run with the otherPersistenceUnit
#Transactional(value="other")
public static Result test1() {
JPA.em().persist(new Company("MyCompany"));
//Transaction is run with the "defaultPersistenceUnit"
JPA.withTransaction(new play.libs.F.Callback0() {
#Override
public void invoke() throws Throwable {
JPA.em().persist(new User("Bobby"));
}
});
return ok();
}
//This action run with the otherPersistenceUnit
#Transactional
public static Result test2() {
JPA.em().persist(new User("Ryan"));
try {
JPA.withTransaction("other", false, new play.libs.F.Function0<Void>() {
public Void apply() throws Throwable {
JPA.em().persist(new Company("YourCompany"));
return null;
}
});
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
return ok();
}
}
This solution doesn't seem to be really "clean". I'd like to know if you know a better way to avoid the need to manually modify the transaction used.
For this purpose, I created a repo on git with a working sample application which shows how I configured the project.
https://github.com/cm0s/play2-jpa-multiple-persistenceunit
Thank you for your help
i met the same problem, too. too many advices are about PersistenceUnit annotation or getJPAConfig. but both them seem not work in play framework.
i found out a method which works well in my projects. maybe you can try it.
playframework2 how to open multi-datasource configuration with jpa
gud luk!

Xpage REST service control and service bean

I am trying to implement REST Service using XPage REST Service Control. I have opted for "customRESTService".
I would like to emit JSON when this service is requested. I can write logic in Server Side Java Script.
But I noticed that this customRESTService also supports "serviceBean", meaning I can write whole logic in pure JAVA.
I have given below code of the bean. I have declared it in faces-config.xml as well. But it throws exception while rendering. Has anyone used "serviceBean" in customRESTService?
I appreciate any help!! Thanks!!
public class GetApproverJSON{
public GetApproverJSON(){
System.out.println("Instantiating Bean");
}
public String doGet() throws NotesException{
JSONObject mainObj = new JSONObject();;
JSONObject itemObj;
try{
mainObj.put("label", "name");
mainObj.put("identifier", "abbr");
itemObj = new JSONObject();
itemObj.put("name", "");
itemObj.put("abbr", "");
mainObj.accumulate("items", itemObj);
return mainObj.toString();
}catch(Exception e){
System.out.println("Exception occured while generating JSON ");
e.printStackTrace();
return mainObj.toString();
}finally{
}
}
Error :
com.ibm.domino.services.ServiceException: Error while rendering service
at com.ibm.xsp.extlib.component.rest.CustomService$ScriptServiceEngine.renderService(CustomService.java:304)
at com.ibm.domino.services.HttpServiceEngine.processRequest(HttpServiceEngine.java:167)
at com.ibm.xsp.extlib.component.rest.UIBaseRestService._processAjaxRequest(UIBaseRestService.java:252)
at com.ibm.xsp.extlib.component.rest.UIBaseRestService.processAjaxRequest(UIBaseRestService.java:229)
at com.ibm.xsp.util.AjaxUtilEx.renderAjaxPartialLifecycle(AjaxUtilEx.java:206)
at com.ibm.xsp.webapp.FacesServletEx.renderAjaxPartial(FacesServletEx.java:221)
at com.ibm.xsp.webapp.FacesServletEx.serviceView(FacesServletEx.java:166)
at com.ibm.xsp.webapp.FacesServlet.service(FacesServlet.java:160)
at com.ibm.xsp.webapp.FacesServletEx.service(FacesServletEx.java:137)
at com.ibm.xsp.webapp.DesignerFacesServlet.service(DesignerFacesServlet.java:103)
at com.ibm.designer.runtime.domino.adapter.ComponentModule.invokeServlet(ComponentModule.java:576)
at com.ibm.domino.xsp.module.nsf.NSFComponentModule.invokeServlet(NSFComponentModule.java:1267)
at com.ibm.designer.runtime.domino.adapter.ComponentModule$AdapterInvoker.invokeServlet(ComponentModule.java:847)
at com.ibm.designer.runtime.domino.adapter.ComponentModule$ServletInvoker.doService(ComponentModule.java:796)
at com.ibm.designer.runtime.domino.adapter.ComponentModule.doService(ComponentModule.java:565)
at com.ibm.domino.xsp.module.nsf.NSFComponentModule.doService(NSFComponentModule.java:1251)
at com.ibm.domino.xsp.module.nsf.NSFService.doServiceInternal(NSFService.java:598)
at com.ibm.domino.xsp.module.nsf.NSFService.doService(NSFService.java:421)
at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.doService(LCDEnvironment.java:341)
at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.service(LCDEnvironment.java:297)
at com.ibm.domino.xsp.bridge.http.engine.XspCmdManager.service(XspCmdManager.java:272)
Caused by: com.ibm.xsp.FacesExceptionEx: Bean getApproverJSON is not a CustomServiceBean
at com.ibm.xsp.extlib.component.rest.CustomService.findBeanInstance(CustomService.java:226)
at com.ibm.xsp.extlib.component.rest.CustomService$ScriptServiceEngine.renderService(CustomService.java:255)
... 20 more
You need to change your code to:
public class GetApproverJSON{ ...}
to:
public class GetApproverJSON extends CustomServiceBean {
#Override
public void renderService(CustomService service, RestServiceEngine engine) throws ServiceException {
HttpServletRequest request = engine.getHttpRequest();
HttpServletResponse response = engine.getHttpResponse();
response.setHeader("Content-Type", "application/json; charset=UTF-8");
// Here goes your code, get the response writer or stream
}
since that's the interface the REST service is expecting. You will need to implement just renderService. You can get the method (GET, POST etc.) from the request object
I've never used the service bean before, I usually create my own parser with a static doGet method very similar to yours and in the doGet property of the custom REST service make a call to the static doGet method I create. But I think (I'm probably wrong on this count) if you use the service bean it has to be an entire servlet like if you wrote your own actual REST Service, and not just the parser portion.
I've created quite a few of the parsers and have found that a list of maps:
List>
is usually the best approach for building the initial data. I then loop through the list to build my JSON. In the Extension Library there is a class called JsonWriter which makes it very easy to build a JSON Object. Use the JsonWriter like:
StringWriter sw = new StringWriter();
JsonWriter jw = new JsonWriter(sw);
jw.startObject();
jw.startProperty("SomeProperty");
jw.outStringLiteral("SomeValue");
jw.endProperty();
jw.endObject();
return sw.toString();
For a full on example you can take a look at the REST service I built for my JQuery FullCalendar demo. While none of the methods are static (I need to track a couple of properties) you should get the basic idea. But what kicks the whole thing off is a call to the writeJson() method. That is invoked in this custom control.
Those examples should get you going on building your own custom JSON parser and emitting that JSON back to your application.

Problem with GWT behind a reverse proxy - either nginx or apache

I'm having this problem with GWT when it's behind a reverse proxy. The backend app is deployed within a context - let's call it /context.
The GWT app works fine when I hit it directly:
http://host:8080/context/
I can configure a reverse proxy in front it it. Here's my nginx example:
upstream backend {
server 127.0.0.1:8080;
}
...
location / {
proxy_pass http://backend/context/;
}
But, when I run through the reverse proxy, GWT gets confused, saying:
2009-10-04 14:05:41.140:/:WARN: Login: ERROR: The serialization policy file '/C7F5ECA5E3C10B453290DE47D3BE0F0E.gwt.rpc' was not found; did you forget to include it in this deployment?
2009-10-04 14:05:41.140:/:WARN: Login: WARNING: Failed to get the SerializationPolicy 'C7F5ECA5E3C10B453290DE47D3BE0F0E' for module 'https://hostname:444/'; a legacy, 1.3.3 compatible, serialization policy will be used. You may experience SerializationExceptions as a result.
2009-10-04 14:05:41.292:/:WARN: StoryService: ERROR: The serialization policy file '/0445C2D48AEF2FB8CB70C4D4A7849D88.gwt.rpc' was not found; did you forget to include it in this deployment?
2009-10-04 14:05:41.292:/:WARN: StoryService: WARNING: Failed to get the SerializationPolicy '0445C2D48AEF2FB8CB70C4D4A7849D88' for module 'https://hostname:444/'; a legacy, 1.3.3 compatible, serialization policy will be used. You may experience SerializationExceptions as a result.
In other words, GWT isn't getting the word that it needs to prepend /context/ hen look for C7F5ECA5E3C10B453290DE47D3BE0F0E.gwt.rpc, but only when the request comes throught proxy. A workaround is to add the context to the url for the web site:
location /context/ {
proxy_pass http://backend/context/;
}
but that means the context is now part of the url that the user sees, and that's ugly.
Anybody know how to make GWT happy in this case?
Software versions:
GWT - 1.7.0 (same problem with 1.7.1)
Jetty - 6.1.21 (but the same problem existed under tomcat)
nginx - 0.7.62 (same problem under apache 2.x)
I've looked at the traffic between the proxy and the backend using DonsProxy, but there's nothing noteworthy there.
I have the same problem, and I opened a bug report:
http://code.google.com/p/google-web-toolkit/issues/detail?id=4817
The problem is that it was marked "As Design", so I don't think it will be fixed.
I found this solution for me. I extended the class RemoteServiceServlet and I forced GWT to load serialization policy file starting from ContextName instead of URL.
Then I extended my service my class instead of RemoteServiceServlet class.
In this way the application will be unlinked from the url from where it will be called.
Here there is my custom class:
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
public class MyRemoteServiceServlet extends RemoteServiceServlet
{
#Override
protected SerializationPolicy doGetSerializationPolicy(HttpServletRequest request, String moduleBaseURL, String strongName)
{
return MyRemoteServiceServlet.loadSerializationPolicy(this, request, moduleBaseURL, strongName);
}
/**
* Used by HybridServiceServlet.
*/
static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
HttpServletRequest request, String moduleBaseURL, String strongName) {
// The serialization policy path depends only by contraxt path
String contextPath = request.getContextPath();
SerializationPolicy serializationPolicy = null;
String contextRelativePath = contextPath + "/";
String serializationPolicyFilePath = SerializationPolicyLoader.getSerializationPolicyFileName(contextRelativePath
+ strongName);
// Open the RPC resource file and read its contents.
InputStream is = servlet.getServletContext().getResourceAsStream(
serializationPolicyFilePath);
try {
if (is != null) {
try {
serializationPolicy = SerializationPolicyLoader.loadFromStream(is,
null);
} catch (ParseException e) {
servlet.log("ERROR: Failed to parse the policy file '"
+ serializationPolicyFilePath + "'", e);
} catch (IOException e) {
servlet.log("ERROR: Could not read the policy file '"
+ serializationPolicyFilePath + "'", e);
}
} else {
String message = "ERROR: The serialization policy file '"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in this deployment?";
servlet.log(message);
}
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// Ignore this error
}
}
}
return serializationPolicy;
}
}
Michele,
Thank you for the example servlet to handle this problem. However when I tried to use your approach it worked in the reverse proxy environment but not in my dev mode eclipse environment.
I took an approach that would let me seamlessly move between my dev and prod environments.
As you did I overwrote RemoteServiceServlet but I only replaced following...
#Override
protected SerializationPolicy doGetSerializationPolicy(
HttpServletRequest request, String moduleBaseURL, String strongName) {
//get the base url from the header instead of the body this way
//apache reverse proxy with rewrite on the header can work
String moduleBaseURLHdr = request.getHeader("X-GWT-Module-Base");
if(moduleBaseURLHdr != null){
moduleBaseURL = moduleBaseURLHdr;
}
return super.doGetSerializationPolicy(request, moduleBaseURL, strongName);
}
In my apache config I added...
ProxyPass /app/ ajp://localhost:8009/App-0.0.1-SNAPSHOT/
RequestHeader edit X-GWT-Module-Base ^(.*)/app/(.*)$ $1/App-0.0.1-SNAPSHOT/$2
This approach works in all scenarios and delegates the url "mucking" to apache's proxy settings which is the approach I've always taken.
Comments on this approach are appreciated
I'm fairly sure the correct answer here is to patch the source and submit a bug report. Another option would be to run the GWT app at / on your backend.
I'd prefer the former, but the latter should work too. If you really needed things separated out into multiple contexts, use a different port number?
I've run into a similar problem, a successful workaround was to make all serialized objects implement GWT's IsSerializable interface (in addition to the standard Serializable interface). If you read the message, it states that 'a legacy, 1.3.3 compatible, serialization policy will be used' - the 1.3.3 compatible policy requires all of your serialized objects implement the IsSerializable interface, so by adding it, everything worked.
I do have concerns that the legacy policy will be desupported in future versions of GWT, so i am also in search for a better workaround myself.
KC's answer is good. For those that do not want to muck around with apache configs, or need a quick and dirty way to test, here is a code only solution.
protected SerializationPolicy doGetSerializationPolicy(final HttpServletRequest request, String moduleBaseURL, final String strongName) {
final String moduleBaseURLHdr = request.getHeader("X-GWT-Module-Base");
if (moduleBaseURLHdr != null) {
moduleBaseURL = moduleBaseURLHdr.replace("foo/bar", "bar");
}
return super.doGetSerializationPolicy(request, moduleBaseURL, strongName);
}
The application is on http://server/bar, the proxy is serving it at http://proxy/foo/bar
Hence moduleBaseURL = moduleBaseURLHdr.replace("foo/bar", "bar"); makes GWT happy.
Likewise if the application is at http://server/bar and the proxy is serving at http://proxy/, you need to add bar to the moduleBaseURL (right before the package name).
This can be generalized through the use of getServletContext().getContextPath() etc...
My goal was to avoid additional header(s) which would make deployment and configuration harder. I solved this problem by overriding RemoteServiceServlet.doGetSerializationPolicy():
#Override
protected SerializationPolicy doGetSerializationPolicy(HttpServletRequest request, String moduleBaseURL, String strongName) {
String localServerAddress = "http://127.0.0.1:" + getThreadLocalRequest().getLocalPort();
String localContextPath = getServletConfig().getServletContext().getContextPath();
String moduleName = extractGwtModuleName(moduleBaseURL);
String localModuleBaseURL = joinPaths(localServerAddress, localContextPath, moduleName, "/");
return super.doGetSerializationPolicy(request, localModuleBaseURL, strongName);
}
In above code:
extractGwtModuleName() extracts last string prefixed and/or followed by slash
joinPaths() joins multiple url parts, removes unnecessary slashes
Use restful JSON for your RPC calls instead of GWT-RPC.
This solves the reverse-proxy problem since no serialization files are required.