RESTEasy doesn't recognize custom message body writer

java, jax-rs, resteasy

Solution

Based on your code above it would appear that your `CSVMessageBodyWriter` is being called, but it is failing your `isWriteable` check.

Your `isWriteable` check is always going to return false. You are checking if `JaxbList` is assignable from `CSVSerializer`. This is always going to fail and your `CSVMessageBodyWriter` will not be deemed able to handle `text/csv`.

Try changing your isWriteable method to the following:

public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) 
{
    return true;
}

This will serialize all `text/csv` annotated methods. If you want to constrain it to `JaxbList` then you can do something like this:

public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) 
{
    ParameterizedType paramType = (ParameterizedType) genericType;

    if(paramType.getRawType().equals(JaxbList.class))
    {
        return true;
    }

    return false;
}

Here is a simple working example of configuring a custom `MessageBodyWriter`:

Application

public class ProductGuideApplication extends Application
{
    private Set<Object> singletons = new HashSet<Object>();
    private Set<Class<?>> classes = new HashSet<Class<?>>();

    public ProductGuideApplication() 
    { 
        singletons.add(new CSVMessageBodyWriter());
        classes.add(FooResource.class);
    }

    @Override
    public Set<Object> getSingletons()
    { 
        return singletons;
    }

    @Override
    public Set<Class<?>> getClasses() 
    {
        return classes;
    }
}

Resource

@Path("/foo")
public class FooResource
{
    @GET
    @Produces("text/csv")
    public List<Consumer> getConsumers()
    {
        Consumer consumer1 = new Consumer();
        consumer1.setId("1234");
        consumer1.setGender("Male");

        Consumer consumer2 = new Consumer();
        consumer2.setId("2345");
        consumer2.setGender("Male");

        List<Consumer> consumers = new ArrayList<Consumer>();
        consumers.add(consumer1);
        consumers.add(consumer2);

        return consumers;
    }
}

MessageBodyWriter

@Provider
@Produces("text/csv")
public class CSVMessageBodyWriter implements MessageBodyWriter<List<Consumer>>
{
    @Override
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) 
    {
        ParameterizedType paramType = (ParameterizedType) genericType;

        if(paramType.getRawType().equals(List.class))
        {
            if(paramType.getActualTypeArguments()[0].equals(Consumer.class))
            {
                return true;
            }
        }

        return false;
    }

    @Override
    public long getSize(List<Consumer> t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) 
    {
        return 0;
    }

    @Override
    public void writeTo(List<Consumer> t, Class<?> type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap<String, Object> httpHeaders,
            OutputStream entityStream) throws IOException,
            WebApplicationException 
    {
        //Write your CSV to entityStream here.
    }
}

Problem

My MessageBodyWriter ``` @Provider @Produces("text/csv") public class CSVMessageBodyWriter implements MessageBodyWriter<JaxbList> public static final String CONTENT_DISPOSITION_HEADER = "Content-Disposition"; //$NON-NLS-1$ private final static HeaderDelegate<ContentDispositionHeader> header = RuntimeDelegate.getInstance().createHeaderDelegate(ContentDispositionHeader.class); public long getSize(JaxbList t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return CsvSerializer.class.isAssignableFrom(type); } public void writeTo(JaxbList t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { // set content disposition. This will enable browsers to open excel ContentDispositionHeader contentDispositionHeader = ContentDispositionHeader.createContentDispositionHeader(MediaTypeUtils.CSV_TYPE); contentDispositionHeader.setFileName("representation"); //$NON-NLS-1$ httpHeaders.putSingle(CONTENT_DISPOSITION_HEADER, header.toString(contentDispositionHeader)); Charset charset = Charset.forName(ProviderUtils.getCharset(mediaType)); OutputStreamWriter writer = new OutputStreamWriter(entityStream, charset); PrintWriter printWriter = new PrintWriter(writer); Iterator<String[]> rows = ((CsvSerializer) t).getEntities(); while (rows.hasNext()) { printWriter.println(CsvWriter.getCSVRow(rows.next())); } printWriter.flush(); } } ``` My REST Application ``` @Path("app/v3") @GZIP @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, "text/csv"}) public class ApplicationREST ``` Extension of Application ``` @ApplicationPath("/") public class JaxRsActivator extends Application { private Set<Object> singletons = new HashSet<Object>(); private Set<Class<?>> classes = new HashSet<Class<?>>(); public JaxRsActivator() { singletons.add(new CSVMessageBodyWriter()); classes.add(ApplicationREST.class); } @Override public Set<Object> getSingletons() { Set<Object> defaults = super.getSingletons(); singletons.addAll(defaults); return singletons; } public Set<Class<?>> getClasses() { return classes; } } ``` When I run in debug mode I can hit my JaxRsActivator class, so I know that the providers are being loaded. However I get the error "Could not find MessageBodyWriter for response object of type: net.comp.jaxb.JaxbList of media type: text/csv"

Original source