enhancement
major
minor
defect
critical
major
minor
major
#30124
OpenAPI-Server: Fehlermeldung eines gescheiterten Skripts ist unlesbar und wird außerhalb des Interaction-Contexts erzeugt
Problem
Scheitert das Skript einer OpenAPI-Methode (ServiceMethodByExpression), schreibt der OpenApiServer eine Meldung ins Log, die für die Diagnose kaum zu gebrauchen ist:
INFO OpenApiServer - API request to '/equipments/{uuid}' failed: Evaluation of 'query(...)' failed, arguments: (),
variables: {computeTransformationMatrix=(planningElt -> {...}); createJSON=(equipment -> { ... mehrere tausend Zeichen Skript ... }); uuid=5324fb1f-...; ...}:
Evaluation of '...' failed, arguments: (), variables: {computeTransformationMatrix=(planningElt -> {trafoMatrix = $planningElt.ERROR: java.lang.IllegalStateException: KnowledgeBase operations require a valid InteractionContext.; ...}:
Cannot load object data: Unable to fetch knowledge item with id 'ID(2916037)' of type 'epp.plan.Equipment': ...
Die Meldung hat zwei Mängel:
- Teile der Meldung sind kaputt. Im zweiten Teil stehen statt der Objekte nur noch Fehler der Art ERROR: java.lang.IllegalStateException: KnowledgeBase operations require a valid InteractionContext..
- Die Meldung ist riesig und enthält das ganze Skript, sogar doppelt. Die Meldung der gescheiterten Auswertung enthält den kompletten Variablen-Context. Darin stehen auch die Funktionen, die das Skript per let bindet (im Beispiel createJSON, createJSONWithSecurity, computeTransformationMatrix), also das ganze Skript der Methode. Und dieselbe Meldung steht zweimal in der Ausgabe (siehe Ursache). Mit welchen Parametern der Aufrufer die Methode aufgerufen hat und was wirklich gescheitert ist, geht darin unter.
Ursache
- ServiceMethodByExpression.handleRequest führt das Skript in TLContextManager.inPersonContext(...) bzw. ThreadContext.inSystemContext(...) aus. Scheitert es, verpackt execute() die Exception in new ComputationFailure(ex.getMessage(), ex). Deren Meldung wird also noch innerhalb des Contexts erzeugt.
- OpenApiServer.process fängt die ComputationFailure erst nach dem Ende dieses Contexts und baut die Meldung in messages(ex) aus allen Causes der Exception. Dabei wird die Meldung der verpackten Exception ein zweites Mal erzeugt, jetzt außerhalb des Contexts.
- Diese Exception ist eine TopLogicException mit dem Schlüssel ERROR_EVALUATION_FAILED__EXPR_ARGS_DEFS, den SearchExpression.evalWith mit dem Ausdruck, den Argumenten und dem EvalContext befüllt. I18NRuntimeException.getMessage() übersetzt den Schlüssel erst beim Aufruf und ruft dabei toString() der Fachobjekte auf. Außerhalb des Contexts können die nicht mehr geladen werden. Weil sich die beiden Fassungen der Meldung dadurch unterscheiden, greift das Entfernen doppelter Meldungen in messages(ex) nicht.
Lösung
ServiceMethodByExpression baut die Meldung für einen gescheiterten Aufruf selbst (FailureMessages.scriptFailure), und zwar noch innerhalb des Interaction-Contexts. Sie enthält:
- die Parameter des Aufrufers, so wie sie an das Skript übergeben werden (Binärdaten nur mit ihrer Größe),
- den Ausdruck der innersten gescheiterten Auswertung mit seinen Funktionsargumenten, aber ohne die Variablen des Contexts. Die bestehen außer den Parametern des Aufrufers im Wesentlichen aus den Funktionen des Skripts der Methode, die über den Pfad schon feststeht.
- die Meldungen der Ursachen dieser Auswertung, z.B. Unable to fetch knowledge item ....
Der Pfad der Methode steht weiterhin vorne in der Meldung von OpenApiServer. Dieser übernimmt im ComputationFailure-Zweig nur noch die fertige Meldung und durchläuft die Causes nicht mehr.
Die Parameter werden vollständig ausgegeben, wie bisher auch schon als Teil des Variablen-Contexts. Sensible Parameter werden dadurch nicht geschützt; das ist nicht Gegenstand dieses Tickets.
ERROR_EVALUATION_FAILED__EXPR_ARGS_DEFS in SearchExpression bleibt unverändert, weil andere Verwender (z.B. Fehlerdialoge in der Oberfläche) den vollständigen Context brauchen können.