Validate Jaxbelement in JPA/Jax-Rs Web Service

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



Leave a reply



Submit