Skip to content

Commit a27be64

Browse files
committed
[bugfix] fn:transform - race condition
XQTS tests were intermittently getting the wrong error message becase we were pullling it as the last error from a shared global error listener (error logger). Make an individual listener for each compilation/request which also side-effects the write to the shared logger.
1 parent 623615e commit a27be64

File tree

1 file changed

+43
-24
lines changed
  • exist-core/src/main/java/org/exist/xquery/functions/fn/transform

1 file changed

+43
-24
lines changed

exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Transform.java

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
194194
return invocation.invoke();
195195

196196
} catch (final SaxonApiException | UncheckedXPathException e) {
197-
throw originalXPathException(e, ErrorCodes.FOXT0003);
197+
throw originalXPathException("Could not transform input: ", e, ErrorCodes.FOXT0003);
198198
}
199199

200200
} else {
@@ -205,7 +205,8 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
205205

206206
private XsltExecutable compileExecutable(final Options options) throws XPathException {
207207
final XsltCompiler xsltCompiler = org.exist.xquery.functions.fn.transform.Transform.SAXON_PROCESSOR.newXsltCompiler();
208-
xsltCompiler.setErrorListener(org.exist.xquery.functions.fn.transform.Transform.ERROR_LISTENER);
208+
final SingleRequestErrorListener errorListener = new SingleRequestErrorListener(Transform.ERROR_LISTENER);
209+
xsltCompiler.setErrorListener(errorListener);
209210

210211
for (final Map.Entry<net.sf.saxon.s9api.QName, XdmValue> entry : options.staticParams.entrySet()) {
211212
xsltCompiler.setParameter(entry.getKey(), entry.getValue());
@@ -237,11 +238,10 @@ private XsltExecutable compileExecutable(final Options options) throws XPathExce
237238

238239
try {
239240
options.resolvedStylesheetBaseURI.ifPresent(anyURIValue -> options.xsltSource._2.setSystemId(anyURIValue.getStringValue()));
240-
Transform.ERROR_LISTENER.clear();
241241
return xsltCompiler.compile(options.xsltSource._2); // .compilePackage //TODO(AR) need to implement support for xslt-packages
242242
} catch (final SaxonApiException e) {
243-
final Optional<Exception> compilerException = Transform.ERROR_LISTENER.getWorst().map(e1 -> e1);
244-
throw originalXPathException(compilerException.orElse(e), ErrorCodes.FOXT0003);
243+
final Optional<Exception> compilerException = errorListener.getWorst().map(e1 -> e1);
244+
throw originalXPathException("Could not compile stylesheet: ", compilerException.orElse(e), ErrorCodes.FOXT0003);
245245
}
246246
}
247247

@@ -254,11 +254,11 @@ private XsltExecutable compileExecutable(final Options options) throws XPathExce
254254
* @param defaultErrorCode use this code and its description to fill in blanks in what we finally throw
255255
* @returns XPathException the eventual eXist exception which the caller is expected to throw
256256
*/
257-
private XPathException originalXPathException(@Nonnull final Throwable e, final ErrorCodes.ErrorCode defaultErrorCode) {
257+
private XPathException originalXPathException(final String prefix, @Nonnull final Throwable e, final ErrorCodes.ErrorCode defaultErrorCode) {
258258
Throwable cause = e;
259259
while (cause != null) {
260260
if (cause instanceof XPathException) {
261-
return (XPathException) cause;
261+
return new XPathException(fnTransform, ((XPathException) cause).getErrorCode(), prefix + cause.getMessage());
262262
}
263263
cause = cause.getCause();
264264
}
@@ -271,15 +271,15 @@ private XPathException originalXPathException(@Nonnull final Throwable e, final
271271
if (from != null) {
272272
final QName errorCodeQName = new QName(from.getLocalPart(), from.getURI(), from.getPrefix());
273273
final ErrorCodes.ErrorCode errorCode = new ErrorCodes.ErrorCode(errorCodeQName, cause.getMessage());
274-
return new XPathException(errorCode, cause.getMessage());
274+
return new XPathException(fnTransform, errorCode, prefix + cause.getMessage());
275275
} else {
276-
return new XPathException(fnTransform, defaultErrorCode, cause.getMessage());
276+
return new XPathException(fnTransform, defaultErrorCode, prefix + cause.getMessage());
277277
}
278278
}
279279
cause = cause.getCause();
280280
}
281281

282-
return new XPathException(fnTransform, defaultErrorCode, e.getMessage());
282+
return new XPathException(fnTransform, defaultErrorCode, prefix + e.getMessage());
283283
}
284284

285285
/**
@@ -429,18 +429,6 @@ private static Optional<Source> getSourceNode(final Optional<NodeValue> sourceNo
429429

430430
private static class ErrorListenerLog4jAdapter implements ErrorListener {
431431
private final Logger logger;
432-
private Optional<TransformerException> lastError;
433-
private Optional<TransformerException> lastFatal;
434-
435-
public void clear() {
436-
lastError = Optional.empty();
437-
lastFatal = Optional.empty();
438-
}
439-
440-
public Optional<TransformerException> getWorst() {
441-
if (lastFatal.isPresent()) return lastFatal;
442-
return lastError;
443-
}
444432

445433
public ErrorListenerLog4jAdapter(final Logger logger) {
446434
this.logger = logger;
@@ -453,14 +441,45 @@ public void warning(final TransformerException e) {
453441

454442
@Override
455443
public void error(final TransformerException e) {
456-
lastError = Optional.of(e);
457444
logger.error(e.getMessage(), e);
458445
}
459446

460447
@Override
461448
public void fatalError(final TransformerException e) {
462-
lastFatal = Optional.of(e);
463449
logger.fatal(e.getMessage(), e);
464450
}
465451
}
452+
453+
private static class SingleRequestErrorListener implements ErrorListener {
454+
455+
private Optional<TransformerException> lastError;
456+
private Optional<TransformerException> lastFatal;
457+
458+
public Optional<TransformerException> getWorst() {
459+
if (lastFatal.isPresent()) return lastFatal;
460+
return lastError;
461+
}
462+
463+
private final ErrorListener global;
464+
SingleRequestErrorListener(ErrorListener global) {
465+
this.global = global;
466+
}
467+
468+
@Override
469+
public void warning(TransformerException exception) throws TransformerException {
470+
global.warning(exception);
471+
}
472+
473+
@Override
474+
public void error(TransformerException exception) throws TransformerException {
475+
lastError = Optional.of(exception);
476+
global.error(exception);
477+
}
478+
479+
@Override
480+
public void fatalError(TransformerException exception) throws TransformerException {
481+
lastFatal = Optional.of(exception);
482+
global.fatalError(exception);
483+
}
484+
}
466485
}

0 commit comments

Comments
 (0)