RESTEasy Mock vs. Mapper wyjątków vs. Kontekst

RESTEasy mock framework działa bez mapera wyjątków - żądanie jest odbierane, a obiekt jest zwracany z oczekiwaną zawartością.

Po zarejestrowaniu programu odwzorowującego wyjątki i wymuszeniu wyjątku wywołanie kończy się niepowodzeniem, gdy wtyczka wywołania RESTEasy ResteasyProviderFactory.getContextData (typ) zwraca wartość null, co powoduje nieoczekiwany komunikat o błędzie: „Nie można znaleźć danych kontekstowych typu: javax.servlet.http.HttpServletRequest” .

Nie można znaleźć żadnych przykładów nigdzie w internecie z makiety RESTEasy i programu odwzorowującego wyjątki, a także nie można znaleźć niczego użytecznego w przypadku błędu.

Klasa klienta:

package com.foo;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "foo-type", propOrder = {
    "name"
})
@XmlRootElement(name = "foo")
public class Foo {

    protected String name;

    public String getName() {
        return name;
    }

    public void setName(String value) {
        this.name = value;
    }
}

Fabryka obiektów:

package com.foo;

import javax.xml.bind.annotation.XmlRegistry;

@XmlRegistry
public class ObjectFactory {

    public ObjectFactory() {
    }

    public Foo createFoo() {
        return new Foo();
    }
}

Wyjątek walidacji:

package com.foo;

public class ValidationException extends RuntimeException {

    private static final long serialVersionUID = -8100360206713223313L;

    public ValidationException(String message) {
        super(message);
    }

    public ValidationException(Exception innerException) {
        super(innerException);
    }

    public ValidationException(String message, Exception innerException) {
        super(message, innerException);
    }
}

Punkt końcowy usługi:

package com.foo;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/rest/v1")
public class FooService {

    @GET
    @Path("/foo")
    @Produces("application/xml")
    public Foo alwaysBlowUp() throws ValidationException {
        if (System.currentTimeMillis() > 0) {
            throw new ValidationException("FOO");
        }
        return null;
    }
}

Mapowanie wyjątków:

package com.foo;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class FooExceptionMapper implements ExceptionMapper<ValidationException> {

    @Context
    private static HttpServletRequest request;
    @Context
    private static HttpHeaders headers;

    @Override
    public Response toResponse(ValidationException exception) {
        MediaType mediaType = null;

/*
Set breakpoint on line below.
Step over line and you get the exception in the logs.
Step into the line and the problem is in ResteasyProviderFactory:

public static <T> T getContextData(Class<T> type)
{
    return (T) getContextDataMap().get(type); <<< type == javax.servlet.http.HttpServletRequest
}

The type is not in the map, so it returns null.

The null results in this error in ContextParameterInjector:

private class GenericDelegatingProxy implements InvocationHandler
{
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable
    {
        try
        {
            Object delegate = ResteasyProviderFactory.getContextData(type);
            if (delegate == null)
               throw new LoggableFailure("Unable to find contextual data of type: " + type.getName()); <<< ERROR IN LOGS
*/

        String acceptHeader = request.getHeader("accept");

        if (MediaType.APPLICATION_XML.equals(acceptHeader)) {
            mediaType = MediaType.APPLICATION_XML_TYPE;
        } else if (MediaType.APPLICATION_JSON.equals(acceptHeader)) {
            mediaType = MediaType.APPLICATION_JSON_TYPE;
        } else {
            mediaType = headers.getMediaType();

            if (mediaType == null) {
                mediaType = MediaType.APPLICATION_XML_TYPE;
            }
        }

        ResponseBuilder builder = Response.status(Status.BAD_REQUEST);
        builder.type(mediaType);
        return builder.build();
    }
}

Test:

package com.foo;

import java.net.URISyntaxException;

import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory;

public final class TestFooExceptionMapper {

    public static void main(String[] args) throws URISyntaxException {
        Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
        dispatcher.getRegistry().addResourceFactory(new POJOResourceFactory(FooService.class));
        dispatcher.getProviderFactory().addExceptionMapper(FooExceptionMapper.class);

        MockHttpRequest request = MockHttpRequest.get("/rest/v1/foo");
        MockHttpResponse response = new MockHttpResponse();
        dispatcher.invoke(request, response);
    }
}

Błąd:

Aug 26, 2012 10:44:26 PM org.jboss.resteasy.core.SynchronousDispatcher 
SEVERE: Failed executing GET /rest/v1/foo
org.jboss.resteasy.spi.LoggableFailure: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest
    at org.jboss.resteasy.core.ContextParameterInjector$GenericDelegatingProxy.invoke(ContextParameterInjector.java:56)
    at $Proxy18.getHeader(Unknown Source)
    at com.foo.FooExceptionMapper.toResponse(FooExceptionMapper.java:51)
    at com.foo.FooExceptionMapper.toResponse(FooExceptionMapper.java:1)
    at org.jboss.resteasy.core.SynchronousDispatcher.executeExceptionMapper(SynchronousDispatcher.java:330)
    at org.jboss.resteasy.core.SynchronousDispatcher.unwrapException(SynchronousDispatcher.java:359)
    at org.jboss.resteasy.core.SynchronousDispatcher.handleApplicationException(SynchronousDispatcher.java:348)
    at org.jboss.resteasy.core.SynchronousDispatcher.handleException(SynchronousDispatcher.java:220)
    at org.jboss.resteasy.core.SynchronousDispatcher.handleInvokerException(SynchronousDispatcher.java:196)
    at org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:551)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:513)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:125)
    at com.foo.TestFooExceptionMapper.main(TestFooExceptionMapper.java:20)

questionAnswers(1)

yourAnswerToTheQuestion