Search code examples
javasoapjax-wsjakarta-mailquoted-printable

Using a Java SOAP service breaks javax.mail's quoted printable mail encapsulation


This has had me puzzled for many hours, and seems inherently unlikely, but please bear with me; I believe I have proof.

I have simplified this down to a minimal test case (below). To test the code, you will need to use EmailTest.java (below), then import a SOAP service. To do this I used:

wsimport -d bin -s src -keep -extension 'http://www.webservicex.net/stockquote.asmx?wsdl'

For convenience I've also put the code on github here, so you can simply clone it and run it; this has the wsimport already performed.

What the code below does is as follows:

  • calls testMime(), which creates a quoted-printable part, then displays it correctly (with top bit set characters escaped with sequences beginning with =);
  • calls stockQuoteTest, which simply gets a new web service entry point - it need not actually call the web service;
  • calls testMime() again, which creates a quoted-printable part, but displays it incorrectly, not escaping the characters; system.out.Println then (predictably) replaces them with ?.

I've used multiple SOAP libraries all of which do the same thing - I picked a simple demo one to demonstrate. It is not necessary actually to call the SOAP endpoint. The call to getPort (here in getStockQuoteSoap12) appears to be the problem.

My debugging so far has determined that none of the default Java encodings I know about have changed, and that the problem appears to be that on the second call the quoted-printable encoder never gets called. This appears to be because part.dh.dataContentHandler gets set to something of type ObjectDataContentHandler when it works, and of type StringDataContentHandler when it doesn't work.

Any ideas on what is going on here or how to fix it?

I'm using javax.mail 1.5.2 (the newest, but but an older version was the same) - and

$ java -version
java version "1.6.0_65"
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-462-11M4609)
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-462, mixed mode)

Here's EmailTest.java

package emailtest;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;

// use any SOAP service. This one was generated using
//   wsimport -d bin -s src -keep -extension http://www.webservicex.net/stockquote.asmx?wsdl
import net.webservicex.StockQuote;
import net.webservicex.StockQuoteSoap;

public class Emailtest {

    public static void stockQuoteTest() {
        StockQuote stockService = new StockQuote();
        StockQuoteSoap s = stockService.getStockQuoteSoap12();
        // We don't actually need to call SOAP to demonstrate the problem
        // System.out.println("quote is "+s.getQuote("GOOG"));
    }

    public static void testMime() throws MessagingException {
        String msg = "\u0287x\u01DD\u0287 u\u028Dop \u01DDp\u1D09sdn";

        MimeBodyPart messageBodyPart = new MimeBodyPart();
        messageBodyPart.setText("" + msg, "utf-8");
        messageBodyPart.setHeader("Content-Type", "text/plain; charset=\"utf-8\"");
        messageBodyPart.setHeader("Content-Transfer-Encoding", "quoted-printable");

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            messageBodyPart.writeTo(os);
            String aString = new String(os.toByteArray(),"UTF-8");
            System.out.println(aString);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("");
    }

    public static void main(String args[]) {
        try {
            System.out.println("Before call to SOAP:");
            testMime();

            stockQuoteTest();

            System.out.println("After call to SOAP:");
            testMime();
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }
}

Here's the output:

Before call to SOAP:
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

=CA=87x=C7=9D=CA=87 u=CA=8Dop =C7=9Dp=E1=B4=89sdn

After call to SOAP:
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

?x?? u?op ?p?sdn

Oh, and in case it matters, the encoded text is:

ʇxǝʇ uʍop ǝpᴉsdn

You can see where quoted-printable is failing to work (second call) that the 'upside down' letters which are created using normal letters show through. Investigation shows it's simply not doing quoted-printable at all.

Update

This would appear to be a bug in jax-ws and arguably a bug in javax.mail for relying on the text/plain handler that happens to be in JAF rather its own. Any ideas for a work-around? (using Java 7, where is is allegedly fixed in jax-ws is not an option)


Solution

  • I believe this is because of this bug in JAX-WS (allegedly fixed in Java 7).

    JAX-WS uses JAF to introduce a DataHandler for type text/plain that does not respect the character set (i.e. text/plain; charset="utf-8". When the JAX-WS class is loaded, this is used rather than the text/plain handler within javax.mail. This then uses the java platform default encoding, rather than the encoding specified.

    Two possible work-arounds are:

    • Upgrade to Java 7 (specifically for JAX-WS)

    • Launch the application with -Dfile.encoding=UTF-8 on the command line to set the Java default file encoding; obviously this may have other consequences