Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PersistentEntityResourceHandlerMethodArgumentResolver dump in PUT requests with HashMap [DATAREST-491] #868

Closed
spring-projects-issues opened this issue Mar 11, 2015 · 4 comments
Assignees
Labels
type: bug

Comments

@spring-projects-issues
Copy link

@spring-projects-issues spring-projects-issues commented Mar 11, 2015

Sebastian Scheele opened DATAREST-491 and commented

When updating a existing resource with a JSON PUT with this sample code

@Document
public class Ticket {

	@Id
	private String id;

	@Version
	private Long version;

	private String type;

	private Map<String, String> texts;
}

and JSON

{
  "version" : 1,
  "type" : "HR",
  "texts" : {
    "description" : "Let's test1234"
  }
}

I get the following stack trace:

 
2015-03-11 14:34:08.049 ERROR 39988 --- [nio-8080-exec-2] s.d.r.w.AbstractRepositoryRestController : Could not read an object of type class demo.Ticket from the request!; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: Could not read payload!; nested exception is java.lang.NullPointerException

org.springframework.http.converter.HttpMessageNotReadableException: Could not read an object of type class demo.Ticket from the request!; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: Could not read payload!; nested exception is java.lang.NullPointerException
	at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.readPutForUpdate(PersistentEntityResourceHandlerMethodArgumentResolver.java:197)
	at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.read(PersistentEntityResourceHandlerMethodArgumentResolver.java:169)
	at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.resolveArgument(PersistentEntityResourceHandlerMethodArgumentResolver.java:125)
	at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
	at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:777)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:706)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
	at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:879)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
	at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: Could not read payload!; nested exception is java.lang.NullPointerException
	at org.springframework.data.rest.webmvc.json.DomainObjectReader.merge(DomainObjectReader.java:137)
	at org.springframework.data.rest.webmvc.json.DomainObjectReader.readPut(DomainObjectReader.java:129)
	at org.springframework.data.rest.webmvc.config.JsonPatchHandler.applyPut(JsonPatchHandler.java:133)
	at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.readPutForUpdate(PersistentEntityResourceHandlerMethodArgumentResolver.java:194)
	... 45 common frames omitted
Caused by: java.lang.NullPointerException: null
	at org.springframework.data.rest.webmvc.json.DomainObjectReader.getJacksonProperties(DomainObjectReader.java:204)
	at org.springframework.data.rest.webmvc.json.DomainObjectReader.doMerge(DomainObjectReader.java:157)
	at org.springframework.data.rest.webmvc.json.DomainObjectReader.doMerge(DomainObjectReader.java:185)
	at org.springframework.data.rest.webmvc.json.DomainObjectReader.merge(DomainObjectReader.java:135)
	... 48 common frames omitted

The reason seems to be that in DomainObjectReader the PersistentEntity for a HashMap is null.

public class DomainObjectReader {
	public <T> T readPut(final ObjectNode source, T target, final ObjectMapper mapper) {

		Assert.notNull(source, "ObjectNode must not be null!");
		Assert.notNull(target, "Existing object instance must not be null!");
		Assert.notNull(mapper, "ObjectMapper must not be null!");

		final PersistentEntity<?, ?> entity = entities.getPersistentEntity(target.getClass());

This works before DATAREST-461 was included.

You can find a example, to reproduce here https://github.com/sebastian-sch/mongo_data_rest


Affects: 2.2.2 (Evans SR2), 2.3 RC1 (Fowler)

Issue Links:

  • DATAREST-461 @JsonIgnored properties must not be updated in PUT requests

Referenced from: commits 8414b29, f80bebf

Backported to: 2.2.3 (Evans SR3)

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Mar 20, 2015

Oliver Drotbohm commented

That should be fixed! Thanks for reporting

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented May 27, 2015

José Carlos Valero Sánchez commented

I am using spring-data-rest 2.3.0 and It looks like this issue is not resolved. Same NPE in the same place.

I'm working with spring data Mongo.

For entity:

@Document(collection = "formDefinitions")
public class CustomFormDefinition  extends AbstractMongoDocument<CustomFormDefinition> {

    private String name;
    private String description;

    private List<AgnosticCustomFieldDefinition> fieldDefinitions;

//public accessors
}

@Configurable
public class AbstractMongoDocument<T> implements Persistable<T>{

    @Transient
    @Autowired
    private transient MongoTemplate mongoTemplate;

    @Id
    private String id;

    @CreatedDate
    private LocalDateTime createdDate = LocalDateTime.now();

    @LastModifiedDate
    public LocalDateTime modifiedDate = LocalDateTime.now();
//public accessors
}

And tests

@Test
    public void fullUpdateShouldChangeAllValues() throws Exception {
        cleanableCFD = new CustomFormDefinition();
        cleanableCFD.setName("test");
        cleanableCFD.setDescription("test desc");

        mvc.perform(
                put("/form-definitions/" + cleanableCFD.getId())
                .content("{\"id\":\""+cleanableCFD.getId()+"\",\"name\":\"updated!!\"}")
                .contentType(org.springframework.http.MediaType.APPLICATION_JSON))

                .andExpect(status().is2xxSuccessful())
        ;
        mvc.perform(get("/form-definitions/" + cleanableCFD.getId()))
                .andExpect(jsonPath("$.name", is("updated!!")))
                .andExpect(jsonPath("$.description", isEmptyOrNullString()))

                .andExpect(status().is2xxSuccessful())
        ;
    }

@Test
public void partialUpdateShouldKeepNonSpecifiedValues() throws Exception {
cleanableCFD = new CustomFormDefinition();
cleanableCFD.setName("test");
cleanableCFD.setDescription("test desc");

    mvc.perform(
            patch("/form-definitions/" + cleanableCFD.getId())
                    .content("{\"name\":\"updated!!\"}")
                    .contentType(org.springframework.http.MediaType.APPLICATION_JSON))

            .andExpect(status().is2xxSuccessful())
    ;
    mvc.perform(get("/form-definitions/" + cleanableCFD.getId()))
            .andExpect(jsonPath("$.name", is("updated!!")))
            .andExpect(jsonPath("$.description", is("test desc")))

            .andExpect(status().is2xxSuccessful())
    ;
}

{{org.springframework.http.converter.HttpMessageNotReadableException: Could not read an object of type class net.pi.components.fds.core.model.CustomFormDefinition from the request!; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: Could not read payload!; nested exception is java.lang.NullPointerException
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.readPatch(PersistentEntityResourceHandlerMethodArgumentResolver.java:183)
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.read(PersistentEntityResourceHandlerMethodArgumentResolver.java:160)
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.resolveArgument(PersistentEntityResourceHandlerMethodArgumentResolver.java:125)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:839)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:770)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:144)
at net.pi.components.fds.web.FormDefinitionEndpoints.partialUpdateShouldKeepNonSpecifiedValues(FormDefinitionEndpoints.java:174)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:224)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: Could not read payload!; nested exception is java.lang.NullPointerException
at org.springframework.data.rest.webmvc.json.DomainObjectReader.read(DomainObjectReader.java:90)
at org.springframework.data.rest.webmvc.config.JsonPatchHandler.applyMergePatch(JsonPatchHandler.java:129)
at org.springframework.data.rest.webmvc.config.JsonPatchHandler.apply(JsonPatchHandler.java:99)
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.readPatch(PersistentEntityResourceHandlerMethodArgumentResolver.java:181)
... 48 more
Caused by: java.lang.NullPointerException
at org.springframework.data.rest.webmvc.json.DomainObjectReader.getJacksonProperties(DomainObjectReader.java:204)
at org.springframework.data.rest.webmvc.json.DomainObjectReader.doMerge(DomainObjectReader.java:157)
at org.springframework.data.rest.webmvc.json.DomainObjectReader.read(DomainObjectReader.java:88)
... 51 more}}

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented May 27, 2015

José Carlos Valero Sánchez commented

Oliver, It looks like changing my mongo configuration to inheriting from AbstractMongoConfiguration also workarounded this issue

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented May 27, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug
Projects
None yet
Development

No branches or pull requests

2 participants