0

I'm new to JPA and trying to pass a JSON string to the web service that is backed by JPA entities. However, I'm having a hard time figuring out the case/naming convention. The following is the structure in my entity, but when I pass my JSON statement, it wants "userId" and "enrollmentstatus" (note case difference) or it generates an error about "Unrecognized field 'enrollmentStatus'". For one it's insisting on camelCase and for the other field all lowercase. It doesn't seem to be consistent. What am I missing or misunderstanding here? Thank you in advance.

@Basic(optional = false)
@NotNull
@Column(name = "USERID")
private long userid;

@Basic(optional = false)
@NotNull
@Size(min = 1, max = 1)
@Column(name = "ENROLLMENTSTATUS")
private String enrollmentstatus;

Error message below:

javax.servlet.ServletException: org.glassfish.jersey.server.ContainerException: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "enrollmentStatus" (Class entities.LearningActivity), not marked as ignorable
 at [Source: org.glassfish.jersey.message.internal.EntityInputStream@344a5b; line: 1, column: 75] (through reference chain: entities.LearningActivity["enrollmentStatus"])

root cause

org.glassfish.jersey.server.ContainerException: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "enrollmentStatus" (Class entities.LearningActivity), not marked as ignorable
 at [Source: org.glassfish.jersey.message.internal.EntityInputStream@344a5b; line: 1, column: 75] (through reference chain: entities.LearningActivity["enrollmentStatus"])

JSON String:

{"mobile":0,"userId":12345,"learngingActivityId":134,"enrollmentStatus":"C","dateTimeEnrolled":"2014-03-20T00:08:30.18375Z","dateTimeCompleted":"2014-03-20T00:08:30.18375Z","hoursSpent":0.0,"score":13.0,"passed":false,"instructorNotes":"u0000","studentComments":"u0000","internalData":"u0000"}

EntityFacade Code:

@POST
@Override
@Consumes({"application/xml", "application/json"})
@Path("add")
public void create(LearningActivity entity) {
    super.create(entity);
}

Log file:

[2014-03-20T08:01:51.777-0700] [glassfish 4.0] [FINE] [] [org.glassfish.jersey.server.wadl.internal.generators.WadlGeneratorJAXBGrammarGenerator] [tid: _ThreadID=111 _ThreadName=http-listener-1(3)] [timeMillis: 1395327711777] [levelValue: 500] [CLASSNAME: org.glassfish.jersey.server.wadl.internal.generators.WadlGeneratorJAXBGrammarGenerator$6] [METHODNAME: resolve] [[

java.lang.InstantiationException
    at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.glassfish.jersey.server.wadl.internal.generators.WadlGeneratorJAXBGrammarGenerator$6.resolve(WadlGeneratorJAXBGrammarGenerator.java:420)
    at org.glassfish.jersey.server.wadl.WadlGenerator$ExternalGrammarDefinition.resolve(WadlGenerator.java:179)
    at org.glassfish.jersey.server.wadl.internal.ApplicationDescription.resolve(ApplicationDescription.java:82)
    at org.glassfish.jersey.server.wadl.internal.generators.WadlGeneratorJAXBGrammarGenerator.attachTypes(WadlGeneratorJAXBGrammarGenerator.java:481)
    at org.glassfish.jersey.server.wadl.internal.WadlBuilder.generate(WadlBuilder.java:149)
    at org.glassfish.jersey.server.wadl.internal.WadlApplicationContextImpl.getApplication(WadlApplicationContextImpl.java:153)
    at org.glassfish.jersey.server.wadl.processor.WadlModelProcessor$OptionsHandler.apply(WadlModelProcessor.java:134)
    at org.glassfish.jersey.server.wadl.processor.WadlModelProcessor$OptionsHandler.apply(WadlModelProcessor.java:118)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:125)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ObjectOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:167)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:91)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:346)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:341)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:101)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:224)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:198)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:946)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:323)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:372)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:335)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:218)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:344)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
    at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:393)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
    at java.lang.Thread.run(Thread.java:724)
]]

[2014-03-20T08:01:51.779-0700] [glassfish 4.0] [FINE] [] [org.glassfish.jersey.server.wadl.internal.generators.WadlGeneratorJAXBGrammarGenerator] [tid: _ThreadID=111 _ThreadName=http-listener-1(3)] [timeMillis: 1395327711779] [levelValue: 500] [CLASSNAME: org.glassfish.jersey.server.wadl.internal.generators.WadlGeneratorJAXBGrammarGenerator] [METHODNAME: attachTypes] [[
  Couldn't find JAX-B element for class javax.ws.rs.core.Response]]
Trebor
  • 793
  • 3
  • 11
  • 37
  • Might be an issue with the webservice... What casing does the JSON string contain? enrollmentStatus or enrollmentstatus ? And how do you read the JSON string? Jackson? – fvu Mar 20 '14 at 01:35
  • Sorry. You asked how do I read the JSON string. I haven't even gotten that far yet. I just have the following annotations. POST Override Consumes({"application/xml", "application/json"}) Path("add") public void create(LearningActivity entity) { super.create(entity); } – Trebor Mar 20 '14 at 01:37
  • I'm using Jersey (javax.ws.rs.Consumes). I've edited my JSON string from the client side and tried various case structures using a simple HTTP Post routine in Netbeans test-resbeans.html tester. That was how I saw JPA kicking back errors about the case. – Trebor Mar 20 '14 at 01:42
  • I must admit I never used that webservice tester thingie in Netbeans, so I won't comment on that, but I have a hard time believing some element in the chain changes identifier case just like that - I'd recheck all participating code and declarations to see whether there's some definition where another form was used. Anyways, JPA is most probably not involved in this issue I think - can you add a verbatim copy of the errors or stacktrace you're getting? – fvu Mar 20 '14 at 01:53
  • FVU, I knew I received a different error after matching all of the cases so thinking they might be related, I just changed my case to not give me the "not found" error. Now it generates "The request sent by the client was syntactically incorrect." I'm wondering if the two are related. I've also included my stack trace in the original post in the event it sheds light. – Trebor Mar 20 '14 at 15:31
  • I think I know what's going on now, let me check a couple of things. – fvu Mar 20 '14 at 15:34

2 Answers2

3

The original error message is a good indication of what's going on:

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "enrollmentStatus" (Class entities.LearningActivity), not marked as ignorable

which is caused by the case difference. Jackson is the JSON serializer/deserializer use by GF and it tries to perform a case sensitive match between what it finds in the JSON file and the entity class - and fails. I see three possible solutions:

The easiest one, change the entity field's name to match the JSON case

Alternatively, just add a @JsonProperty annotation to tell Jackson that entity field enrollmentstatus should map to JSON field enrollmentStatus

@Basic(optional = false)
@NotNull
@Size(min = 1, max = 1)
@Column(name = "ENROLLMENTSTATUS")
@JsonProperty("enrollmentStatus")
private String enrollmentstatus;

Last but I think it's a bit messy, provide a getter and setter for the field using the JSON-side casing

public String getEnrollmentStatus() {
    return enrollmentstatus;
}

public void setEnrollmentStatus(String value) {
    this.enrollmentstatus = value;
}

Regarding that stacktrace, I see no obvious connection between the case issue and that problem (but one never knows). If the error doesn't go away one you have fixed the case issue it might be worthwhile to try disabling WADL generation unless you really need that feature. The procedure is explained in the Jersey documentation, the essence is

WADL generation is enabled in Jersey by default. This means that OPTIONS methods are added by default to each resource and an auto-generated /application.wadl resource is deployed too. To override this default behavior and disable WADL generation in Jersey, setup the configuration property in your application:

jersey.config.server.wadl.disableWadl=true

This property can be setup in a web.xml if the Jersey application is deployed in the servlet with web.xml or the property can be returned from the Application. getProperties(). See Deployment chapter for more information on setting the application configuration properties in various deployments.

Community
  • 1
  • 1
fvu
  • 32,488
  • 6
  • 61
  • 79
  • FVU, thank you for all of your help on this. I appreciate it. I actually changed the JSON's case as it was just as easy. I just realized that there may be another clue in all of this. All of the fields require all lower case json fields except two that I added after Netbeans generated the files. "userId" and "learningActivityId" both require camel case and yet both of those use lower case in the Java entity class like the rest of the Column entities. Aside from the LearningActivity.java and LearningActivityFacadeREST.java files, is there possibly another file that I need to modify? – Trebor Mar 20 '14 at 16:32
  • Maybe some autogenerated file, so it's probably a good idea to perform a "Clean and build" first. Also, to avoid all traces of code that may have gotten stuck in your Glassfish instance, manually undeploy the project, restart Glassfish (and maybe Netbeans) and redeploy. Especially under Windows Netbeans sometimes has issues with replacing or erasing files when things go wrong, so cleaning up manually may help. – fvu Mar 20 '14 at 16:42
  • Thank you. Been there and done that a couple of times, but I'll try it again. – Trebor Mar 20 '14 at 18:42
  • If all else fails, do a recursive search on all files in the project folder to see where the variable might be hiding. – fvu Mar 20 '14 at 18:51
  • Alternatively - but sometimes easier especially if the project was used a lot for all kinds of experiments is to start over with a completely new project, transplant known-good code and regenerate the rest. – fvu Mar 20 '14 at 18:56
  • I've managed to debug this a little more and find that it is choking at getEntityManager().persist(entity) in the AbstractFacade class. I think it might be choking on my the primary key. Is there a way to tell it to let Oracle provide the PK instead of JPA providing it? I've looked at several articles that indicate that I might be able to use GeneratedValue but they all seem to imply that this would create a new id generation scheme. I already have a built in Oracle sequence trigger to generate my PKs. I still haven't found the reason for the camel case variance.. Thank you. – Trebor Mar 21 '14 at 20:10
  • Solved it, finally. Just about when I was ready to completely give up on JPA all together. I found a solution partly in http://stackoverflow.com/questions/2595124/java-jpa-generators-sequencegenerator using SequenceGenerator to point to my existing Oracle sequence generator. This and changing some of the strings passed in my JSON string allowed my POST to actually complete. Thank you for all of your help. I'm not sure we solved the camelCase issue yet, but at least it's not blowing up in my face. – Trebor Mar 21 '14 at 20:37
0

Inconsistent case solved at last. It appears that the case of the JSON field name must match the getter/setter functions not the actual variable/field names. In my case, I had added two fields to the entity manually, but had used camel case for the the get/set names. I.e., getLearningActivityId and setLearningActivityId, getUserId and setUserId. As soon as I changed my get/set methods to setLearningactivityid and getLearningactivityid everything was consistent. The clue came when I noticed that format of all of the get/set methods Netbeans had created followed a case where only the first letter after get/set was capitalized. So apparently, Jackson grabs the name following the first three letters of the get/set methods (including the case) and uses the right hand portion of the method name as the name to match against and to create the JSON string with.

Trebor
  • 793
  • 3
  • 11
  • 37