xenit-eu / dynamic-extensions-for-alfresco

Rapid development of Alfresco repository extensions in Java. Deploy your code in seconds, not minutes. Life is too short for endless server restarts.
Apache License 2.0
76 stars 31 forks source link

JsonWriterResolution fails on alfresco 7 (DEVEM-486) #331

Closed RVanhuysseXenit closed 3 years ago

RVanhuysseXenit commented 3 years ago

I'm submitting a ... (check one with "x")

[X] bug report
[ ] feature request
[ ] question

Expected Behavior

The JsonWriterResolution & extending classes resolve successfully and create json responses.

Current Behavior

JsonWriterResolution fails to instantiate its internal org.json.JSONWriter with a java.lang.NoSuchMethodError on the JSONWriter constructor:

[ALFRESCO] 2021-06-28 15:41:39,308  ERROR [extensions.webscripts.AbstractRuntime] [http-nio-8080-exec-9] Exception from executeScript: java.lang.NoSuchMethodError: 'void org.json.JSONWriter.<init>(java.io.Writer)'
java.lang.RuntimeException: java.lang.NoSuchMethodError: 'void org.json.JSONWriter.<init>(java.io.Writer)'
    at com.github.dynamicextensionsalfresco.webscripts.AnnotationWebScript.translateException(AnnotationWebScript.java:354)
    at com.github.dynamicextensionsalfresco.webscripts.AnnotationWebScript.invokeExceptionHandlerMethods(AnnotationWebScript.java:328)
    at com.github.dynamicextensionsalfresco.webscripts.AnnotationWebScript.execute(AnnotationWebScript.java:79)
    at com.github.dynamicextensionsalfresco.webscripts.WebScriptProxy.execute(WebScriptProxy.java:71)
    at org.alfresco.repo.web.scripts.RepositoryContainer$3.execute(RepositoryContainer.java:527)
    at org.alfresco.repo.transaction.RetryingTransactionHelper.doInTransaction(RetryingTransactionHelper.java:450)
    at org.alfresco.repo.web.scripts.RepositoryContainer.transactionedExecute(RepositoryContainer.java:595)
    at org.alfresco.repo.web.scripts.RepositoryContainer.transactionedExecuteAs(RepositoryContainer.java:664)
    at org.alfresco.repo.web.scripts.RepositoryContainer.executeScriptInternal(RepositoryContainer.java:435)
    at org.alfresco.repo.web.scripts.RepositoryContainer.executeScript(RepositoryContainer.java:315)
    at org.springframework.extensions.webscripts.AbstractRuntime.executeScript(AbstractRuntime.java:399)
    at org.springframework.extensions.webscripts.AbstractRuntime.executeScript(AbstractRuntime.java:210)
    at org.springframework.extensions.webscripts.servlet.WebScriptServlet.service(WebScriptServlet.java:132)
    at org.alfresco.repo.web.scripts.AlfrescoWebScriptServlet.service(AlfrescoWebScriptServlet.java:43)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.extensions.webscripts.servlet.SecurityHeadersFilter.doFilter(SecurityHeadersFilter.java:177)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.alfresco.web.app.servlet.ServletMetricsFilter.doFilter(ServletMetricsFilter.java:161)
    at org.alfresco.repo.web.filter.beans.BeanProxyFilter.doFilter(BeanProxyFilter.java:89)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.alfresco.web.app.servlet.GlobalLocalizationFilter.doFilter(GlobalLocalizationFilter.java:68)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.alfresco.web.app.servlet.ClearSecurityContextFilter.doFilter(ClearSecurityContextFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:544)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:747)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:616)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:818)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1626)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NoSuchMethodError: 'void org.json.JSONWriter.<init>(java.io.Writer)'
    at com.github.dynamicextensionsalfresco.webscripts.resolutions.JsonWriterResolution.resolve(JsonWriterResolution.java:12)
    at com.github.dynamicextensionsalfresco.webscripts.resolutions.AbstractResolution.resolve(AbstractResolution.java:49)
    at com.github.dynamicextensionsalfresco.webscripts.resolutions.AbstractJsonResolution.resolve(AbstractJsonResolution.java:34)
    at com.github.dynamicextensionsalfresco.webscripts.AnnotationWebScript.handleUriMethodReturnValue(AnnotationWebScript.java:217)
    at com.github.dynamicextensionsalfresco.webscripts.AnnotationWebScript.execute(AnnotationWebScript.java:77)
    ... 48 more

The used constructor is no longer available in the currently available version of the library.

Possible Solution

Steps to Reproduce (for bugs)

  1. create webscript returning object extending JsonWriterResolution
  2. deploy and call webscript

See also first commit on branch DEVEM-486

Context

Issue was discovered when porting Care4alf to alfresco 7.

Your Environment

tgeens commented 3 years ago

Alternative solution: use Jackson instead of org.json ?

kerkhofsd commented 3 years ago

Apart from this being fixed, I'd advise to make use of the @ResponseBody and @RequestBody functionality instead of the JsonWriterResolution.

vierbergenlars commented 3 years ago

Is this component inside or outside the OSGi container? If it is inside, can't we ship a correct version of org.json, which will automatically be wired up by OSGi if the headers on your jar are correct?

kerkhofsd commented 3 years ago

Based on the source code, I assume that the type of the constructor argument for JSONWriter has been changed from Writer to Appendable: https://github.com/stleary/JSON-java/blob/20201115/src/main/java/org/json/JSONWriter.java#L98

Futhermore, Writer implements Appendable.

Hence in theory this should still work?

vierbergenlars commented 3 years ago

As we have seen previously in alfred-telemetry (https://github.com/xenit-eu/alfred-telemetry/issues/20), constructors are not handled the same as other methods.

The exact constructor signature to call is set at compile-time. It expects a method with those exact types to exist and it will not perform any fallback to a constructor with a compatible signature.

What still works in practice is compiling against the different version of org.json.JSONWriter without doing any source-level changes. However, the generated bytecode is not compatible and not interchangable between those two versions.

kerkhofsd commented 3 years ago

Right, that rings a bell (https://github.com/xenit-eu/alfred-telemetry/issues/20#issuecomment-791538978).

Thanks @vierbergenlars !

RVanhuysseXenit commented 3 years ago

So another possible solution would be to change just https://github.com/xenit-eu/dynamic-extensions-for-alfresco/blob/06ca1b79a78347e71a19bdf5b22a82f10fe332c2/webscripts/build.gradle#L6 to use a version specific enforcedPlatform.