Validate JAXBElement in JPA/JAX-RS Web Service
You could handle this by creating a custom MessageBodyReader. The example below is based on a Customer model:
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URL;
import javax.ws.rs.Consumes;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
@Provider
@Consumes("application/xml")
public class ValidatingReader implements MessageBodyReader<Customer> {
@Context
protected Providers providers;
private Schema schema;
public ValidatingReader() {
try {
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
URL schemaURL = null;
schema = sf.newSchema(schemaURL);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
public boolean isReadable(Class<?> arg0, Type arg1, Annotation[] arg2, MediaType arg3) {
return arg0 == Customer.class;
}
public Customer readFrom(Class<Customer> arg0, Type arg1, Annotation[] arg2, MediaType arg3, MultivaluedMap<String, String> arg4, InputStream arg5)
throws IOException, WebApplicationException {
try {
JAXBContext jaxbContext = null;
ContextResolver<JAXBContext> resolver = providers.getContextResolver(JAXBContext.class, arg3);
if(null != resolver) {
jaxbContext = resolver.getContext(arg0);
}
if(null == jaxbContext) {
jaxbContext = JAXBContext.newInstance(arg0);
}
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema(schema);
return (Customer) unmarshaller.unmarshal(arg5);
} catch(JAXBException e) {
throw new RuntimeException(e);
}
}
}
Practical validation of JAXB data with JAX-RS?
If you have an XML schema then you could use JAXB validation inside a MessageBodyReader
. For a concrete example see my answer to a similar question.
- Validate JAXBElement in JPA/JAX-RS Web Service
ValidatingReader
Below is a bare bones implementation of MessageBodyReader
that does four things: 1) Create a JAXBContext
, 2) Create an instance of Schema
, 3) Sets the schema on the Unmarshaller
4) Unmarshals the InputStream
.
package org.example;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URL;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import javax.xml.XMLConstants;
import javax.xml.bind.*;
import javax.xml.validation.*;
@Provider
@Consumes("application/xml")
public class ValidatingReader implements MessageBodyReader<Customer> {
@Context
protected Providers providers;
private Schema schema;
private JAXBContext jaxbContext;
public ValidatingReader() {
try {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
URL schemaURL = null; // URL for your XML schema
schema = sf.newSchema(schemaURL);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
public boolean isReadable(Class<?> arg0, Type arg1, Annotation[] arg2, MediaType arg3) {
return arg0 == Customer.class;
}
public Customer readFrom(Class<Customer> arg0, Type arg1, Annotation[] arg2, MediaType arg3, MultivaluedMap<String, String> arg4, InputStream arg5)
throws IOException, WebApplicationException {
try {
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema(schema);
return (Customer) unmarshaller.unmarshal(arg5);
} catch(JAXBException e) {
throw new RuntimeException(e);
}
}
}
JAX-RS: Validating JSON and XML (RESTEasy)
Got this to work.
the post quoted in the question does answer this qustion.
what I was not sure was not to hook the reader to the code, but that is done automatically using the @Provider annotation.
JBoss RESTeasy JAX-RS JAXB schema validation with Decorator
Finally this problem was resolved for JBoss AS 7.
The problem lies in the Resteasy implementation which is buggy until version 2.3.5.Final.
See https://issues.jboss.org/browse/RESTEASY-711 but ignore the mentioned workaround, it does not work until version 2.3.5.
The working solution is to download the Restwasy distribution version 2.3.5.Final or above, extract it and look for resteasy-jboss-modules-2.3.5.Final.zip
Extract this file in the root of JBoss AS 7.1.1 and resteasy will be updated to the new version. After this step, all of the above code just works.
Thanks to Bill Burke to point me to the solution,
JAX-RS with Schema Validation
Turns out the servlet I was using wasn't accepting the jaxrs configuration shown above. I changed from using this:
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
to this:
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<init-param>
<param-name>config-location</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
Snippet of my applicationContext.xml
<jaxrs:server address="/">
<jaxrs:schemaLocations>
<jaxrs:schemaLocation>classpath:schema/myschema1.xsd</jaxrs:schemaLocation>
<jaxrs:schemaLocation>schema/myschema2.xsd</jaxrs:schemaLocation>
<jaxrs:schemaLocation>schema/myschema3.xsd</jaxrs:schemaLocation>
</jaxrs:schemaLocations>
<jaxrs:serviceBeans>
<bean class="my.package.endPoint1" />
<bean class="my.package.endPoint2" />
</jaxrs:serviceBeans>
<jaxrs:features>
<cxf:logging />
</jaxrs:features>
</jaxrs:server>
Schema references are from the resources directory, adjacent to WEB-INF.
Related Topics
JPA Hibernate One-To-One Relationship
Java "Lambda Expressions Not Supported at This Language Level"
How to Replace Special Characters in a String
How to Create a File in a Directory in Java
How to Get the Path of Running Java Program
Java: How to Import a Jar File from Command Line
Deployment Error:Starting of Tomcat Failed, the Server Port 8080 Is Already in Use
Technique or Utility to Minimize Java "Warm-Up" Time
How to Copy a Java.Util.List into Another Java.Util.List
What Is the Main-Stream Java Alternative to ASP.NET/Php
Consistency of Hashcode() on a Java String
Java 8 Streams Flatmap Method Example
Adding in Clause List to a JPA Query
How to Load a Large Xlsx File with Apache Poi
Java: Random Long Number in 0 <= X < N Range
Streaming Large Result Sets with MySQL
How to Terminate a Thread Blocking on Socket Io Operation Instantly