开发者

JBoss RESTeasy JAX-RS JAXB schema validation with Decorator

开发者 https://www.devze.com 2023-04-07 18:28 出处:网络
I am trying to validate all incoming XML files that come over my (contract first) REST interface in an application running inside JBoss AS 7. I have written a @Decorator for Pretty-Printing (as by the

I am trying to validate all incoming XML files that come over my (contract first) REST interface in an application running inside JBoss AS 7. I have written a @Decorator for Pretty-Printing (as by the example in the JBoss RESTeasy documentation) and an analogous one for switching on XML schema validation for the unmarshaller. Unfortunately, the decorator for the unmarshaller is never called.

Here is the code for the Pretty Decorator:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.xml.bind.Marshaller;

import org.jboss.resteasy.annotations.Decorator;

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Decorator(processor = PrettyProcessor.class, target = Marshaller.class)
public @interface Pretty {}

And the implementation:

import java.lang.annotation.Annotation;

import javax.ws.rs.core.MediaType;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;

import org.apache.log4j.Logger;
import org.jboss.resteasy.annotations.DecorateTypes;
import org.jboss.resteasy.spi.interception.Dec开发者_开发技巧oratorProcessor;

@DecorateTypes({ "text/*+xml", "application/*+xml", MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public class PrettyProcessor implements DecoratorProcessor<Marshaller, Pretty> {
    private static final Logger LOGGER = Logger.getLogger(PrettyProcessor.class);

    @Override
    public Marshaller decorate(Marshaller target, Pretty annotation, Class type, Annotation[] annotations, MediaType mediaType) {
        LOGGER.debug("Pretty.");
        try {
            target.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        } catch (PropertyException e) {
        }
        return target;
    }
}

Now the annotation for the Validate (that does not work):

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.xml.bind.Unmarshaller;

import org.jboss.resteasy.annotations.Decorator;

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Decorator(processor = ValidateProcessor.class, target = Unmarshaller.class)
public @interface Validate {}

The implementation:

import java.lang.annotation.Annotation;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
import javax.xml.XMLConstants;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.apache.log4j.Logger;
import org.jboss.resteasy.annotations.DecorateTypes;
import org.jboss.resteasy.spi.interception.DecoratorProcessor;
import org.xml.sax.SAXException;

@DecorateTypes({ "text/*+xml", "application/*+xml", MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public class ValidateProcessor implements DecoratorProcessor<Unmarshaller, Validate> {
    private static final Logger LOGGER = Logger.getLogger(ValidateProcessor.class);

    @Override
    public Unmarshaller decorate(Unmarshaller target, Validate annotation,  Class type, Annotation[] annotations, MediaType mediaType) {
        target.setSchema(getSchema());
        LOGGER.debug("Set validation schema.");
        System.out.println("Set validation schema.");
        return target;
    }
}

And the code of the REST interface:

import javax.annotation.security.RolesAllowed;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.xml.ws.WebServiceException;

@Path("/{mdvt}/{ouid}/order")
public interface OrderResource {

    @RolesAllowed({ "mpa" })
    @POST
    @Path("/update")
    @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML})
    @Produces(MediaType.APPLICATION_XML)
    @Pretty
    public Response update(@Context SecurityContext sec,
            @PathParam("ouid") String ouID, 
            @PathParam("mdvt") long masterDataVersionTag,
            @Validate UpdateOrdersRequest uor) throws WebServiceException;
}

On the same REST method, (update) the @Pretty Decorator gets called while the @Validate does not. What am I doing wrong here?

I have found an old bug Decorator for Jaxb unmarshaller doesn't work which is closed.

The comments there say that everything works, and the code above is almost exactly the one from there, including the solution. Nevertheless, nothing works for the Unmarshaller.


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,


You could implement a MessageBodyReader that sets an Schema on the JAXB Unmarshaller to perform validation. For sample code check out my answer to a similar question:

  • Validate JAXBElement in JPA/JAX-RS Web Service
0

精彩评论

暂无评论...
验证码 换一张
取 消