You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commons-dev@ws.apache.org by ve...@apache.org on 2009/01/26 16:14:34 UTC

svn commit: r737710 - in /webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src: main/java/org/apache/ws/commons/tcpmon/core/ main/java/org/apache/ws/commons/tcpmon/core/filter/ main/java/org/apache/ws/commons/tcpmon/core/filter/http/ main/j...

Author: veithen
Date: Mon Jan 26 15:14:33 2009
New Revision: 737710

URL: http://svn.apache.org/viewvc?rev=737710&view=rev
Log:
Decode charset encoding when in pretty print ("XML Format") mode.

Added:
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilter.java   (with props)
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilterTest.java   (with props)
Modified:
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/AbstractConnection.java
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetDecoderFilter.java
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/Pipeline.java
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/ReadOnlyStream.java
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/Stream.java
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/http/HttpFilter.java
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/ContentFilterFactory.java
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/DefaultContentFilterFactory.java
    webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/MimePartFilter.java

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/AbstractConnection.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/AbstractConnection.java?rev=737710&r1=737709&r2=737710&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/AbstractConnection.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/AbstractConnection.java Mon Jan 26 15:14:33 2009
@@ -37,6 +37,7 @@
 import java.io.StringWriter;
 import java.io.Writer;
 import java.net.Socket;
+import java.nio.charset.Charset;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -45,6 +46,8 @@
  * a connection listens to a single current connection
  */
 public abstract class AbstractConnection extends Thread {
+    private static final Charset UTF8 = Charset.forName("utf-8");
+    
     private final Configuration config;
 
     /**
@@ -181,7 +184,7 @@
                 filter.setContentFilterFactory(new DefaultContentFilterFactory());
                 requestPipeline.addFilter(filter);
             }
-            requestPipeline.addFilter(new CharsetDecoderFilter(inputWriter));
+            requestPipeline.addFilter(new CharsetDecoderFilter(inputWriter, UTF8));
             
             // If we act as a proxy, we first need to read the start of the request before
             // the outSocket is available.
@@ -202,7 +205,7 @@
                 filter.setContentFilterFactory(new DefaultContentFilterFactory());
                 responsePipeline.addFilter(filter);
             }
-            responsePipeline.addFilter(new CharsetDecoderFilter(outputWriter));
+            responsePipeline.addFilter(new CharsetDecoderFilter(outputWriter, UTF8));
             
             // this is the channel to the endpoint
             rr1 = new SocketRR(this, inSocket, tmpIn1, outSocket, tmpOut2, requestPipeline);

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetDecoderFilter.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetDecoderFilter.java?rev=737710&r1=737709&r2=737710&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetDecoderFilter.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetDecoderFilter.java Mon Jan 26 15:14:33 2009
@@ -18,26 +18,44 @@
 
 import java.io.IOException;
 import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
 
 /**
  * Filter that decodes the stream to character data and sends it to a {@link Writer}.
  */
 public class CharsetDecoderFilter implements StreamFilter {
     private final Writer writer;
+    private final CharsetDecoder decoder;
+    private final ByteBuffer inBuffer = ByteBuffer.allocate(64);
+    private final CharBuffer outBuffer = CharBuffer.allocate(64);
 
-    public CharsetDecoderFilter(Writer writer) {
+    public CharsetDecoderFilter(Writer writer, Charset charset) {
         this.writer = writer;
+        decoder = charset.newDecoder();
+    }
+    
+    public CharsetDecoderFilter(Writer writer, String charsetName) {
+        this(writer, Charset.forName(charsetName));
     }
 
     public void invoke(Stream stream) {
-        StringBuffer buffer = new StringBuffer(stream.available());
-        while (stream.available() > 0) {
-            buffer.append((char)stream.skip());
-        }
-        try {
-            writer.write(buffer.toString());
-        } catch (IOException ex) {
-            throw new StreamException(ex);
-        }
+        CoderResult coderResult;
+        do {
+            stream.skip(stream.read(inBuffer));
+            inBuffer.flip();
+            coderResult = decoder.decode(inBuffer, outBuffer, stream.isEndOfStream());
+            outBuffer.flip();
+            try {
+                writer.write(outBuffer.array(), outBuffer.position(), outBuffer.remaining());
+            } catch (IOException ex) {
+                throw new StreamException(ex);
+            }
+            outBuffer.clear();
+            inBuffer.compact();
+        } while (stream.available() > 0 || coderResult == CoderResult.OVERFLOW);
     }
 }

Added: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilter.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilter.java?rev=737710&view=auto
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilter.java (added)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilter.java Mon Jan 26 15:14:33 2009
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ws.commons.tcpmon.core.filter;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+
+/**
+ * Filter that changes the charset encoding of a stream.
+ */
+public class CharsetRecoderFilter implements StreamFilter {
+    private final CharsetDecoder decoder;
+    private final CharsetEncoder encoder;
+    private final ByteBuffer inBuffer = ByteBuffer.allocate(64);
+    private final CharBuffer charBuffer = CharBuffer.allocate(64);
+    private final ByteBuffer outBuffer = ByteBuffer.allocate(64);
+    
+    public CharsetRecoderFilter(Charset fromCharset, Charset toCharset) {
+        decoder = fromCharset.newDecoder();
+        encoder = toCharset.newEncoder();
+    }
+    
+    public CharsetRecoderFilter(String fromCharset, String toCharset) {
+        this(Charset.forName(fromCharset), Charset.forName(toCharset));
+    }
+    
+    public void invoke(Stream stream) {
+        CoderResult inResult;
+        do {
+            stream.discard(stream.read(inBuffer));
+            inBuffer.flip();
+            inResult = decoder.decode(inBuffer, charBuffer, stream.isEndOfStream());
+            charBuffer.flip();
+            CoderResult outResult;
+            do {
+                outResult = encoder.encode(charBuffer, outBuffer, stream.isEndOfStream() && inResult == CoderResult.UNDERFLOW);
+                outBuffer.flip();
+                stream.insert(outBuffer);
+                outBuffer.compact();
+            } while (outResult == CoderResult.OVERFLOW);
+            inBuffer.compact();
+            charBuffer.compact();
+        } while (stream.available() > 0 || inResult == CoderResult.OVERFLOW);
+    }
+}

Propchange: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/Pipeline.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/Pipeline.java?rev=737710&r1=737709&r2=737710&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/Pipeline.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/Pipeline.java Mon Jan 26 15:14:33 2009
@@ -19,6 +19,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.ByteBuffer;
 import java.util.LinkedList;
 
 /**
@@ -211,6 +212,12 @@
             return c;
         }
 
+        public int read(ByteBuffer buffer) {
+            int c = Math.min(buffer.remaining(), inLength);
+            buffer.put(inBuffer, inOffset, c);
+            return c;
+        }
+
         public void readAll(OutputStream out) throws IOException {
             out.write(inBuffer, inOffset, inLength);
         }
@@ -229,7 +236,7 @@
             inLength -= len;
         }
 
-        public void insert(byte b) {
+        private void prepareOutput() {
             flushSkip(false);
             if (outLength > 0 && outLength == outBuffer.length) {
                 flushOutput(false);
@@ -237,6 +244,10 @@
             if (outBuffer == null) {
                 outBuffer = allocateBuffer();
             }
+        }
+
+        public void insert(byte b) {
+            prepareOutput();
             outBuffer[outLength++] = b;
         }
         
@@ -246,6 +257,15 @@
             invokeNext(buffer, offset, length, false, true);
         }
 
+        public void insert(ByteBuffer buffer) {
+            while (buffer.hasRemaining()) {
+                prepareOutput();
+                int c = Math.min(outBuffer.length-outLength, buffer.remaining());
+                buffer.get(outBuffer, outLength, c);
+                outLength += c;
+            }
+        }
+
         public byte skip() {
             byte b = inBuffer[inOffset];
             skip(1);

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/ReadOnlyStream.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/ReadOnlyStream.java?rev=737710&r1=737709&r2=737710&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/ReadOnlyStream.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/ReadOnlyStream.java Mon Jan 26 15:14:33 2009
@@ -18,6 +18,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.nio.ByteBuffer;
 
 public class ReadOnlyStream implements Stream {
     private final Stream parent;
@@ -56,6 +57,10 @@
         // Ignore this
     }
 
+    public void insert(ByteBuffer buffer) {
+        // Ignore this
+    }
+
     public boolean isEndOfStream() {
         return parent.isEndOfStream();
     }
@@ -64,6 +69,10 @@
         return parent.read(buffer, offset, length);
     }
 
+    public int read(ByteBuffer buffer) {
+        return parent.read(buffer);
+    }
+
     public void readAll(OutputStream out) throws IOException {
         parent.readAll(out);
     }

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/Stream.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/Stream.java?rev=737710&r1=737709&r2=737710&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/Stream.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/Stream.java Mon Jan 26 15:14:33 2009
@@ -18,6 +18,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.nio.ByteBuffer;
 
 /**
  * Interface used by a filter to access the stream flowing through a pipeline.
@@ -78,6 +79,20 @@
     int read(byte[] buffer, int offset, int length);
     
     /**
+     * Read data from the stream into a byte buffer, starting from the
+     * current position in the stream.
+     * Calling this method will not modify the current position in
+     * the stream.
+     * The number of bytes read is only limited by the number of available
+     * bytes in the stream and the remaining bytes in the buffer (as returned
+     * by {@link ByteBuffer#remaining()}.
+     * 
+     * @param buffer the buffer into which the data is read
+     * @return the total number of bytes read into the buffer
+     */
+    int read(ByteBuffer buffer);
+    
+    /**
      * Read all currently available data from the stream and
      * copy it to an {@link OutputStream} object.
      * Calling this method will not modify the current position in
@@ -128,6 +143,15 @@
     void insert(byte[] buffer, int offset, int length);
     
     /**
+     * Insert the content of a byte buffer at the current position in the stream.
+     * The logical position after invocation of this method will
+     * be just after the last inserted byte.
+     * 
+     * @param buffer the byte buffer containing the sequence to be inserted in the stream
+     */
+    void insert(ByteBuffer buffer);
+    
+    /**
      * Skip the byte at the current position in the stream.
      * This will increment the current position and copy the byte
      * to the next filter.

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/http/HttpFilter.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/http/HttpFilter.java?rev=737710&r1=737709&r2=737710&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/http/HttpFilter.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/http/HttpFilter.java Mon Jan 26 15:14:33 2009
@@ -44,7 +44,7 @@
     private int state = STATE_FIRST_LINE;
     private ContentFilterFactory contentFilterFactory;
     private EntityProcessor transferDecoder;
-    private StreamFilter contentFilter;
+    private StreamFilter[] contentFilterChain;
     
     public HttpFilter(boolean decodeTransferEncoding) {
         this.decodeTransferEncoding = decodeTransferEncoding;
@@ -87,8 +87,10 @@
                 case STATE_HEADER: {
                     if (headerProcessor.process(stream)) {
                         state = STATE_CONTENT;
-                        if (contentFilter != null) {
-                            stream.pushFilter(contentFilter);
+                        if (contentFilterChain != null) {
+                            for (int i=contentFilterChain.length-1; i>=0; i--) {
+                                stream.pushFilter(contentFilterChain[i]);
+                            }
                         }
                         break;
                     } else {
@@ -101,8 +103,10 @@
                                 decodeTransferEncoding ? stream : new ReadOnlyStream(stream);
                         if (transferDecoder.process(decoderStream)) {
                             state = STATE_COMPLETE;
-                            if (contentFilter != null) {
-                                stream.popFilter();
+                            if (contentFilterChain != null) {
+                                for (int i=0; i<contentFilterChain.length; i++) {
+                                    stream.popFilter();
+                                }
                             }
                         }
                         break;
@@ -126,7 +130,7 @@
             }
         } else if (name.equalsIgnoreCase("Content-Type")) {
             if (contentFilterFactory != null) {
-                contentFilter = contentFilterFactory.getContentFilter(value);
+                contentFilterChain = contentFilterFactory.getContentFilterChain(value);
             }
         }
         for (Iterator it = handlers.iterator(); it.hasNext(); ) {

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/ContentFilterFactory.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/ContentFilterFactory.java?rev=737710&r1=737709&r2=737710&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/ContentFilterFactory.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/ContentFilterFactory.java Mon Jan 26 15:14:33 2009
@@ -23,12 +23,12 @@
  */
 public interface ContentFilterFactory {
     /**
-     * Get a new filter for the given content type.
+     * Get a new filter (chain) for the given content type.
      * 
      * @param contentType the content type
-     * @return the filter to apply to the content or
-     *         <code>null</code> if no filter should
-     *         be applied
+     * @return the filters to apply to the content or
+     *         <code>null</code> (or an empty array) if no
+     *         filter should be applied
      */
-    StreamFilter getContentFilter(String contentType);
+    StreamFilter[] getContentFilterChain(String contentType);
 }

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/DefaultContentFilterFactory.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/DefaultContentFilterFactory.java?rev=737710&r1=737709&r2=737710&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/DefaultContentFilterFactory.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/DefaultContentFilterFactory.java Mon Jan 26 15:14:33 2009
@@ -16,6 +16,17 @@
 
 package org.apache.ws.commons.tcpmon.core.filter.mime;
 
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.activation.MimeType;
+import javax.activation.MimeTypeParseException;
+
+import org.apache.ws.commons.tcpmon.core.filter.CharsetRecoderFilter;
 import org.apache.ws.commons.tcpmon.core.filter.StreamFilter;
 import org.apache.ws.commons.tcpmon.core.filter.XmlFormatFilter;
 
@@ -23,16 +34,34 @@
  * Default {@link ContentFilterFactory} implementation.
  */
 public class DefaultContentFilterFactory implements ContentFilterFactory {
-    public StreamFilter getContentFilter(String contentType) {
-        int idx = contentType.indexOf(';');
-        String baseContentType = (idx == -1 ? contentType : contentType.substring(0, idx)).trim().toLowerCase();
-        if (baseContentType.equals("text/xml") || baseContentType.equals("application/xml")
-                || baseContentType.equals("application/soap+xml") || baseContentType.equals("application/xop+xml")) {
-            return new XmlFormatFilter(3);
-        } else if (baseContentType.equals("multipart/related")) {
-            return new MultipartFilter(this, contentType);
-        } else {
+    private static final Set xmlContentTypes = new HashSet(Arrays.asList(new String[] {
+            "text/xml", "application/xml", "application/soap+xml", "application/xop+xml" }));
+    private static final Charset UTF8 = Charset.forName("utf-8");
+    
+    public StreamFilter[] getContentFilterChain(String contentType) {
+        MimeType ctype;
+        try {
+            ctype = new MimeType(contentType);
+        } catch (MimeTypeParseException ex) {
             return null;
         }
+        String baseType = ctype.getBaseType().toLowerCase();
+        boolean isXml = xmlContentTypes.contains(baseType);
+        List filters = new ArrayList(2);
+        if (isXml || ctype.getPrimaryType().equalsIgnoreCase("text")) {
+            String charsetName = ctype.getParameter("charset");
+            if (charsetName != null) {
+                Charset charset = Charset.forName(charsetName);
+                if (!charset.equals(UTF8)) {
+                    filters.add(new CharsetRecoderFilter(charset, UTF8));
+                }
+            }
+        }
+        if (isXml) {
+            filters.add(new XmlFormatFilter(3));
+        } else if (baseType.equals("multipart/related")) {
+            filters.add(new MultipartFilter(this, contentType));
+        }
+        return filters.isEmpty() ? null : (StreamFilter[])filters.toArray(new StreamFilter[filters.size()]);
     }
 }

Modified: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/MimePartFilter.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/MimePartFilter.java?rev=737710&r1=737709&r2=737710&view=diff
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/MimePartFilter.java (original)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/main/java/org/apache/ws/commons/tcpmon/core/filter/mime/MimePartFilter.java Mon Jan 26 15:14:33 2009
@@ -26,14 +26,14 @@
  */
 public class MimePartFilter implements StreamFilter {
     private HeaderProcessor headerProcessor;
-    private StreamFilter contentFilter;
+    private StreamFilter[] contentFilterChain;
 
     public MimePartFilter(final ContentFilterFactory contentFilterFactory) {
         headerProcessor = new HeaderProcessor();
         headerProcessor.addHandler(new HeaderHandler() {
             public String handleHeader(String name, String value) {
                 if (name.equalsIgnoreCase("Content-Type")) {
-                    contentFilter = contentFilterFactory.getContentFilter(value);
+                    contentFilterChain = contentFilterFactory.getContentFilterChain(value);
                 }
                 return value;
             }
@@ -47,8 +47,10 @@
             } else {
                 if (headerProcessor.process(stream)) {
                     headerProcessor = null;
-                    if (contentFilter != null) {
-                        stream.pushFilter(contentFilter);
+                    if (contentFilterChain != null) {
+                        for (int i=contentFilterChain.length-1; i>=0; i--) {
+                            stream.pushFilter(contentFilterChain[i]);
+                        }
                     }
                 } else {
                     return;

Added: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilterTest.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilterTest.java?rev=737710&view=auto
==============================================================================
--- webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilterTest.java (added)
+++ webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilterTest.java Mon Jan 26 15:14:33 2009
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ws.commons.tcpmon.core.filter;
+
+import junit.framework.TestCase;
+
+public class CharsetRecoderFilterTest extends TestCase {
+    private static final String latin1TestString;
+    
+    static {
+        StringBuffer buffer = new StringBuffer();
+        for (int i=0; i<20; i++) {
+            for (char c=32; c<256; c++) {
+                buffer.append(c);
+            }
+        }
+        latin1TestString = buffer.toString();
+    }
+    
+    private void test(String s, String fromCharset, String toCharset) throws Exception {
+        byte[] in = s.getBytes(fromCharset);
+        byte[] out = TestUtil.filter(new CharsetRecoderFilter(fromCharset, toCharset), in);
+        assertEquals(s, new String(out, toCharset));
+    }
+    
+    public void testLatin1ToUTF8() throws Exception {
+        test(latin1TestString, "iso-8859-1", "utf-8");
+    }
+
+    public void testUTF8ToLatin1() throws Exception {
+        test(latin1TestString, "utf-8", "iso-8859-1");
+    }
+}

Propchange: webservices/commons/trunk/modules/tcpmon/modules/tcpmon-core/src/test/java/org/apache/ws/commons/tcpmon/core/filter/CharsetRecoderFilterTest.java
------------------------------------------------------------------------------
    svn:eol-style = native