DCH class error with JavaMail

2020-02-05 13:44发布

问题:

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-fallback-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

回答1:

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.



回答2:

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.



回答3:

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/



回答4:

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.



回答5:

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:

  1. Flush or close the MailHandler before the cleaner runs.
  2. 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.
  3. Extend the MailHandler and override close to set and restore the context class loader if the context class loader is null.
  4. Install a new LogManager and override reset to set and restore the context class loader if the context class loader is null.
  5. Install a subject formatter to set the context class loader if the cleaner is running.
  6. Set the push level to ALL or set the capacity to 1 so an email is sent for every log record and get spammed.
  7. 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".



回答6:

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.



回答7:

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=""/> -->
  ...