DCH class error with JavaMail
I'm attempting to set up a simple logging test with JavaMail in Java EE 6, using the jar files provided with Glassfish 3.1. There seems to be a wealth of questions out there on this subject, but I haven't found any answers that have helped yet. My test code looks like this:
import java.util.logging.Logger;
public class MyClass {
private static final Logger LOGGER = Logger.getLogger("MyClass");
public static void main(String[] args) {
LOGGER.severe("This is a test");
}
}
My logging.properties file contains this:
com.sun.mail.util.logging.MailHandler.mail.smtp.host={my mail hub FQDN}
com.sun.mail.util.logging.MailHandler.mail.smtp.port=25
com.sun.mail.util.logging.MailHandler.mail.to={me}
com.sun.mail.util.logging.MailHandler.mail.from={support address}
com.sun.mail.util.logging.MailHandler.level=WARNING
com.sun.mail.util.logging.MailHandler.verify=local
com.sun.mail.util.logging.MailHandler.subject=Application Error
com.sun.mail.util.logging.MailHandler.formatter=java.util.logging.SimpleFormatter
I build the class using:
javac -cp $AS_INSTALL/glassfish/modules/javax.mail.jar:$AS_INSTALL/install/lib/external/jaxb/activation.jar:. MyClass.java
Then I run the program using:
java -cp $AS_INSTALL/glassfish/modules/javax.mail.jar:$AS_INSTALL/install/lib/external/jaxb/activation.jar:. -Djava.util.logging.config.file=logging.properties MyClass
This results in the following error:
Sep 22, 2011 4:19:25 PM MyClass main
SEVERE: This is a test
java.util.logging.ErrorManager: 3: SEVERE: no object DCH for MIME type multipart/mixed;
boundary="----=_Part_1_26867996.1316722766145"
javax.activation.UnsupportedDataTypeException: no object DCH for MIME type multipart/mixed;
boundary="----=_Part_1_26867996.1316722766145"
at javax.activation.ObjectDataContentHandler.writeTo(DataHandler.java:877)
at javax.activation.DataHandler.writeTo(DataHandler.java:302)
at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1476)
at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1772)
at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1748)
at com.sun.mail.util.logging.MailHandler.toRawString(MailHandler.java:2196)
at com.sun.mail.util.logging.MailHandler.send(MailHandler.java:1597)
at com.sun.mail.util.logging.MailHandler.close(MailHandler.java:552)
at java.util.logging.LogManager.resetLogger(LogManager.java:693)
at java.util.logging.LogManager.reset(LogManager.java:676)
at java.util.logging.LogManager$Cleaner.run(LogManager.java:221)
javax.mail.MessagingException: IOException while sending message;
nested exception is:
javax.activation.UnsupportedDataTypeException: no object DCH for MIME type multipart/mixed;
boundary="----=_Part_1_26867996.1316722766145"
at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1141)
at javax.mail.Transport.send0(Transport.java:195)
at javax.mail.Transport.send(Transport.java:124)
at com.sun.mail.util.logging.MailHandler.send(MailHandler.java:1594)
at com.sun.mail.util.logging.MailHandler.close(MailHandler.java:552)
at java.util.logging.LogManager.resetLogger(LogManager.java:693)
at java.util.logging.LogManager.reset(LogManager.java:676)
at java.util.logging.LogManager$Cleaner.run(LogManager.java:221)
Caused by: javax.activation.UnsupportedDataTypeException: no object DCH for MIME type multipart/mixed;
boundary="----=_Part_1_26867996.1316722766145"
at javax.activation.ObjectDataContentHandler.writeTo(DataHandler.java:877)
at javax.activation.DataHandler.writeTo(DataHandler.java:302)
at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1476)
at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1772)
at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1099)
... 7 more
I've verified that my javax.mail.jar file contains the multipart handler:
unzip -l $AS_INSTALL/glassfish/modules/javax.mail.jar | grep multipart
2617 01-14-2011 15:37 com/sun/mail/handlers/multipart_mixed.class
I've even run the program with the activation debugging enabled. This shows me the following related parts:
parse: multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed; x-java-fallb开发者_如何学Cack-entry=true
Type: multipart/*
Command: content-handler, Class: com.sun.mail.handlers.multipart_mixed
MailcapCommandMap: createDataContentHandler for multipart/mixed
search DB #1
search DB #2
search fallback DB #1
got content-handler
class com.sun.mail.handlers.multipart_mixed
Can't load DCH com.sun.mail.handlers.multipart_mixed; Exception: java.lang.ClassNotFoundException: com/sun/mail/handlers/multipart_mixed
I even get a duplicate of the above for type text/plain.
MailcapCommandMap: createDataContentHandler for text/plain
search DB #1
got content-handler
class com.sun.mail.handlers.text_plain
Can't load DCH com.sun.mail.handlers.text_plain; Exception: java.lang.ClassNotFoundException: com/sun/mail/handlers/text_plain
What am I missing here?
Thanks, Steve
Add these before you send your message:
MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
mc.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html");
mc.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");
mc.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain");
mc.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed");
mc.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822");
CommandMap.setDefaultCommandMap(mc);
I got the problem in my Android app and it works.
In my case I was able to solve this by adding this before send():
Thread.currentThread().setContextClassLoader( getClass().getClassLoader() );
This was suggested in linked blog, so if you want to know more details, read it. Thanks to Jerry Gu for linking it here and original blogger.
URL: http://blog.hpxn.net/2009/12/02/tomcat-java-6-and-javamail-cant-load-dch/
I found the solution here:
http://blog.hpxn.net/2009/12/02/tomcat-java-6-and-javamail-cant-load-dch/
Though I would love to know more about the details behind why this is an issue and what this -Xbootclasspath option is doing to correct the problem. If I run my class like this:
java -Djava.util.logging.config.file=logging.properties -Xbootclasspath/p:/app/glassfish-3.1/glassfish/modules/javax.mail.jar MyClass
It finds the necessary classes and I get my email. Now I just have to figure out how to translate this configuration into my Glassfish server and try a more "real" test from this simple test case.
Though I would love to know more about the details behind why this is an issue and what this -Xbootclasspath option is doing to correct the problem.
It has to do with classloader tree. Recall that child classloaders are allowed to look up classes in the parent class loader but not the other way around. In your sample program, the classloader tree looks like this:
Running under JDK6, JDK7, and JDK8
+---Boot ClassLoader--+
| java.util.logging.* |
| javax.activation .* |
+---------------------+
^
|
+-----System ClassLoader------+
| javax.mail.* |
| com.sun.mail.handlers.* |
| com.sun.mail.util.logging.* |
+-----------------------------+
When the LogManager$Cleaner
shutdown hook runs (JDK6+) the context classloader is forced to boot classloader which is unable to locate the com.sun.mail.handlers.text_plain
class because it is in a child classloader. Because of this, modifying the MailcapCommandMap
to include the mailcap class names won't fix the problem. When you use the -Xbootclasspath
option you are placing all relevant classes in the boot class loader which is visible to the LogManager$Cleaner
. However, don't modify your system to use -Xbootclasspath
to fix this issue.
Instead, upgrade to JavaMail 1.5.3 or later which contains the fix for Bug K6552 | GH133 - Use classloader ergonomics in the MailHandler. If you want to upgrade JavaMail module of GlassFish, you can replace the glassfish-X.X/glassfish/modules/javax.mail.jar
with a newer version of JavaMail.
The incomplete fix applied to JavaMail 1.4.7 was to set the context class loader to the class loader that loaded the MailHandler
during close. The assumption is that the classloader that loaded the MailHandler
should be able to find the activation code.
If you can't upgrade to a newer version of JavaMail then you have to apply one of the following workarounds:
- Flush or close the MailHandler before the cleaner runs.
- Flush all handlers before the cleaner runs (I.E. web app undeploy). You have to synchronize on the LogManager and collect all handlers from each logger. Flush all of the handlers outside of the synchronized block.
- Extend the MailHandler and override close to set and restore the context class loader if the context class loader is null.
- Install a new LogManager and override
reset
to set and restore the context class loader if the context class loader is null. - Install a subject formatter to set the context class loader if the cleaner is running.
- Set the push level to
ALL
or set the capacity to 1 so an email is sent for every log record and get spammed. - Run on a Java version with patched with RFE JDK-8025251.
The main problem is that the LogManager$Cleaner
forces the context classloader to null before it calls close
on every Handler
registered with the LogManager
. A better choice for the LogManager would have been to set the context classloader to the handler class loader before calling close then after all handlers are closed set the context class loader to null. This could still be fooled by nesting handlers but at least it would have fixed the common case. This was filed as RFE JDK-8025251 "LogManager cleaner should use handler classloader during close".
Probablity is high that if you are working on the KARAF(OSGI) server the above suggestions will be difficult to implement as Karaf dont have a startup class or bootup classpath.
I found that activation.jar conflict created this issue .
{FUSEESB_HOME Or ServiceMIX_HOME}/etc/jre.properties was loading activation.jar .
Once commented everything was smooth.
Please refer the link below.
I bumped into this issue today, and it had to do with the thread's class loader.
if you do a sysout on: com.sun.mail.handlers.multipart_mixed.class.getClassLoader()
it may not be the same as the current thread's classloader: Thread.currentThread().getContextClassLoader()
I was able to arrive at this conclusion by adding the following arg: -Djavax.activation.debug=true
After adding that arg, I saw that it was unable to load the data content handler (DCH) for multipart_mixed.class.
How you resolve your classloader issues is up to you, but this should get you started.
For all having this error message above, it turned out that in my case the authentication data was empty, this was due that I had turned off the mail server authentication I didnt needed a username and password anymore, but the empty values were parsed somehow and created a missing authentication error, that wasnt caught well in Mail 1.4.0, updating to Mail 1.4.7 and deleting the two param-entries below resolved the problem.
<appender name="ALARM_MAIL" class="my.utils.SMTPAppender">
<!-- remove this line: <param name="SMTPUsername" value=""/> -->
<!-- remove this line: <param name="SMTPPassword" value=""/> -->
...
精彩评论