I have some #javax.xml.bind.annotation.Xml... annotated classes here intended for a RESt web service. Jersey is setup in a spring managed web container and the web service is returning a well formatted xml. We use the maven-enunciate-plugin to document the web service and create the xsd to the returned xml documents. I now would like to use the documentation xsd file as a schemaLocation within the returned xml file so that the xml validation won't complain about missing definions. How can I get the XML serialisation configured for this?
If I remember correctly, I had to do a few of things to get namespace identifiers properly written into my generated XML.
1) Created a JaxbFactory that configs and returns a custom marshaller (and unmarshaller, too, BTW). I'm omitting the getters/and unmarshalling setup below...
//constructor
public JaxbFactory() throws Exception {
context = JAXBContext.newInstance(ResourceDto.class);
// Setup the marshaller
marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, XmlMetadataConstants.XML_SCHEMA_LOCATION); // this schema location is used in generating the schema-location property in the xml
}
2) That factory class isn't "visible" to Jersey. To make it visible, I create a MarshallerProvider. That looks something like this:
#Provider
public class ResourceJaxbMarshallerProvider implements ContextResolver<Marshaller> {
// injected by Spring
private ResourceJaxbFactory ResourceJaxbFactory;
private ResourceStatusJaxbFactory ResourceStatusJaxbFactory;
/*
* ----------------------------------------
* Setters (for Spring injected properties)
* ----------------------------------------
*/
public void setResourceJaxbFactory(ResourceJaxbFactory ResourceJaxbFactory) {
this.ResourceJaxbFactory = ResourceJaxbFactory;
}
public void setResourceStatusJaxbFactory(ResourceStatusJaxbFactory ResourceStatusJaxbFactory) {
this.ResourceStatusJaxbFactory = ResourceStatusJaxbFactory;
}
/*
* ------------------------
* Interface Implementation
* ------------------------
*/
public Marshaller getContext(Class<?> type) {
if (type == ResourceDto.class)
return ResourceJaxbFactory.getMarshaller();
else if (type == ResourceStatusDto.class)
return ResourceStatusJaxbFactory.getMarshaller();
else
return null;
}
}
I've got Jersey wired into Spring using the Jersey/Spring Servlet so any #Provider class that gets created by Spring is automatically recognized by Jersey. In my Spring applicationContext.xml all I have to do is instantiate the resource provider. It will, in turn, go grab the marshaller from the factory.
3) The other thing that I found critical was that I had to create a package-info.java file in the root package containing my resource. Looks like this:
/*
* Note that this file is critical for ensuring that our ResourceDto object is
* marshalled/unmarshalled with the correct namespace. Without this, marshalled
* classes produce XML files without a namespace identifier
*/
#XmlSchema(namespace = XmlMetadataConstants.XML_SCHEMA_NAMESPACE, elementFormDefault = XmlNsForm.QUALIFIED)
package com.yourcompany.resource;
import javax.xml.bind.annotation.XmlNsForm;
At least I think that's everything I needed to do, I can't remember every single piece. I do remember that the package-info.java piece was the last critical cog that made it all come together.
Hope that helps. I spent wayyyy too much time digging for the info on all this. Jersey was seductively simple before I wanted it to do proper xml schema validation (and decent error reporting for schema-invalid input). Once I started down that road Jersey went from brain-dead easy to decently hard. The majority of that difficulty was sussing out all the details from the variety of posts online. Hopefully this will help get you farther, quicker. :-)
Related
I have a stateless REST API build on Spring Boot 1.4.2. I want to log all the API calls into elk. Requests and responses data (headers, parameters, payload) need to be logged as well. I don't want to log them 1:1 - I want to filter out sensitive data etc.
I made an aspect that is intercepting my #RestController's methods invocation. I created custom annotation for method's parameter that should be logged (I use it on payloads annotated as well by #RequestBody) following this article and it gave me access to my data transfer objects in my #Around advice. I dont care about their type - I would like to call logger.debug(logObject) and send this log to logstash.
As far as I understand log message should be send as JSON with JSONLayout set in Log4j2 appender to ease things on the logstash side. So I serialize my logObject into JSON log message but during this and this only serialization I want to filter sensitive data out. I can not use transient because my controller depends on the same field.
Can I somehow create an #IgnoreForLogging annotation, that will be detected only by my custom Gson serializer that I use within logging advice and will be ignored within standard Spring's infrastructure? Is my logging into logstash approach even correct (I am trying to set it up for the first time)?
I can't believe I missed that in documentation. Here is the link
My custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface IgnoreForLogging {
}
Strategy for serializing objects:
public class LoggingExclusionStrategy implements ExclusionStrategy {
#Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
return fieldAttributes.getAnnotation(IgnoreForLogging.class) != null;
}
#Override
public boolean shouldSkipClass(Class<?> aClass) {
return false;
}
}
Serializing log message in aspect class:
Gson gson = new GsonBuilder()
.setExclusionStrategies(new LoggingExclusionStrategy())
.create();
String json = gson.toJson(logObject);
This way Spring internally uses default serializer that doesn't know about #IgnoreForLogging and I can take advantage of my annotation in other places.
Question Description :
I have a JAX-RS resource pojo defined as below (outside is cxf container inregrated with spring, running in a tomcat)
#Path("/test/{id}")
#Consumes(MediaType.APPLICATION_JSON)
public class TestService {
#PathParam("id") private String id;
#GET
public Response get() throws Exception{
return Response.ok().entity(id).build();
}
}
Then I use jmeter to send some load with auto-increasing "id" parameters to the server. And I got this issue : the id in the response doesn't match that was sent.
E.g. request "localhost:8090/test/100" will get a "87" in the response.
And The frequency of error increases by using more client threads or making the handler method slower like this :
#GET
public Response get() throws Exception{
return Response.ok().entity(id).build();
Thread.sleep(500);
}
My thinking and confusion: The TestService is used as a singleton and since the "id" is a shared
field, so it MAY cause inconsistency issue when there are multiple threads running the "get()" function because it uses the shared "id". And then I put the "id" into the method parameter issue was resolved :
#Path("/test/{id}")
#Consumes(MediaType.APPLICATION_JSON)
public class TestService {
#GET
public Response get(#PathParam("id") String id) throws Exception{
return Response.ok().entity(id).build();
}
}
My confusion is : If this is a existing problem, I did saw lots of places and articles with the first style of using #PathParam, even in the jsr-339-jaxrs final spec?
![code snippets from jsr-339-jaxrs final spec][1]
Or both style there is good but I made some mistakes on my code?
Thanks!
A quick look at the docs seems to suggest that in CXF, with Spring, resources are treated as singletons by default:
"By default, the service beans which are referenced directly from the jaxrs:server endpoint declarations are treated by the runtime as singleton JAX-RS root resources"
Apache CXF Docs - Lifecycle Management Section
But in Jersey, the JAX-RS reference implementation, root resources are treated as dependent scoped (a new one is created on each request) unless otherwise specified.
By default the life-cycle of root resource classes is per-request which, namely that a new instance of a root resource class is created every time the request URI path matches the root resource.
See section 3.4 in https://jersey.java.net/documentation/latest/jaxrs-resources.html
So, if you are using CXF with Spring, your resources are likely singletons unless you configure them to be Spring Prototypes. With dependent scoped injection, #PathParam as an instance field should be fine, but in a singleton scope it you would expect to see issues like you describe.
I'm working on REST web-service written with jersey and I'm trying to output some XML with CDATA sections in it. I understand the reference implementation of JAXB doesn't support that, so I've downloaded EclipseLink's MOXy and I'm trying to get the #XmlCDATA annotation to work.
My JAXB mapped bean looks like this
package com.me.entities;
#XmlRootElement #XmlAccessorType(XmlAccessType.FIELD)
public class MyBean {
#XmlAttribute
private URI thumbnail;
#XmlElement(name="longdescription") #XmlCDATA
private String description;
public MyBean() { }
public final String getDescription() { return description; }
public final void setDescription(String d) { this.description = d; }
}
and I have the jaxb.properties file in the com/me/entities along with the class files. The properties file has
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
in it. I'm sure it gets loaded successfully since if I replace the factory class name with some nonsense the app breaks down. Also, explicitly marshaling the file creating the JAXBContext on my own works fine, so the problem seems related to jersey. According to this my setup is fine, but when my jersey resource returns an instance of MyBean
...
#GET #Produces(MediaType.TEXT_XML)
public MyBean getMyBean() {
MyBean b = new MyBean();
b.setDescription("Some blurb plenty of invalid chars like <<< && >>>");
return b;
}
what I get back has no CDATA in it, but looks like
<?xml version="1.0" encoding="UTF-8"?>
<info><longdescription>Some blurb plenty of invalid chars like <<< && >>></longdescription></info>
What am I doing wrong?
Looks like the problem was my application server: I am running this with WebLogic 10.3.5 in development mode, which comes with a lot of common libraries pre-installed that in the default configuration take precedence over those deployed in the webapp WEB-INF/lib folder.
To fix this a weblogic specific application description is needed, just create a weblogic.xml file inside WEB-INF containing the prefer-web-inf-classes option. The file I used is this:
<?xml version='1.0' standalone='yes'?>
<weblogic-web-app>
<container-descriptor>
<prefer-web-inf-classes>true</prefer-web-inf-classes>
</container-descriptor>
</weblogic-web-app>
I still have no idea which library was the problem though, anyone knows feel free to edit this answer.
Please download Jaxb Extension:
This is Eclipselink open source extension for Jaxb.
Get jar file: eclipselink.jar copy into Project lib.
http://www.eclipse.org/eclipselink/downloads/
EclipseLink 2.4.1 Installer Zip (37 MB)
And see example at:
http://theopentutorials.com/tutorials/java/jaxb/jaxb-marshalling-and-unmarshalling-cdata-block-using-eclipselink-moxy/
Good look!.
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?
I'm using JAXB in a web service with some slightly complex objects. One of the objects, Sensor, has a list of other objects it can communicate with, which necessarily can include itself (behavior that cannot be changed), leading to a cyclic reference during marshalling to XML.
#XmlAccessorType(XmlAccessType.FIELD)
public class Sensor extends BaseObject {
private ArrayList<SensorCommLink> sensorCommLinks;
}
#XmlAccessorType(XmlAccessType.FIELD)
public class SensorCommLink {
#XmlIDREF
private BaseObject receiver;
#XmlIDREF
private Sensor cueingSensor;
}
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class BaseObject {
#XmlElement
#XmlID
private String id;
}
As shown above I solved this using #XmlIDREF and #XmlID and it works very nicely.
The client-side code generated via wsimport marshals the objects to XML and the server is able to unmarshal them perfectly.
The problem I'm experiencing is that for some reason on the server side I am getting a cyclic reference exception when I try to marshal a Sensor object. The maddening part is that the server-side code contains the JAXB annotations that are used by wsimport to create the client-side code, which works great, yet I can't marshal server-side Sensors due to the cycle.
I tried copying all of the extra annotations JAXB adds to the client-side code onto the server-side classes thinking perhaps there was a runtime bug in JAXB that was preventing it from properly applying the #XmlIDREF annotation. No luck there.
Perhaps there's something very basic I'm missing here but this issue is driving me a little batty and I'm at a dead stop while I try to figure it out.
One thing I did notice that I'm investigating is that some of the namespaces on the generated client-side objects aren't what I expected, though the code works. I'm curious to see if somehow a namespace issue on the server is causing the IDREF marshalling to bomb.
Any chance on the server side it is processing properties (get/set) instead of fields (instance variables). You can enforce field access in the following way:
#XmlAccessorType(XmlAccessType.FIELD)
public class SensorCommLink {
#XmlIDREF
private BaseObject receiver;
#XmlIDREF
private Sensor cueingSensor;
}
Or you could annotate the get methods.