You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by gn...@apache.org on 2026/03/12 12:31:50 UTC

(cxf) branch cxf-7491-charset-aware-interceptors created (now 42dddeb7d8)

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a change to branch cxf-7491-charset-aware-interceptors
in repository https://gitbox.apache.org/repos/asf/cxf.git


      at 42dddeb7d8 [CXF-7491] Make XSLT and Transform interceptors charset-aware

This branch includes the following new commits:

     new 42dddeb7d8 [CXF-7491] Make XSLT and Transform interceptors charset-aware

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(cxf) 01/01: [CXF-7491] Make XSLT and Transform interceptors charset-aware

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch cxf-7491-charset-aware-interceptors
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit 42dddeb7d851d7022dd7596a9e74600e30d437b0
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Thu Mar 12 13:31:38 2026 +0100

    [CXF-7491] Make XSLT and Transform interceptors charset-aware
    
    The XSLT and Transform interceptors were hardcoded to use UTF-8
    encoding, ignoring the charset specified in the message. This change
    propagates the message encoding through the interceptor chain so that
    non-UTF-8 content (e.g. ISO-8859-1) is handled correctly.
    
    - Add getEncoding() to AbstractXSLTInterceptor
    - Add encoding-aware XSLTUtils.transform(Templates, InputStream, String)
    - Add encoding-aware TransformUtils.createNewReaderIfNeeded() and
      createTransformReaderIfNeeded() overloads
    - Update XSLTInInterceptor, XSLTOutInterceptor, and
      TransformInInterceptor to pass message encoding
    - Add tests for ISO-8859-1 encoded XML transformation
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 .../feature/transform/AbstractXSLTInterceptor.java | 17 ++++++++
 .../cxf/feature/transform/XSLTInInterceptor.java   | 14 +++++--
 .../cxf/feature/transform/XSLTOutInterceptor.java  | 14 +++++--
 .../apache/cxf/feature/transform/XSLTUtils.java    | 14 ++++++-
 .../transform/TransformInInterceptor.java          | 47 +++++++++++++++++-----
 .../cxf/staxutils/transform/TransformUtils.java    | 26 +++++++++++-
 .../feature/transform/XSLTInterceptorsTest.java    | 34 ++++++++++++++++
 .../cxf/feature/transform/message-iso-8859-1.xml   |  8 ++++
 8 files changed, 154 insertions(+), 20 deletions(-)

diff --git a/core/src/main/java/org/apache/cxf/feature/transform/AbstractXSLTInterceptor.java b/core/src/main/java/org/apache/cxf/feature/transform/AbstractXSLTInterceptor.java
index 1dc91c8d76..d50af90fb1 100644
--- a/core/src/main/java/org/apache/cxf/feature/transform/AbstractXSLTInterceptor.java
+++ b/core/src/main/java/org/apache/cxf/feature/transform/AbstractXSLTInterceptor.java
@@ -21,6 +21,7 @@ package org.apache.cxf.feature.transform;
 
 
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 
 import javax.xml.stream.XMLStreamException;
 import javax.xml.transform.Templates;
@@ -31,6 +32,7 @@ import javax.xml.transform.dom.DOMSource;
 import org.w3c.dom.Document;
 
 import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.message.Exchange;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.message.MessageUtils;
 import org.apache.cxf.phase.AbstractPhaseInterceptor;
@@ -88,4 +90,19 @@ public abstract class AbstractXSLTInterceptor extends AbstractPhaseInterceptor<M
     protected Templates getXSLTTemplate() {
         return xsltTemplate;
     }
+
+    protected String getEncoding(Message message) {
+        String encoding = (String) message.get(Message.ENCODING);
+        if (encoding == null) {
+            Exchange ex = message.getExchange();
+            if (ex != null && ex.getInMessage() != null) {
+                encoding =
+                    (String) ex.getInMessage().get(Message.ENCODING);
+            }
+        }
+        if (encoding == null) {
+            encoding = StandardCharsets.UTF_8.name();
+        }
+        return encoding;
+    }
 }
diff --git a/core/src/main/java/org/apache/cxf/feature/transform/XSLTInInterceptor.java b/core/src/main/java/org/apache/cxf/feature/transform/XSLTInInterceptor.java
index 5e98493ffd..6ffd9e29ed 100644
--- a/core/src/main/java/org/apache/cxf/feature/transform/XSLTInInterceptor.java
+++ b/core/src/main/java/org/apache/cxf/feature/transform/XSLTInInterceptor.java
@@ -79,9 +79,12 @@ public class XSLTInInterceptor extends AbstractXSLTInterceptor {
     protected void transformXReader(Message message, XMLStreamReader xReader) {
         CachedOutputStream cachedOS = new CachedOutputStream();
         try {
+            String encoding = getEncoding(message);
             StaxUtils.copy(xReader, cachedOS);
-            InputStream transformedIS = XSLTUtils.transform(getXSLTTemplate(), cachedOS.getInputStream());
-            XMLStreamReader transformedReader = StaxUtils.createXMLStreamReader(transformedIS);
+            InputStream transformedIS = XSLTUtils.transform(
+                getXSLTTemplate(), cachedOS.getInputStream(), encoding);
+            XMLStreamReader transformedReader =
+                StaxUtils.createXMLStreamReader(transformedIS, encoding);
             message.setContent(XMLStreamReader.class, transformedReader);
         } catch (XMLStreamException e) {
             throw new Fault("STAX_COPY", LOG, e, e.getMessage());
@@ -103,9 +106,12 @@ public class XSLTInInterceptor extends AbstractXSLTInterceptor {
 
     protected void transformIS(Message message, InputStream is) {
         try (InputStream inputStream = is) {
-            message.setContent(InputStream.class, XSLTUtils.transform(getXSLTTemplate(), inputStream));
+            String encoding = getEncoding(message);
+            message.setContent(InputStream.class,
+                XSLTUtils.transform(getXSLTTemplate(), inputStream, encoding));
         } catch (IOException e) {
-            LOG.warning("Cannot close stream after transformation: " + e.getMessage());
+            LOG.warning("Cannot close stream after transformation: "
+                + e.getMessage());
         }
     }
 
diff --git a/core/src/main/java/org/apache/cxf/feature/transform/XSLTOutInterceptor.java b/core/src/main/java/org/apache/cxf/feature/transform/XSLTOutInterceptor.java
index a7f54c80f0..c4508cda7a 100644
--- a/core/src/main/java/org/apache/cxf/feature/transform/XSLTOutInterceptor.java
+++ b/core/src/main/java/org/apache/cxf/feature/transform/XSLTOutInterceptor.java
@@ -94,7 +94,10 @@ public class XSLTOutInterceptor extends AbstractXSLTInterceptor {
 
     protected void transformOS(Message message, OutputStream out) {
         CachedOutputStream wrapper = new CachedOutputStream();
-        CachedOutputStreamCallback callback = new XSLTCachedOutputStreamCallback(getXSLTTemplate(), out);
+        String encoding = getEncoding(message);
+        CachedOutputStreamCallback callback =
+            new XSLTCachedOutputStreamCallback(getXSLTTemplate(),
+                                               out, encoding);
         wrapper.registerCallback(callback);
         message.setContent(OutputStream.class, wrapper);
     }
@@ -144,10 +147,14 @@ public class XSLTOutInterceptor extends AbstractXSLTInterceptor {
     public static class XSLTCachedOutputStreamCallback implements CachedOutputStreamCallback {
         private final Templates xsltTemplate;
         private final OutputStream origStream;
+        private final String encoding;
 
-        public XSLTCachedOutputStreamCallback(Templates xsltTemplate, OutputStream origStream) {
+        public XSLTCachedOutputStreamCallback(Templates xsltTemplate,
+                                              OutputStream origStream,
+                                              String encoding) {
             this.xsltTemplate = xsltTemplate;
             this.origStream = origStream;
+            this.encoding = encoding;
         }
 
         @Override
@@ -159,7 +166,8 @@ public class XSLTOutInterceptor extends AbstractXSLTInterceptor {
             InputStream transformedStream;
             Exception exceptionOnClose = null;
             try {
-                transformedStream = XSLTUtils.transform(xsltTemplate, wrapper.getInputStream());
+                transformedStream = XSLTUtils.transform(
+                    xsltTemplate, wrapper.getInputStream(), encoding);
                 IOUtils.copyAndCloseInput(transformedStream, origStream);
             } catch (IOException e) {
                 throw new Fault("STREAM_COPY", LOG, e, e.getMessage());
diff --git a/core/src/main/java/org/apache/cxf/feature/transform/XSLTUtils.java b/core/src/main/java/org/apache/cxf/feature/transform/XSLTUtils.java
index 4b35b2ffb9..1b70ab3cf2 100644
--- a/core/src/main/java/org/apache/cxf/feature/transform/XSLTUtils.java
+++ b/core/src/main/java/org/apache/cxf/feature/transform/XSLTUtils.java
@@ -50,8 +50,18 @@ public final class XSLTUtils {
     }
 
     public static InputStream transform(Templates xsltTemplate, InputStream in) {
-        try (InputStream inputStream = in; CachedOutputStream out = new CachedOutputStream()) {
-            Source beforeSource = new StaxSource(StaxUtils.createXMLStreamReader(inputStream));
+        return transform(xsltTemplate, in, null);
+    }
+
+    public static InputStream transform(Templates xsltTemplate,
+                                        InputStream in,
+                                        String encoding) {
+        try (InputStream inputStream = in;
+             CachedOutputStream out = new CachedOutputStream()) {
+            Source beforeSource = new StaxSource(
+                encoding != null
+                    ? StaxUtils.createXMLStreamReader(inputStream, encoding)
+                    : StaxUtils.createXMLStreamReader(inputStream));
 
             Transformer trans = xsltTemplate.newTransformer();
             trans.transform(beforeSource, new StreamResult(out));
diff --git a/core/src/main/java/org/apache/cxf/interceptor/transform/TransformInInterceptor.java b/core/src/main/java/org/apache/cxf/interceptor/transform/TransformInInterceptor.java
index 1c0eb847b5..cb69d1bebc 100644
--- a/core/src/main/java/org/apache/cxf/interceptor/transform/TransformInInterceptor.java
+++ b/core/src/main/java/org/apache/cxf/interceptor/transform/TransformInInterceptor.java
@@ -21,12 +21,14 @@ package org.apache.cxf.interceptor.transform;
 
 
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Map;
 
 import javax.xml.stream.XMLStreamReader;
 
 import org.apache.cxf.interceptor.StaxInInterceptor;
+import org.apache.cxf.message.Exchange;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.message.MessageUtils;
 import org.apache.cxf.phase.AbstractPhaseInterceptor;
@@ -71,26 +73,51 @@ public class TransformInInterceptor extends AbstractPhaseInterceptor<Message> {
 
     public void handleMessage(Message message) {
         if (contextPropertyName != null
-            && !MessageUtils.getContextualBoolean(message, contextPropertyName, false)) {
+            && !MessageUtils.getContextualBoolean(message,
+                contextPropertyName, false)) {
             return;
         }
-        XMLStreamReader reader = message.getContent(XMLStreamReader.class);
+        XMLStreamReader reader =
+            message.getContent(XMLStreamReader.class);
         InputStream is = message.getContent(InputStream.class);
 
-        XMLStreamReader transformReader = createTransformReaderIfNeeded(reader, is);
+        String encoding = getEncoding(message);
+        XMLStreamReader transformReader =
+            createTransformReaderIfNeeded(reader, is, encoding);
         if (transformReader != null) {
             message.setContent(XMLStreamReader.class, transformReader);
         }
 
     }
 
-    protected XMLStreamReader createTransformReaderIfNeeded(XMLStreamReader reader, InputStream is) {
-        return TransformUtils.createTransformReaderIfNeeded(reader, is,
-                                                            inDropElements,
-                                                            inElementsMap,
-                                                            inAppendMap,
-                                                            inAttributesMap,
-                                                            blockOriginalReader);
+    protected XMLStreamReader createTransformReaderIfNeeded(
+            XMLStreamReader reader,
+            InputStream is,
+            String encoding) {
+        return TransformUtils.createTransformReaderIfNeeded(
+            reader, is,
+            inDropElements,
+            inElementsMap,
+            inAppendMap,
+            inAttributesMap,
+            blockOriginalReader,
+            encoding);
+    }
+
+    private String getEncoding(Message message) {
+        String encoding =
+            (String) message.get(Message.ENCODING);
+        if (encoding == null) {
+            Exchange ex = message.getExchange();
+            if (ex != null && ex.getInMessage() != null) {
+                encoding = (String) ex.getInMessage()
+                    .get(Message.ENCODING);
+            }
+        }
+        if (encoding == null) {
+            encoding = StandardCharsets.UTF_8.name();
+        }
+        return encoding;
     }
 
     public void setInAppendElements(Map<String, String> inElements) {
diff --git a/core/src/main/java/org/apache/cxf/staxutils/transform/TransformUtils.java b/core/src/main/java/org/apache/cxf/staxutils/transform/TransformUtils.java
index dfe6051b1d..cbd98fbb44 100644
--- a/core/src/main/java/org/apache/cxf/staxutils/transform/TransformUtils.java
+++ b/core/src/main/java/org/apache/cxf/staxutils/transform/TransformUtils.java
@@ -42,6 +42,17 @@ public final class TransformUtils {
         return reader == null ? StaxUtils.createXMLStreamReader(is) : reader;
     }
 
+    public static XMLStreamReader createNewReaderIfNeeded(
+            XMLStreamReader reader, InputStream is, String encoding) {
+        if (reader != null) {
+            return reader;
+        }
+        if (encoding != null) {
+            return StaxUtils.createXMLStreamReader(is, encoding);
+        }
+        return StaxUtils.createXMLStreamReader(is);
+    }
+
     public static XMLStreamWriter createNewWriterIfNeeded(XMLStreamWriter writer, OutputStream os) {
         return createNewWriterIfNeeded(writer, os, null);
     }
@@ -106,9 +117,22 @@ public final class TransformUtils {
                                                                 Map<String, String> inAppendMap,
                                                                 Map<String, String> inAttributesMap,
                                                                 boolean blockOriginalReader) {
+        return createTransformReaderIfNeeded(reader, is,
+                          inDropElements, inElementsMap, inAppendMap,
+                          inAttributesMap, blockOriginalReader, null);
+    }
+
+    public static XMLStreamReader createTransformReaderIfNeeded(XMLStreamReader reader,
+                                                                InputStream is,
+                                                                List<String> inDropElements,
+                                                                Map<String, String> inElementsMap,
+                                                                Map<String, String> inAppendMap,
+                                                                Map<String, String> inAttributesMap,
+                                                                boolean blockOriginalReader,
+                                                                String encoding) {
         if (inElementsMap != null || inAppendMap != null || inDropElements != null
             || inAttributesMap != null) {
-            reader = new InTransformReader(createNewReaderIfNeeded(reader, is),
+            reader = new InTransformReader(createNewReaderIfNeeded(reader, is, encoding),
                                            inElementsMap, inAppendMap, inDropElements,
                                            inAttributesMap, blockOriginalReader);
         }
diff --git a/core/src/test/java/org/apache/cxf/feature/transform/XSLTInterceptorsTest.java b/core/src/test/java/org/apache/cxf/feature/transform/XSLTInterceptorsTest.java
index 72f6b2b786..8f40071fc9 100644
--- a/core/src/test/java/org/apache/cxf/feature/transform/XSLTInterceptorsTest.java
+++ b/core/src/test/java/org/apache/cxf/feature/transform/XSLTInterceptorsTest.java
@@ -144,6 +144,40 @@ public class XSLTInterceptorsTest {
         Assert.assertTrue("Message was not transformed", checkTransformedXML(doc));
     }
 
+    @Test
+    public void inStreamWithEncodingTest() throws Exception {
+        InputStream iso88591IS = ClassLoaderUtils
+            .getResourceAsStream("message-iso-8859-1.xml",
+                                 this.getClass());
+        message.setContent(InputStream.class, iso88591IS);
+        message.put(Message.ENCODING, "ISO-8859-1");
+        inInterceptor.handleMessage(message);
+        InputStream transformedIS =
+            message.getContent(InputStream.class);
+        Document doc = StaxUtils.read(transformedIS);
+        Assert.assertTrue("Message was not transformed",
+            checkTransformedXML(doc));
+    }
+
+    @Test
+    public void outStreamWithEncodingTest() throws Exception {
+        CachedOutputStream cos = new CachedOutputStream();
+        cos.holdTempFile();
+        message.setContent(OutputStream.class, cos);
+        message.put(Message.ENCODING, "ISO-8859-1");
+        outInterceptor.handleMessage(message);
+        InputStream iso88591IS = ClassLoaderUtils
+            .getResourceAsStream("message-iso-8859-1.xml",
+                                 this.getClass());
+        OutputStream os = message.getContent(OutputStream.class);
+        IOUtils.copy(iso88591IS, os);
+        os.close();
+        cos.releaseTempFileHold();
+        Document doc = StaxUtils.read(cos.getInputStream());
+        Assert.assertTrue("Message was not transformed",
+            checkTransformedXML(doc));
+    }
+
     private boolean checkTransformedXML(Document doc) {
         NodeList list = doc.getDocumentElement()
             .getElementsByTagNameNS("http://customerservice.example.com/", "getCustomersByName1");
diff --git a/core/src/test/java/org/apache/cxf/feature/transform/message-iso-8859-1.xml b/core/src/test/java/org/apache/cxf/feature/transform/message-iso-8859-1.xml
new file mode 100644
index 0000000000..e13197d89e
--- /dev/null
+++ b/core/src/test/java/org/apache/cxf/feature/transform/message-iso-8859-1.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
+    <soap:Body>
+        <cus:getCustomersByName xmlns:cus="http://customerservice.example.com/">
+            <name xmlns:ns2="http://customerservice.example.com/">M&#xFC;ller</name>
+        </cus:getCustomersByName>
+    </soap:Body>
+</soap:Envelope>