openequella / openEQUELLA

Core openEQUELLA sources
https://openequella.github.io/
Apache License 2.0
41 stars 44 forks source link

Repeatedly clicking the save button in a canvas selection session with selections triggers an error #2601

Closed SammyIsConfused closed 3 years ago

SammyIsConfused commented 3 years ago

Describe the bug Affects old and new UI. Discovered in 2020.2 alpha, was able to replicate in 2020.1.6. When making selections in a Canvas selection session, if you spam click Save with at least one selection set, it will trigger an error.

In the legacy search New UI, start a selection session and add an attachment or resource summary. Then spam click (double click sometimes works but otherwise just quickly click repeatedly) “Save” and a full page error will display. 500: Internal Server Error - Already rendered!

In the old UI, this happens with a different error message.

Troublingly, once this has happened it seems that any further selection sessions will have the same issue when hitting Save, preventing selections.

This is specific to Canvas integration - it occurs within CanvasConnectorService when attempting to get a response.getMessage. There is also a missing language string in the area- com.tle.core.connectors.canvas.error.prelim which used to read:

An error occurred in Canvas\:

To Reproduce Steps to reproduce the behavior:

  1. Go to a selection session from Canvas.
  2. Make some selections.
  3. Very quickly, repeatedly click the Save button.
  4. See full page error.
  5. Re open selection session.
  6. Make some selections.
  7. Click Save once.
  8. Note that the same issue arises as before.

Expected behavior We should handle this better, perhaps debouncing the Save call such that it doesn't cause issues. If #2600 were to be fixed, it would make this much harder to occur as it would close the selection session after the first save click. The expected behaviour would be to handle spam clicking without error.

Screenshots Here are two clips of this happening in the old and new UI. Note - this does not have to be a cloud search selection, that's simply what I was testing at the time that I found this bug. You can make this happen with any selection. Peek 2020-12-09 14-31

Peek 2020-12-09 14-02

Stacktrace

java.lang.RuntimeException: ???com.tle.core.connectors.canvas.error.prelim??? (400) Bad Request --- <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<p>Your browser sent a request that this server could not understand.<br />
Size of a request header field exceeds server limit.</p>
<hr>
<address>Apache Server at canvas.instructure.com Port 80</address>
</body></html>
at com.tle.core.connectors.canvas.service.CanvasConnectorService.getCanvasResponse(CanvasConnectorService.java:803)
at com.tle.core.connectors.canvas.service.CanvasConnectorService.getCanvasResponse(CanvasConnectorService.java:760)
at com.tle.core.connectors.canvas.service.CanvasConnectorService.getCanvasJSONResponse(CanvasConnectorService.java:749)
at com.tle.core.connectors.canvas.service.CanvasConnectorService.getCanvasCourse(CanvasConnectorService.java:650)
at com.tle.core.connectors.canvas.service.CanvasConnectorService.getCourseCode(CanvasConnectorService.java:140)
at com.tle.core.connectors.service.ConnectorRepositoryServiceImpl.addItemToCourse(ConnectorRepositoryServiceImpl.java:191)
at com.tle.core.security.impl.MethodSecurityInteceptor.invoke(MethodSecurityInteceptor.java:79)
at com.tle.core.security.impl.MethodSecurityInteceptor.invoke(MethodSecurityInteceptor.java:79)
at com.tle.integration.lti.canvasextension.CanvasIntegration.select(CanvasIntegration.java:418)
at com.tle.integration.lti.canvasextension.CanvasIntegration.select(CanvasIntegration.java:79)
at com.tle.web.integration.IntegrationImpl.select(IntegrationImpl.java:63)
at com.tle.web.integration.IntegrationSection.executeSelectionsMade(IntegrationSection.java:57)
at com.tle.web.selection.TreeLookupSelectionCallback.executeSelectionsMade(TreeLookupSelectionCallback.java:37)
at com.tle.web.selection.SelectionServiceImpl.returnFromSession(SelectionServiceImpl.java:146)
at com.tle.web.selection.section.CourseListSection.save(CourseListSection.java:510)
at sun.reflect.GeneratedMethodAccessor411.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.tle.web.sections.events.js.MethodInvocationEventGenerator$1.fireDirect(MethodInvocationEventGenerator.java:94)
at com.tle.web.sections.events.AbstractDirectEvent.fire(AbstractDirectEvent.java:86)
at com.tle.web.sections.generic.DefaultSectionInfo.processEvent(DefaultSectionInfo.java:420)
at com.tle.web.sections.generic.DefaultSectionInfo.processEvent(DefaultSectionInfo.java:394)
at com.tle.web.sections.generic.DefaultSectionInfo.processQueue(DefaultSectionInfo.java:388)
at com.tle.web.sections.registry.AbstractSectionsController.execute(AbstractSectionsController.java:44)
at com.tle.web.api.LegacyContentApi.$anonfun$submit$1(LegacyContentApi.scala:450)
at com.tle.web.api.LegacyContentApi.withTreePath(LegacyContentApi.scala:318)
at com.tle.web.api.LegacyContentApi.submit(LegacyContentApi.scala:447)
at sun.reflect.GeneratedMethodAccessor182.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:138)
at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:543)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:432)
at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:393)
at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:358)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:395)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:364)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:337)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:440)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135)
at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:358)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:245)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:61)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at com.tle.web.remoting.resteasy.RestEasyServlet.service(RestEasyServlet.java:140)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at com.tle.web.dispatcher.ServletDispatcher.dispatch(ServletDispatcher.java:95)
at com.tle.web.dispatcher.RequestDispatchFilter.doFilter(RequestDispatchFilter.java:91)
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:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
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:374)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:880)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1601)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
11:51:06,230 ERROR [RestEasyExceptionMapper] REST API error
com.tle.web.sections.SectionsRuntimeException: Already rendered!
at com.tle.web.sections.generic.DefaultSectionInfo.setRendered(DefaultSectionInfo.java:100)
at com.tle.web.api.LegacyContentApi.$anonfun$submit$5(LegacyContentApi.scala:456)
at scala.Option.getOrElse(Option.scala:189)
at com.tle.web.api.LegacyContentApi.$anonfun$submit$1(LegacyContentApi.scala:455)
at com.tle.web.api.LegacyContentApi.withTreePath(LegacyContentApi.scala:318)
at com.tle.web.api.LegacyContentApi.submit(LegacyContentApi.scala:447)
at sun.reflect.GeneratedMethodAccessor182.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:138)
at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:543)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:432)
at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:393)
at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:358)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:395)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:364)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:337)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:440)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135)
at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:358)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:245)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:61)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at com.tle.web.remoting.resteasy.RestEasyServlet.service(RestEasyServlet.java:140)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at com.tle.web.dispatcher.ServletDispatcher.dispatch(ServletDispatcher.java:95)
at com.tle.web.dispatcher.RequestDispatchFilter.doFilter(RequestDispatchFilter.java:91)
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:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
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:374)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:880)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1601)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)

Platform:

SammyIsConfused commented 3 years ago

Fixed. Now when this error occurs the cookie cache is cleared and a simple refresh gets the session working again.