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:
testMime()
, which creates a quoted-printable
part, then displays it correctly (with top bit set characters escaped with sequences beginning with =
);stockQuoteTest
, which simply gets a new web service entry point - it need not actually call the web service;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.
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)
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