MCR.ContentTransformer.{ID}.Stylesheet. You may choose your own instance of
@@ -273,7 +275,7 @@ public void transform(MCRContent source, OutputStream out, MCRParameterCollector
reader.parse(source.getInputSource());
} catch (TransformerConfigurationException | SAXException | IllegalArgumentException
| ParserConfigurationException e) {
- throw new IOException(e);
+ throw new IOException(getErrorMessage(e), e);
} catch (RuntimeException e) {
if (el != null && e.getCause() == null && el.getExceptionThrown() != null) {
//typically if a RuntimeException has no cause,
@@ -286,6 +288,18 @@ public void transform(MCRContent source, OutputStream out, MCRParameterCollector
}
}
+ /**
+ * Returns the message for an exception wrapping the given exception. Saxon does not include the text of a
+ * terminating xsl:message in its exception message, so it is added here.
+ */
+ private static String getErrorMessage(Exception e) {
+ if (e.getCause() instanceof TerminationException terminationException) {
+ return "Error while XSL transformation: " + MCRErrorListener.getMyMessageAndLocation(terminationException);
+ }
+ // same message as for new IOException(e) or new MCRException(e)
+ return e.toString();
+ }
+
protected MCRContent transform(MCRContent source, XMLReader reader, TransformerHandler transformerHandler,
MCRParameterCollector parameter)
throws IOException, SAXException, TransformerException, ParserConfigurationException {
@@ -332,11 +346,11 @@ protected Deque
+ * For Saxon transformers, xsl:message output is logged, too.
+ *
+ * @param transformer the transformer to configure
+ * @return the error listener set to the transformer
+ */
+ public static MCRErrorListener setTo(Transformer transformer) {
+ MCRErrorListener errorListener = new MCRErrorListener();
+ transformer.setErrorListener(errorListener);
+ AbstractXsltTransformer xsltTransformer = null;
+ if (transformer instanceof TransformerImpl saxonTransformer) {
+ xsltTransformer = saxonTransformer.getUnderlyingXsltTransformer();
+ } else if (transformer instanceof StreamingTransformerImpl saxonTransformer) {
+ xsltTransformer = saxonTransformer.getUnderlyingXsltTransformer();
+ }
+ if (xsltTransformer != null) {
+ // Saxon does not report xsl:message to the ErrorListener but writes it to System.err
+ xsltTransformer.setMessageHandler(MCRErrorListener::logMessage);
+ }
+ return errorListener;
+ }
+
public TransformerException getExceptionThrown() {
return exceptionThrown;
}
+ private static void logMessage(Message message) {
+ Location location = message.getLocation();
+ LOGGER.info(() -> location == null
+ ? getMessageAndLocation(null, 0, 0, message.getStringValue())
+ : getMessageAndLocation(location.getSystemId(), location.getLineNumber(), location.getColumnNumber(),
+ message.getStringValue()));
+ }
+
private boolean triggerException(TransformerException e) {
if (exceptionThrown != null) {
return false;
@@ -90,6 +132,10 @@ public void fatalError(TransformerException exception) throws TransformerExcepti
&& stackTrace[0].getClassName().endsWith("ElemMessage")) {
LOGGER.debug("Original exception: ", unwrappedException);
unwrappedException = new TransformerException(lastMessage);
+ } else if (unwrappedException instanceof TerminationException) {
+ //Saxon: xsl:message with terminate="yes", the exception message does not contain the message text
+ LOGGER.debug("Original exception: ", unwrappedException);
+ unwrappedException = new TransformerException(getMyMessageAndLocation(unwrappedException));
}
if (triggerException(unwrappedException)) {
LOGGER.fatal("Exception while XSL transformation.", unwrappedException);
@@ -114,27 +160,51 @@ public static TransformerException unwrapException(TransformerException exceptio
public static String getMyMessageAndLocation(TransformerException exception) {
SourceLocator locator = exception.getLocator();
- StringBuilder msg = new StringBuilder();
- if (locator != null) {
- String systemID = locator.getSystemId();
- int line = locator.getLineNumber();
- int col = locator.getColumnNumber();
- if (systemID != null) {
- msg.append("SystemID: ");
- msg.append(systemID);
- }
- if (line != 0) {
- msg.append(" [");
- msg.append(line);
- if (col != 0) {
- msg.append(',');
- msg.append(col);
+ String message = getMessage(exception);
+ if (locator == null) {
+ return getMessageAndLocation(null, 0, 0, message);
+ }
+ return getMessageAndLocation(locator.getSystemId(), locator.getLineNumber(), locator.getColumnNumber(),
+ message);
+ }
+
+ /**
+ * Returns the message of the given exception. For a Saxon exception caused by xsl:message with
+ * terminate="yes" this is the text of the message, if it is not empty.
+ */
+ private static String getMessage(TransformerException exception) {
+ if (exception instanceof TerminationException terminationException
+ && terminationException.getErrorObject() != null) {
+ try {
+ Item content = terminationException.getErrorObject().head();
+ String text = content == null ? null : content.getStringValue();
+ if (text != null && !text.isBlank()) {
+ return text;
}
- msg.append(']');
+ } catch (XPathException e) {
+ LOGGER.debug("Could not get text of terminating xsl:message.", e);
+ }
+ }
+ return exception.getMessage();
+ }
+
+ private static String getMessageAndLocation(String systemID, int line, int col, String message) {
+ StringBuilder msg = new StringBuilder();
+ if (systemID != null) {
+ msg.append("SystemID: ");
+ msg.append(systemID);
+ }
+ if (line > 0) {
+ msg.append(" [");
+ msg.append(line);
+ if (col > 0) {
+ msg.append(',');
+ msg.append(col);
}
+ msg.append(']');
}
msg.append(": ");
- msg.append(exception.getMessage());
+ msg.append(message);
return msg.toString();
}
}
diff --git a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandUtils.java b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandUtils.java
index 12028ae361..7f7fa60ee0 100644
--- a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandUtils.java
+++ b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandUtils.java
@@ -40,6 +40,7 @@
import org.mycore.common.MCRConstants;
import org.mycore.common.MCRUsageException;
import org.mycore.common.content.transformer.MCRXSLTransformer;
+import org.mycore.common.xsl.MCRErrorListener;
import org.mycore.common.xsl.MCRXSLResourceHelper;
import org.mycore.common.xsl.uriresolver.MCRURIResolver;
import org.mycore.datamodel.common.MCRXMLMetadataManager;
@@ -188,6 +189,8 @@ public static Transformer getTransformer(String style, String defaultStyle, Map<
}
Transformer transformer = cache.get(style);
if (transformer != null) {
+ // fresh error listener, the old one keeps the exception of a previous transformation
+ MCRErrorListener.setTo(transformer);
return transformer;
}
@@ -202,6 +205,7 @@ public static Transformer getTransformer(String style, String defaultStyle, Map<
TransformerFactory transformerFactory = MCRXSLTransformer.createDefaultTransformerFactory();
transformerFactory.setURIResolver(MCRURIResolver.obtainInstance());
transformer = transformerFactory.newTransformer(new JDOMSource(element));
+ MCRErrorListener.setTo(transformer);
cache.put(style, transformer);
LOGGER.info("Loaded transformer from resource {} for style {}.", xslFilePath, style);
return transformer;
diff --git a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java
index b89fdec8b3..ab7aa06bdb 100644
--- a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java
+++ b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java
@@ -977,6 +977,7 @@ private static void xslt(String objectId, String xslFilePath, boolean force) thr
xmlReader.setEntityResolver(MCREntityResolver.getInstance());
SAXSource styleSource = new SAXSource(xmlReader, style.getInputSource());
Transformer transformer = transformerFactory.newTransformer(styleSource);
+ MCRErrorListener.setTo(transformer);
for (Entry