KohlbacherLab / dnpm-dip-api-gateway

REST API Gateway component for DNPM:DIP
MIT License
0 stars 0 forks source link

NoSuchElementException: JsError.get #5

Closed nr23730 closed 1 month ago

nr23730 commented 1 month ago

Recents builds of the backend produce lots of error messages, while the API is also unavailable with the following error:

{"issues":[{"severity":"error","details":"Server error: Unable to provision, see the following errors:\n\n1) [Guice/ErrorInjectingConstructor]: NoSuchElementException: JsError.get\n  at MTBController.<init>(MTBController.scala:82)\n  at MTBRouter.<init>(MTBRouter.scala:27)\n      \\_ for 1st parameter controller\n  at api.Router.<init>(Router.scala:16)\n      \\_ for 3rd parameter mtbRouter\n  at router.Routes.<init>(Routes.scala:24)\n      \\_ for 2nd parameter de_dnpm_dip_rest_api_Router_0\n  while locating router.Routes\n  while locating RoutesProvider\n  while locating routing.Router\n\nLearn more:\n  https://github.com/google/guice/wiki/ERROR_INJECTING_CONSTRUCTOR\n\n1 error\n\n======================\nFull classname legend:\n======================\nMTBController:          \"de.dnpm.dip.rest.api.MTBController\"\nMTBRouter:              \"de.dnpm.dip.rest.api.MTBRouter\"\nRoutesProvider:         \"play.api.inject.RoutesProvider\"\napi.Router:             \"de.dnpm.dip.rest.api.Router\"\nrouting.Router:         \"play.api.routing.Router\"\n========================\nEnd of classname legend:\n========================\n"}]}

This is the error in the log:

backend  | 14:30:54.563 [main] INFO play.api.http.HttpErrorHandlerExceptions -- Registering exception handler: guice-provision-exception-handler
backend  | 14:30:55.745 [application-akka.actor.default-dispatcher-6] INFO  akka.event.slf4j.Slf4jLogger - Slf4jLogger started
backend  | 14:30:56.449 [main] INFO  play.api.Play - Application started (Prod) (no global state)
backend  | 14:30:57.104 [main] INFO  play.core.server.AkkaHttpServer - Listening for HTTP on /[0:0:0:0:0:0:0:0]:9000
backend  | 14:30:58.803 [default-akka.actor.default-dispatcher-6] INFO  akka.event.slf4j.Slf4jLogger - Slf4jLogger started
backend  | 14:30:59.002 [application-akka.actor.default-dispatcher-7] INFO  d.d.d.a.c.StandaloneAuthupClient - Setting up permission/role model
backend  | 14:30:59.239 [default-akka.actor.default-dispatcher-4] INFO  akka.event.slf4j.Slf4jLogger - Slf4jLogger started
backend  | 14:30:59.430 [pool-4-thread-1] WARN  d.dnpm.dip.connector.BrokerConnector - Global site config from broker not available, falling back to empty external site list
backend  | 14:31:00.289 [application-akka.actor.default-dispatcher-7] ERROR de.dnpm.dip.rest.api.ErrorHandler - Server error
backend  | com.google.inject.ProvisionException: Unable to provision, see the following errors:
backend  | 
backend  | 1) [Guice/ErrorInjectingConstructor]: NoSuchElementException: JsError.get
backend  |   at MTBController.<init>(MTBController.scala:82)
backend  |   at MTBRouter.<init>(MTBRouter.scala:27)
backend  |       \_ for 1st parameter controller
backend  |   at api.Router.<init>(Router.scala:16)
backend  |       \_ for 3rd parameter mtbRouter
backend  |   at router.Routes.<init>(Routes.scala:24)
backend  |       \_ for 2nd parameter de_dnpm_dip_rest_api_Router_0
backend  |   while locating router.Routes
backend  |   while locating RoutesProvider
backend  |   while locating routing.Router
backend  | 
backend  | Learn more:
backend  |   https://github.com/google/guice/wiki/ERROR_INJECTING_CONSTRUCTOR
backend  | 
backend  | 1 error
backend  | 
backend  | ======================
backend  | Full classname legend:
backend  | ======================
backend  | MTBController:          "de.dnpm.dip.rest.api.MTBController"
backend  | MTBRouter:              "de.dnpm.dip.rest.api.MTBRouter"
backend  | RoutesProvider:         "play.api.inject.RoutesProvider"
backend  | api.Router:             "de.dnpm.dip.rest.api.Router"
backend  | routing.Router:         "play.api.routing.Router"
backend  | ========================
backend  | End of classname legend:
backend  | ========================
backend  | 
backend  |  at com.google.inject.internal.InternalProvisionException.toProvisionException(InternalProvisionException.java:251)
backend  |  at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1151)
backend  |  at play.api.http.DefaultHttpRequestHandler.routeRequest(HttpRequestHandler.scala:263)
backend  |  at play.api.http.JavaCompatibleHttpRequestHandler.routeRequest(HttpRequestHandler.scala:353)
backend  |  at play.api.http.DefaultHttpRequestHandler.routeWithFallback$1(HttpRequestHandler.scala:173)
backend  |  at play.api.http.DefaultHttpRequestHandler.handlerForRequest(HttpRequestHandler.scala:223)
backend  |  at play.core.server.Server$.getHandlerFor(Server.scala:133)
backend  |  at play.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:355)
backend  |  at play.core.server.AkkaHttpServer.$anonfun$createServerBinding$1(AkkaHttpServer.scala:224)
backend  |  at akka.stream.impl.fusing.MapAsync$$anon$30.onPush(Ops.scala:1309)
backend  |  at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:542)
backend  |  at akka.stream.impl.fusing.GraphInterpreter.processEvent(GraphInterpreter.scala:496)
backend  |  at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:390)
backend  |  at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:650)
backend  |  at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:521)
backend  |  at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:625)
backend  |  at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:800)
backend  |  at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:818)
backend  |  at akka.actor.Actor.aroundReceive(Actor.scala:537)
backend  |  at akka.actor.Actor.aroundReceive$(Actor.scala:535)
backend  |  at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:716)
backend  |  at akka.actor.ActorCell.receiveMessage(ActorCell.scala:579)
backend  |  at akka.actor.ActorCell.invoke(ActorCell.scala:547)
backend  |  at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:270)
backend  |  at akka.dispatch.Mailbox.run(Mailbox.scala:231)
backend  |  at akka.dispatch.Mailbox.exec(Mailbox.scala:243)
backend  |  at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
backend  |  at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312)
backend  |  at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843)
backend  |  at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808)
backend  |  at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188)
backend  | Caused by: java.util.NoSuchElementException: JsError.get
backend  |  at play.api.libs.json.JsError.get(JsResult.scala:53)
backend  |  at play.api.libs.json.JsError.get(JsResult.scala:52)
backend  |  at de.dnpm.dip.service.validation.FSBackedRepository.$anonfun$readAsJson$3(FSBackedRepository.scala:75)
backend  |  at scala.util.ChainingOps$.pipe$extension(ChainingOps.scala:64)
backend  |  at de.dnpm.dip.service.validation.FSBackedRepository.$anonfun$readAsJson$1(FSBackedRepository.scala:75)
backend  |  at scala.collection.immutable.LazyList.$anonfun$mapImpl$1(LazyList.scala:517)
backend  |  at scala.collection.immutable.LazyList.scala$collection$immutable$LazyList$$state$lzycompute(LazyList.scala:259)
backend  |  at scala.collection.immutable.LazyList.scala$collection$immutable$LazyList$$state(LazyList.scala:252)
backend  |  at scala.collection.immutable.LazyList.isEmpty(LazyList.scala:269)
backend  |  at scala.collection.immutable.LazyList.$anonfun$mapImpl$1(LazyList.scala:516)
backend  |  at scala.collection.immutable.LazyList.scala$collection$immutable$LazyList$$state$lzycompute(LazyList.scala:259)
backend  |  at scala.collection.immutable.LazyList.scala$collection$immutable$LazyList$$state(LazyList.scala:252)
backend  |  at scala.collection.immutable.LazyList.isEmpty(LazyList.scala:269)
backend  |  at scala.collection.immutable.LazyList$LazyIterator.hasNext(LazyList.scala:1251)
backend  |  at scala.collection.mutable.Growable.addAll(Growable.scala:61)
backend  |  at scala.collection.mutable.Growable.addAll$(Growable.scala:57)
backend  |  at scala.collection.mutable.AbstractMap.addAll(Map.scala:268)
backend  |  at scala.collection.concurrent.TrieMap$.from(TrieMap.scala:1044)
backend  |  at de.dnpm.dip.service.validation.FSBackedRepository.<init>(FSBackedRepository.scala:87)
backend  |  at de.dnpm.dip.mtb.validation.impl.MTBValidationRepository$$anonfun$getInstance$1$$anon$1.<init>(MTBValidationRepository.scala:38)
backend  |  at de.dnpm.dip.mtb.validation.impl.MTBValidationRepository$$anonfun$getInstance$1.applyOrElse(MTBValidationRepository.scala:38)
backend  |  at de.dnpm.dip.mtb.validation.impl.MTBValidationRepository$$anonfun$getInstance$1.applyOrElse(MTBValidationRepository.scala:32)
backend  |  at scala.util.Failure.recover(Try.scala:242)
backend  |  at de.dnpm.dip.mtb.validation.impl.MTBValidationRepository$.getInstance(MTBValidationRepository.scala:32)
backend  |  at de.dnpm.dip.mtb.validation.impl.MTBValidationServiceImpl$.instance$lzycompute(MTBValidationServiceImpl.scala:32)
backend  |  at de.dnpm.dip.mtb.validation.impl.MTBValidationServiceImpl$.instance(MTBValidationServiceImpl.scala:29)
backend  |  at de.dnpm.dip.mtb.validation.impl.MTBValidationServiceProviderImpl.getInstance(MTBValidationServiceImpl.scala:22)
backend  |  at de.dnpm.dip.mtb.validation.impl.MTBValidationServiceProviderImpl.getInstance(MTBValidationServiceImpl.scala:19)
backend  |  at de.dnpm.dip.util.SPILoader.$anonfun$getInstance$3(SPI.scala:49)
backend  |  at scala.util.Success.map(Try.scala:270)
backend  |  at de.dnpm.dip.util.SPILoader.getInstance(SPI.scala:49)
backend  |  at de.dnpm.dip.rest.api.MTBController.<init>(MTBController.scala:105)
backend  |  at de.dnpm.dip.rest.api.MTBController$$FastClassByGuice$$1053e3b.GUICE$TRAMPOLINE(<generated>)
backend  |  at de.dnpm.dip.rest.api.MTBController$$FastClassByGuice$$1053e3b.apply(<generated>)
backend  |  at com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:82)
backend  |  at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:114)
backend  |  at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91)
backend  |  at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300)
backend  |  at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:40)
backend  |  at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:60)
backend  |  at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:113)
backend  |  at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91)
backend  |  at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300)
backend  |  at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:40)
backend  |  at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:60)
backend  |  at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:113)
backend  |  at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91)
backend  |  at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300)
backend  |  at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:40)
backend  |  at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:60)
backend  |  at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:113)
backend  |  at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91)
backend  |  at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300)
backend  |  at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1148)
backend  |  at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1186)
backend  |  at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:434)
backend  |  at play.api.inject.ContextClassLoaderInjector.$anonfun$instanceOf$3(Injector.scala:119)
backend  |  at play.api.inject.ContextClassLoaderInjector.withContext(Injector.scala:127)
backend  |  at play.api.inject.ContextClassLoaderInjector.instanceOf(Injector.scala:119)
backend  |  at play.api.inject.RoutesProvider.$anonfun$get$2(BuiltinModule.scala:128)
backend  |  at scala.Option.fold(Option.scala:263)
backend  |  at play.api.inject.RoutesProvider.get$lzycompute(BuiltinModule.scala:128)
backend  |  at play.api.inject.RoutesProvider.get(BuiltinModule.scala:123)
backend  |  at play.api.inject.RoutesProvider.get(BuiltinModule.scala:117)
backend  |  at com.google.inject.internal.ProviderInternalFactory.provision(ProviderInternalFactory.java:86)
backend  |  at com.google.inject.internal.BoundProviderFactory.provision(BoundProviderFactory.java:72)
backend  |  at com.google.inject.internal.ProviderInternalFactory.circularGet(ProviderInternalFactory.java:60)
backend  |  at com.google.inject.internal.BoundProviderFactory.get(BoundProviderFactory.java:59)
backend  |  at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1148)
backend  |  ... 29 common frames omitted
backend  | 14:31:01.588 [scala-execution-context-global-64] INFO  d.d.d.a.c.StandaloneAuthupClient - Successfully set up permission/role model

Affected versions: ghcr.io/kohlbacherlab/dnpm-dip-backend@sha256:28e51a9b306a01cb586a5197a4a57cc993b74a0a948b9f85c70e2ddedd68b188 and upwards

This is the last docker image that works:

ghcr.io/kohlbacherlab/dnpm-dip-backend@sha256:d7e703dbea28cd2db76081021a4b1f8141433a0c6d341a51a8ac1c4bf4a8f867

Tested with deployment @ da3f5d6a7a51b0a4fe6a026a0cb4f6a6ce7eb526

lucienclin commented 1 month ago

Thank you for the detailed error report.

The problem seems not to be related to the last docker image, though, but rather to the little changes we made to the deployment setup last week:

I could reproduce the error with the last deployment setup: it occurs when the backend process doesn't have correct access rights to the persistence directory. The problem disappeared when this was ensured as described.

Could it be that you forgot to set these permissions?

Previously, the described option to ensure proper access of the backend to the persistence directory was to assign the backend service in docker-compose.yml a suitable system user. However, since we are trying to adapt the deployment process so that none of the versioned deployment files require local modifications, the opted for solution a.t.m is to use chmod -R 777 ... on the persistence directory.

I hope this solves the issue for you as well, else don't hesitate to follow up (you can also contact me by mail: lucien.clin@uni-tuebingen.de)

nr23730 commented 1 month ago

Thank you very much! (Re-)applying the permissions did not fix the issue. However, clearing all data present in the node did fix the issue. This is not an issue, as this one is test anyway, for production it would be a problem...

lucienclin commented 1 month ago

Thank you for the follow-up.

OK, then it might not have been the access permissions, but really parsing errors while loading the present JSON data -- although I was quite sure there weren't any breaking changes in the data objects recently, and thus discarded this potential error source.

I'll add more detailed logging output to enable easier problem diagnosis in such situations.

Thanks again for the feedback.

lucienclin commented 1 month ago

P.S.

I published a new "backend" container image with the aforementioned additional logging output. Of course, this is not a proper solution to the problem you experienced and reported, with breaking changes in the persisted data structures, but for now this will at least facilitate error diagnosis in such cases.

Eventually, for production, we'll have to plan migrations of persisted data in case of breaking changes. I'll keep this mind.