How to register a handler for an annotated JAXB class that uses an XmlAdapter to send an error message to a REST Resource client?

I searched Using BigDecimal in JAXB sorting

What happens by default is that the Exception receives the throw, but no handler listens to it. As a result, such a request reaches the JAR-RS method, but with the thad field was sent with an “invalid” value set to NULL.

I read in JAVADOC that a handler must be installed so that I can do something when such a case happens. But I can not find information on how to configure such a handler.

My endpoint looks something like this:

@Path("/path")
public class MyResource {

    @POST
    @Path("something")
    public Response postSomething(JaxbAnnotatedRequest request) {
        //processing....
    }
}
+4
2

.

" JAVADOC, , - , . 't , .

unmarshalling MessageBodyReader. ValidationEventHandler Unmarshaller, . - MessageBodyReader, , , . , , .

- MessageBodyReader .

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.Consumes;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;

@Provider
@Consumes(MediaType.APPLICATION_XML)
public class JaxbAnnotatedRequestMessageBodyReader
        implements MessageBodyReader<JaxbAnnotatedRequest> {

    JAXBContext context;

    public JaxbAnnotatedRequestMessageBodyReader() {
        try {
            context = JAXBContext.newInstance(JaxbAnnotatedRequest.class);
        } catch (JAXBException ex) {
            throw new InternalServerErrorException();
        }
    }

    @Override
    public boolean isReadable(Class<?> type, Type type1,
            Annotation[] antns, MediaType mt) {
        return JaxbAnnotatedRequest.class == type;
    }

    @Override
    public JaxbAnnotatedRequest readFrom(Class<JaxbAnnotatedRequest> type, Type type1,
            Annotation[] antns, MediaType mt, MultivaluedMap<String, String> mm,
            InputStream in) throws IOException, WebApplicationException {

        try {
            Unmarshaller unmarshaller = context.createUnmarshaller();
            unmarshaller.setEventHandler(new ValidationEventHandler() {

                @Override
                public boolean handleEvent(ValidationEvent event) {
                    String nfe = NumberFormatException.class.getCanonicalName();
                    if (nfe.equals(event.getMessage())) {
                        throw new WebApplicationException(Response.status(400)
                                .entity("Bad Number Formatting").build());
                    }
                    System.out.println(event.getMessage());
                    return false;
                }

            });
            JaxbAnnotatedRequest request
                    = (JaxbAnnotatedRequest) unmarshaller.unmarshal(in);
            return request;

        } catch (JAXBException ex) {
            throw new WebApplicationException(Response.status(400).entity(
                    "Caught JAXBEception: " + ex.toString()).build());
        }
    }
}

ValidationEventHandler Unmarshaller readFrom.

( ) Bean Validation, JAX-RS 2.0. , . - , @NotNull, , , null JAXB, - .

+4

-, , , - . . , JSON XML, , schema validation. , , . null , , .., JAXBException, , , . , ExceptionMapper, . .


Address.java

@XmlRootElement(name = "address", namespace = DataEntity.NSP)
public class Address implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 2508433924387468026L;

    private Integer id;
    private String street;
    private String number;
    private Short zipCode;
    private String city;

    protected Address() {

    }

    public Address(final Integer id, final String street, final String number,
            final Short zipCode, final String city) {
        setId(id);
        setStreet(street);
        setNumber(number);
        setZipCode(zipCode);
        setCity(city);
    }

    @XmlAttribute
    public Integer getId() {
        return id;
    }

    private void setId(Integer id) {
        ValidationHelper.validateId(id);

        this.id = id;
    }

    @XmlElement(namespace = DataEntity.NSP)
    public String getStreet() {
        return street;
    }

    private void setStreet(String street) {
        ValidationHelper.validateAddressStreet(street);

        this.street = street;
    }

    @XmlElement(namespace = DataEntity.NSP)
    public String getNumber() {
        return number;
    }

    private void setNumber(String number) {
        ValidationHelper.validateAddressNumber(number);

        this.number = number;
    }

    @XmlElement(namespace = DataEntity.NSP)
    public Short getZipCode() {
        return zipCode;
    }

    private void setZipCode(Short zipCode) {
        ValidationHelper.validateAddressZipCode(zipCode);

        this.zipCode = zipCode;
    }

    @XmlElement(namespace = DataEntity.NSP)
    public String getCity() {
        return city;
    }

    private void setCity(String city) {
        ValidationHelper.validateAddressCity(city);

        this.city = city;
    }
}

:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns="https://localhost/JAX-RS_BookStore_Service/xsd/" targetNamespace="https://localhost/JAX-RS_BookStore_Service/xsd/"
    elementFormDefault="qualified" xmlns:pref="https://localhost/JAX-RS_BookStore_Service/xsd/">
    <xs:include schemaLocation="Types.xsd" />
    <xs:element name="address" type="addressType" />
</xs:schema>

xsd:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns="https://localhost/JAX-RS_BookStore_Service/xsd/"
    targetNamespace="https://localhost/JAX-RS_BookStore_Service/xsd/"
    elementFormDefault="qualified" xmlns:pref="https://localhost/JAX-RS_BookStore_Service/xsd/">

    <xs:simpleType name="unsignedInteger">
        <xs:restriction base="xs:integer">
            <xs:minInclusive value="1" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="addressStreet">
        <xs:restriction base="xs:string">
            <xs:pattern value="[a-zA-ZöäüÖÄÜß -]{2,32}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="addressNumber">
        <xs:restriction base="xs:string">
            <xs:pattern value="[0-9]{1}[0-9a-z/\\ -]{0,7}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="addressCity">
        <xs:restriction base="xs:string">
            <xs:pattern value="[a-zA-ZöäüÖÄÜß -]{2,32}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="zipCodeMoreThan4Digits">
        <xs:restriction base="xs:short">
            <xs:minInclusive value="1000"></xs:minInclusive>
            <xs:maxInclusive value="9999"></xs:maxInclusive>
        </xs:restriction>
    </xs:simpleType>

    <xs:complexType name="addressType">
        <xs:sequence>
            <xs:element name="city" type="addressCity" />
            <xs:element name="number" type="addressNumber" />
            <xs:element name="street" type="addressStreet" />
            <xs:element name="zipCode" type="zipCodeMoreThan4Digits" />
        </xs:sequence>
        <xs:attribute name="id" type="unsignedInteger" use="required">
        </xs:attribute>
    </xs:complexType>
</xs:schema>

MessageBodyReader, . XML .

    public abstract class AbstractXmlValidationReader<T> implements
        MessageBodyReader<T> {

    private final Providers providers;
    private final Schema schema;

    /**
     * Instantiates the XML validation class (MessageBodyReader)
     * 
     * @param providers
     * @param servletContext
     * @param xsdFileName
     */
    public AbstractXmlValidationReader(final Providers providers,
            final ServletContext servletContext, final String xsdFileName) {
        this.providers = providers;

        try {
            SchemaFactory sf = SchemaFactory
                    .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            File xsd = new File(servletContext.getRealPath(xsdFileName));
            schema = sf.newSchema(xsd);
        } catch (Exception e) {
            throw new RuntimeException(
                    "Unable to create XSD validation schema", e);
        }
    }

    @Override
    public boolean isReadable(Class<?> type, Type genericType,
            Annotation[] annotations, MediaType mediaType) {

        @SuppressWarnings("unchecked")
        Class<T> readableClass = (Class<T>) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0];

        if (type == readableClass
                && type.isAnnotationPresent(XmlRootElement.class)) {
            return true;
        }
        return false;
    }

    /**
     * Source adapted from: <a href=
     * "http://stackoverflow.com/questions/3428273/validate-jaxbelement-in-jpa-jax-rs-web-service"
     * >stackoverflow.com</a>
     */
    @Override
    public T readFrom(Class<T> type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
            throws IOException, WebApplicationException {

        try {
            JAXBContext jaxbContext = null;
            ContextResolver<JAXBContext> resolver = providers
                    .getContextResolver(JAXBContext.class, mediaType);
            if (null != resolver) {
                jaxbContext = resolver.getContext(type);
            }
            if (null == jaxbContext) {
                jaxbContext = JAXBContext.newInstance(type);
            }
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            unmarshaller.setSchema(schema);

            @SuppressWarnings("unchecked")
            T entity = (T) unmarshaller.unmarshal(entityStream);
            return entity;
        } catch (JAXBException e) {
            throw new MessageBodyReaderValidationException(
                    "Failure while performing xml validation or xml marhalling!",
                    e);
        }
    }
}

MeassagBodyReader, XSD .

@Provider
@Consumes(MediaType.APPLICATION_XML)
public class AddressXmlValidationReader extends
        AbstractXmlValidationReader<Address> {

    private final static String xsdFileName = "/xsd/Address.xsd";

    public AddressXmlValidationReader(@Context Providers providers,
            @Context ServletContext servletContext) {
        super(providers, servletContext, xsdFileName);
    }
}

, , ExceptionMapper. . , sth. , HTML.

Application ! classes.add(MessageBodyReaderValidationExceptionMapper.class);

@Provider
public class MessageBodyReaderValidationExceptionMapper implements
        ExceptionMapper<MessageBodyReaderValidationException> {

    @Override
    public Response toResponse(MessageBodyReaderValidationException e) {
        return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(e.getMessage()).type(MediaType.TEXT_PLAIN).build();
    }
}

POST Firefox RESTClient.

, <street> , null !

enter image description here

+2

All Articles