title: Custom Jackson HttpMessageConverter no longer works in Spring 4.2
body: <p>I am updating an application from Spring Platform version 1.1.3.RELEASE to 2.0.1.RELEASE, which bumps the Spring Framework version from 4.1.7 to 4.2.4, and Jackson from 2.4.6 to 2.6.4.  There does not seem to have been any significant changes in Spring or Jackson's handling of custom <code>HttpMessageConverter</code> implementations, but my custom JSON serialization is failing to occur, and I have not been able to determine why.  The following works fine in the previous Spring Platform release:</p>
  
  <p><strong>Model</strong></p>
  
  <pre><code>@JsonFilter("fieldFilter")
  public class MyModel { 
      /*model fields and methods*/ 
  }
  </code></pre>
  
  <p><strong>Model wrapper</strong></p>
  
  <pre><code>public class ResponseEnvelope {
  
      private Set&lt;String&gt; fieldSet;
      private Set&lt;String&gt; exclude;
      private Object entity;
  
      public ResponseEnvelope(Object entity) {
          this.entity = entity;
      }
  
      public ResponseEnvelope(Object entity, Set&lt;String&gt; fieldSet, Set&lt;String&gt; exclude) {
          this.fieldSet = fieldSet;
          this.exclude = exclude;
          this.entity = entity;
      }
  
      public Object getEntity() {
          return entity;
      }
  
      @JsonIgnore
      public Set&lt;String&gt; getFieldSet() {
          return fieldSet;
      }
  
      @JsonIgnore
      public Set&lt;String&gt; getExclude() {
          return exclude;
      }
  
      public void setExclude(Set&lt;String&gt; exclude) {
          this.exclude = exclude;
      }
  
      public void setFieldSet(Set&lt;String&gt; fieldSet) {
          this.fieldSet = fieldSet;
      }
  
      public void setFields(String fields) {
          Set&lt;String&gt; fieldSet = new HashSet&lt;String&gt;();
          if (fields != null) {
              for (String field : fields.split(",")) {
                  fieldSet.add(field);
              }
          }
          this.fieldSet = fieldSet;
      }
  }
  </code></pre>
  
  <p><strong>Controller</strong></p>
  
  <pre><code>@Controller
  public class MyModelController {
  
      @Autowired MyModelRepository myModelRepository;
  
      @RequestMapping(value = "/model", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
      public HttpEntity find(@RequestParam(required=false) Set&lt;String&gt; fields, @RequestParam(required=false) Set&lt;String&gt; exclude){
          List&lt;MyModel&gt; objects = myModelRepository.findAll();
          ResponseEnvelope envelope = new ResponseEnvelope(objects, fields, exclude);
          return new ResponseEntity&lt;&gt;(envelope, HttpStatus.OK);
      }
  }
  </code></pre>
  
  <p><strong>Custom <code>HttpMessageConverter</code></strong></p>
  
  <pre><code>public class FilteringJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
  
      private boolean prefixJson = false;
  
      @Override
      public void setPrefixJson(boolean prefixJson) {
          this.prefixJson = prefixJson;
          super.setPrefixJson(prefixJson);
      }
  
      @Override
      protected void writeInternal(Object object, HttpOutputMessage outputMessage)
              throws IOException, HttpMessageNotWritableException {
  
          ObjectMapper objectMapper = getObjectMapper();
          JsonGenerator jsonGenerator = objectMapper.getFactory().createGenerator(outputMessage.getBody());
  
          try {
  
              if (this.prefixJson) {
                  jsonGenerator.writeRaw(")]}', ");
              }
  
              if (object instanceof ResponseEnvelope) {
  
                  ResponseEnvelope envelope = (ResponseEnvelope) object;
                  Object entity = envelope.getEntity();
                  Set&lt;String&gt; fieldSet = envelope.getFieldSet();
                  Set&lt;String&gt; exclude = envelope.getExclude();
                  FilterProvider filters = null;
  
                  if (fieldSet != null &amp;&amp; !fieldSet.isEmpty()) {
                      filters = new SimpleFilterProvider()
                              .addFilter("fieldFilter", SimpleBeanPropertyFilter.filterOutAllExcept(fieldSet))
                                  .setFailOnUnknownId(false);
                  } else if (exclude != null &amp;&amp; !exclude.isEmpty()) {
                      filters = new SimpleFilterProvider()
                              .addFilter("fieldFilter", SimpleBeanPropertyFilter.serializeAllExcept(exclude))
                                  .setFailOnUnknownId(false);
                  } else {
                      filters = new SimpleFilterProvider()
                              .addFilter("fieldFilter", SimpleBeanPropertyFilter.serializeAllExcept())
                                  .setFailOnUnknownId(false);
                  }
  
                  objectMapper.setFilterProvider(filters);
                  objectMapper.writeValue(jsonGenerator, entity);
  
              } else if (object == null){
                  jsonGenerator.writeNull();
              } else {
                  FilterProvider filters = new SimpleFilterProvider().setFailOnUnknownId(false);
                  objectMapper.setFilterProvider(filters);
                  objectMapper.writeValue(jsonGenerator, object);
              }
  
          } catch (JsonProcessingException e){
              e.printStackTrace();
              throw new HttpMessageNotWritableException("Could not write JSON: " + e.getMessage());
          }
  
      }
  }
  </code></pre>
  
  <p><strong>Configuration</strong></p>
  
  <pre><code>@Configuration
  @EnableWebMvc
  public class WebServicesConfig extends WebMvcConfigurerAdapter {
  
      @Override
      public void configureMessageConverters(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters) {
          FilteringJackson2HttpMessageConverter jsonConverter = new FilteringJackson2HttpMessageConverter();
          jsonConverter.setSupportedMediaTypes(MediaTypes.APPLICATION_JSON);
          converters.add(jsonConverter);
      }
  
      // Other configurations
  }
  </code></pre>
  
  <p>Now I am getting this exception (which is caught by Spring and logged) and a 500 error when making any sort of request: </p>
  
  <pre><code>[main] WARN  o.s.w.s.m.s.DefaultHandlerExceptionResolver - Failed to write HTTP message: 
    org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: 
    Can not resolve PropertyFilter with id 'fieldFilter'; 
    no FilterProvider configured (through reference chain:
    org.oncoblocks.centromere.web.controller.ResponseEnvelope["entity"]-&gt;java.util.ArrayList[0]); 
    nested exception is com.fasterxml.jackson.databind.JsonMappingException: 
    Can not resolve PropertyFilter with id 'fieldFilter'; 
    no FilterProvider configured (through reference chain: 
    org.oncoblocks.centromere.web.controller.ResponseEnvelope["entity"]-&gt;java.util.ArrayList[0])
  </code></pre>
  
  <p>The <code>configureMessageConverters</code> method executes, but it does not look like custom converter is ever utilized during requests.  Is it possible that another message converter could be preventing this one from reaching my response?  My understanding was that overriding <code>configureMessageConverters</code> would prevent converters other than the manually registered ones from being used.</p>
  
  <p>No changes have been made between the working and non-working versions of this code, besides updating dependency versions via the Spring Platform. Has there been any change in the JSON serialization that I am just missing in the documentation?  </p>
  
  <p><strong>Edit</strong></p>
  
  <p>Further testing yields strange results.  I wanted to test to check the following things:</p>
  
  <ol>
  <li>Is my custom <code>HttpMessageConverter</code> actually being registered?</li>
  <li>Is another converter overriding/superseding it?</li>
  <li>Is this a problem with my test setup only?</li>
  </ol>
  
  <p>So, I added an extra test and took a look at the output:</p>
  
  <pre><code>@Autowired WebApplicationContext webApplicationContext;
  
  @Before
  public void setup(){
      mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
  }
  
  @Test
  public void test() throws Exception {
      RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) webApplicationContext.getBean("requestMappingHandlerAdapter");
      List&lt;EntrezGene&gt; genes = EntrezGene.createDummyData();
      Set&lt;String&gt; exclude = new HashSet&lt;&gt;();
      exclude.add("entrezGeneId");
      ResponseEnvelope envelope = new ResponseEnvelope(genes, new HashSet&lt;String&gt;(), exclude);
      for (HttpMessageConverter converter: adapter.getMessageConverters()){
          System.out.println(converter.getClass().getName());
          if (converter.canWrite(ResponseEnvelope.class, MediaType.APPLICATION_JSON)){
              MockHttpOutputMessage message =  new MockHttpOutputMessage();
              converter.write((Object) envelope, MediaType.APPLICATION_JSON, message);    
              System.out.println(message.getBodyAsString());
          }
      }
  }
  </code></pre>
  
  <p>...and it works fine.  My the <code>envelope</code> object and its contents are serialized and filtered correctly.  So either there is an issue with the request handling before it reaches the message converters, or there has been a change in how <code>MockMvc</code> is testing requests.</p>