integrating Swagger with JAX-RS and JAXB help needed - jaxb

I'm working on REST Api using JAX-RS and JAXB. I also would like to use Swagger to generate docs for the api. I followed the example here https://github.com/wordnik/swagger-core/wiki/java-jax-rs . So I have added com.wordnik.swagger.jaxrs.listing to init parameters in web.xml as below:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.api.resources;com.api;com.wordnik.swagger.jaxrs.listing;</param-value>
</init-param>
When I tray to access the localhost:9090/rapi/api-docs.json url I get the following error:
SEVERE: Mapped exception to response: 500 (Internal Server Error)
javax.ws.rs.WebApplicationException: javax.xml.bind.JAXBException: class com.wordnik.swagger.core.Documentation nor any of its super class is known to this context.
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:159)
at com.sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.java:306)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1451)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1363)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1353)
at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:414)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:708)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:362)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:726)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:206)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:829)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:514)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)
Caused by: javax.xml.bind.JAXBException: class com.wordnik.swagger.core.Documentation nor any of its super class is known to this context.
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:611)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:486)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:320)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:177)
at com.sun.jersey.json.impl.BaseJSONMarshaller.marshallToJSON(BaseJSONMarshaller.java:103)
at com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider.writeTo(JSONRootElementProvider.java:143)
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:157)
I fixed it by adding com.wordnik.swagger.core to my custom JAXBContextProvider as below:
public JAXBContextProvider() throws JAXBException {
this.context = new JSONJAXBContext(JSONConfiguration.natural().build(),
"com.api.model.impl.v1:com.api.resource.framework:com.wordnik.swagger.core");
}
This caused another error:
SEVERE: The provider class, class comapi.JAXBContextProvider, could not be instantiated. Processing will continue but the class will not be utilized
javax.xml.bind.JAXBException: "com.wordnik.swagger.core.Documentation" doesnt contain ObjectFactory.class or jaxb.index
I have been using jaxb.index files to keep the context aware of the classes with JAXB annotations, however there is no jaxb.index file in swagger package which inludes the Documentation class. Has anyone come across similar issue when integrating Swagger with JAXB and JAX-RS?

Turned out I needed to create JAXBContext in addition to already created JSONJAXBContext.
I added
this.swaggerJAXBcontext = JAXBContext.newInstance(com.wordnik.swagger.core.Documentation.class);
to constructor and this to getContext(Class type) method:
if (type.equals(com.wordnik.swagger.core.Documentation.class)){
return swaggerJAXBcontext;
}

Related

Handling collections of domain objects in JAXB and Spring

We have a Spring Integration application which uses a JDBC poller together with a RowMapper to read from a database and output a collection of domain objects (presuming the result set returned more than one row).
The domain objects are then marshalled into XML. When using Castor as the marshaller, this works ok, and the XML represents a collection of the domain objects:
<array-list>
<order>
<orderID>23940210</orderID>
...
</order>
<order>
...
</array-list>
We now wish to switch from Castor to JAXB.
This is the definition of the JAXB marshaller in XML:
<oxm:jaxb2-marshaller id="jaxbMarshallerBean">
<oxm:class-to-be-bound name="com.mycompany.Order" />
</oxm:jaxb2-marshaller>
... the JAXB marshaller is used as the transformer used in the Spring Integration chain ..
<int:chain input-channel="input" output-channel="output-jms">
<si-xml:marshalling-transformer id="defaultMarshaller" marshaller="jaxbMarshallerBean" />
</int:chain>
and of course the domain class is annotated:
#XmlRootElement(namespace ="Order")
public class Order{
...
#XmlElement(name="OrderID")
public String getOrderId() {
return orderId;
}
Now, the following exception is thrown:
org.springframework.oxm.UncategorizedMappingException:
Unknown JAXB exception;
nested exception is javax.xml.bind.JAXBException:
class java.util.ArrayList nor any of its super class is known to this context.
It looks like JAXB does not like the fact that it is handling a collection of domain objects. What is the correct way to configure or handle this?
Thanks very much
Surely you want an unmarshalling transformer to go from XML to POJO.
I don't know if there is a way to configure JAXB to handle it directly, but you could add an XPath splitter before the unmarshaller and an aggregator after it.

Is it possible to make Map<?, ?> someMethod work with JAX-B?

When trying to generate web service artifacts using cxf-java2ws-plugin, which in turn uses JAX-B, I get the error below on a method that looks like this:
Map<?, ?> myMethod(...);
Changing the method signature is a last resort so i'm looking for alternatives.
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
java.util.Map is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at java.util.Map
at private java.util.Map com.company.SomeClass.arg2
at com.company.SomeClass
java.util.Map does not have a no-arg default constructor.
this problem is related to the following location:
at java.util.Map
at private java.util.Map
JAXB will allow you to have a property of type Map, but not to use it as a root level object.
http://blog.bdoughan.com/2013/03/jaxb-and-javautilmap.html

Jaxb Xjc: make generated classes subtype of Exception?

How is it possible to make some xjc generated classes subclasses of a custom Exception, such that you can actually throw them, and processable by the JAXBContext? Often webservices return various faults defined that really should be an exception, but since they aren't you need to wrap them unneccesarily.
Even if you could create a JAXB (JSR-222) model that extended from Exception you wouldn't be able to create a JAXBContext from it. I would recommend wrapping the Exception in a domain model that is compatible with JAXB.
Java Model (Foo)
Below is a simple Java class that extends Exception.
package forum12840627;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Foo extends Exception {
}
Demo
The demo code below attempts to creates a JAXBContext on the Java model.
package forum12840627;
import javax.xml.bind.JAXBContext;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
}
}
Output
Below is the exception returned from running the demo code. The problem is that Exception is not a valid JAXB class and JAXB implementations pull in the super classes as it processes the Java model. (Note: In your own domain model you can annotate super classes with #XmlTransient to prevent them from being processed: http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html)
Exception in thread "main" com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.lang.StackTraceElement does not have a no-arg default constructor.
this problem is related to the following location:
at java.lang.StackTraceElement
at public java.lang.StackTraceElement[] java.lang.Throwable.getStackTrace()
at java.lang.Throwable
at java.lang.Exception
at forum12840627.Foo
at com.sun.xml.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:102)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:472)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:302)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1140)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:154)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:121)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:202)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:363)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:574)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:522)
at forum12840627.Demo.main(Demo.java:8)
UPDATE #1
If you are using EclipseLink JAXB (MOXy) as your JAXB provider then you will not see this exception as classes in the javax.* and java.* packages are not treated as domain classes. MOXy is the default JAXB provider in the WebLogic 12c environment or can be configured using a jaxb.properties file.
For More Information
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2011/12/eclipselink-moxy-is-jaxb-provider-in.html
UPDATE #2
The latest versions of the JAXB reference implementation appear to handle this use case now as well as MOXy. My original portability concerns may not be so much of an issue.
Yeah, I finally found something! The Inheritance plugin is able to make the generated classes inherit from classes or implement additional interfaces.
You need to include something like
<bindings node="//xsd:complexType[#name='WhateverException']">
<inheritance:extends>foo.bar.WhateverException</inheritance:extends>
</bindings>
into the binding file and override getStackTrace() to return null such that it doesn't get marshalled.
Unfortunately you might run into trouble with some JAXB implementations (see Blaise Doughan's answer) - I haven't found a workaround for that yet. So you can either use a not quite nonportable solution, or wrap the JAXB objects into Exceptions.

JAXB unmarshalling Custom entities without annotation

We have an xml file which we need to unmarshall(convert into a Java Object). Now the Java object is of third party and I cannot annotate it for unmarshalling. Any idea as to how I can Unmarshal without annotation. Please find my code snippet below
JAXBContext context;
try {
context = JAXBContext.newInstance(Abc.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
Abc abc= (Abc) unMarshaller.unmarshal(new FileInputStream("C:\\Documents and Settings\\sandeep.nair\\Desktop\\abc.xml"));
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (Exception e){
}
I am getting the following exception. One for No argument Constructor(I know I can solve this by adding annotation for Adapter but I want to know or see a sample snippet for handling it without annotation)
Similarly I am getting another message as to Interfaces cannot be handle by JAXB.
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 5 counts of IllegalAnnotationExceptions
java.sql.Timestamp does not have a no-arg default constructor.
this problem is related to the following location:
at java.sql.Timestamp
at public java.sql.Timestamp com.test.Abc.getSomeTimestamp()
at com.riteaid.entities.customer.Customer
com.test.Def does not have a no-arg default constructor.
this problem is related to the following location:
at com.test.Def...
java.sql.Date does not have a no-arg default constructor.
this problem is related to the following location:
at java.sql.Date
...
com.test.Ghi is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
...
com.test.Ghi does not have a no-arg default constructor.
this problem is related to the following location:
..
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
JAXB is configuration by exception , so you only need to add annotations where you want to override the default mapping behaviour:
http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html
The standard way of supplying metadata is through annotations. In situations where you can't annotate such as 3rd party classes, then you may be interested in MOXy's external mapping document extension.
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://blog.bdoughan.com/2012/04/extending-jaxb-representing-metadata-as.html
You can use an XmlAdapter to handle the javax.sql.Date and javax.sql.Timestamp types:
jaxb unmarshal timestamp

JAXB is not picking up #XmlJavaTypeAdapter

I have the following class that I need to serialize as XML:
#XmlAccessorType(XmlAccessType.FIELD)
public class Position {
#XmlElement(name = "Quantity", required = true)
private DecimalQuantity quantity;
...
}
I have put an XmlJavaTypeAdapter on the DecimalQuantity class because I want it to be serialized simply as a BigDecimal without the DecimalQuantity wrapper.
#XmlJavaTypeAdapter(DecimalQuantityAdapter.class)
#Embeddable
public class DecimalQuantity {
private BigDecimal value;
...
}
Here's the very simple DecimalQuantityAdapter class:
public class DecimalQuantityAdapter
extends XmlAdapter<BigDecimal, DecimalQuantity> {
public DecimalQuantity unmarshal(BigDecimal val) throws Exception {
return new DecimalQuantity(val);
}
public BigDecimal marshal(DecimalQuantity val) throws Exception {
return val.getValue();
}
}
I have a unit test that shows that the adapter is working correctly. The following Order object that has a DecimalQuantity gets serialized correctly (notice that this test class looks almost identical to the Position class above):
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Order")
public class Order {
#XmlElement(name = "Quantity", required = true)
private DecimalQuantity quantity;
...
}
This gets serialized as shown below - no wrapper around the decimal number - life is good!
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Order>
<Quantity>10.2</Quantity>
</Order>
The trouble starts when I try to use DecimalQuantity in other maven projects. For example, the Position class shown at the beginning of this post is in a different maven project. The web service that uses the Position class is in yet another maven project. When the web service tries to deserialize DecimalQuantity, it does not know what DecimalQuantity is and is not able to pick up the DecimalQuantityAdapter. This is the error I get:
Caused by: javax.xml.bind.JAXBException:
class org.archfirst.common.quantity.DecimalQuantity nor any of its super class is known to this context.
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:594)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:648)
... 53 more
I have event tried to add the #XmlJavaTypeAdapter annotation on the attribute itself, but JAXB does not pick it up. The only way to get rid of the exception is to put an #XmlSeeAlso({DecimalQuantity.class}) on the Position class itself. However, this disables the adapter and I get the following (undesired) serialization:
<Quantity xsi:type="ns2:decimalQuantity" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
Any idea where the problem is? I feel it has something to do with the visibility of JAXB annotations on DecimalQuantity and DecimalQuantityAdapter across packages/projects.
Thanks.
Naresh
Ok, I finally found the problem. My unit test was picking up the JAXB implementation in the Java runtime, whereas my real application (a web service) was picking up the JAXB implementation from GlassFish. Apparently the implementation bundled with GlassFish (2.2.1.1) cannot handle my use case. I proved it by forcing my unit test to use jaxb-impl-2.2.1.1.jar. Also it seems that the bug has been fixed in the latest JAXB implementation (2.2.3-1), but I am struggling to figure out how to replace GlassFish's implementation with this new version (see my post here).
Are you sure the problem is with the XmlJavaTypeAdapter for decimals, not the DecimalQuantity type. Because the exception you've posted is the one that happens when JAXB encounters a value of unknown class.
What happens if you omit the #XmlJavaTypeAdapter annotation? I know it probably can't work the way you intend, but what is the error message? Isn't it the same?
As you wrote the exception is gone when you added:
#XmlSeeAlso({DecimalQuantity.class})
I would leave the annotation in the code and try to find the reason why the adapter doesn't work.
Can you debug in the your XML adapter and/or add some trace output there, just to make sure the adapter really returns a non-empty String?

Resources