2012-08-27 16 views
6

RESTEasy mock framework funziona bene senza mapping di eccezioni - la richiesta viene ricevuta e l'entità viene restituita con i contenuti previsti.RESTEasy Mock vs. Exception Mapper vs. Contesto

Dopo la registrazione eccezione mapper e costringendo un'eccezione, chiamata non riesce quando interiora di RESTEasy chiamano ResteasyProviderFactory.getContextData (tipo), che restituisce null, con conseguente messaggio di errore imprevisto: "Impossibile trovare i dati contestuali di tipo: javax.servlet. http.HttpServletRequest".

Non è stato possibile trovare alcun esempio da nessuna parte online di RESTEasy mock più un mappatore di eccezioni e non è stato possibile trovare nulla di utile sull'errore.

classe Client: fabbrica

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; 
    } 
} 

Oggetto:

package com.foo; 

import javax.xml.bind.annotation.XmlRegistry; 

@XmlRegistry 
public class ObjectFactory { 

    public ObjectFactory() { 
    } 

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

convalida eccezione: endpoint

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); 
    } 
} 

Servizio:

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; 
    } 
} 

Eccezione mapper:

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(); 
    } 
} 

prova:

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); 
    } 
} 

Errore:

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) 

risposta

11

Il problema è che il 'quadro finta' non fornisce un HttpServletRequest al contesto. Non è direttamente correlato allo ExceptionMapper.

Resteasy injects @Context campi in base ai dati presenti nello ResteasyProviderFactory.getContextDataMap(). Quando questa mappa non contiene un'istanza di HttpServletRequest, si fa saltare in aria whith l'errore 'Impossibile trovare i dati contestuali di tipo ...'

Ho lavorato intorno a questo mettendo un finto HttpServletRequest nel contesto RESTEasy:

Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); 
dispatcher.getRegistry().addSingletonResource(new Service()); 

ResteasyProviderFactory 
     .getContextDataMap() 
     .put(HttpServletRequest.class, new MockHttpServletRequest()); 

MockHttpResponse response = new MockHttpResponse(); 

dispatcher.invoke(
    MockHttpRequest.get("/"), 
    response 
); 
+1

Grazie per la risposta! Questo è molto utile. Ti avrei ringraziato un anno fa, ma la notifica via email è stata inviata all'email sbagliata e non ho visto il tuo messaggio fino ad ora. – user1461450

Problemi correlati