JavaMail Problem with JRE 6.0 using JNI
Hi,
I am currently experiencing problems with JavaMail under JRE 6.0. It is a little odd in that it works fine when run as standalone app, but when called through JNI it throws the following exception:
java.lang.ClassCastException: javax.mail.util.SharedByteArrayInputStream cannot be cast to javax.mail.Multipart
when executing the following line of code:
Multipart parts = (Multipart) mimeMessage.getContent();
I've checked the mailcap file contains the content-handler and that the mail.jar is on the classpath.
This same code works fine under versions 1.4.2 and 5.0 of the JRE.
Any help appreciated!
Regards,
Matt
[679 byte] By [
Devoa] at [2007-11-27 4:55:56]

# 1
Assuming you've got an unmodified mail.jar on your classpath, and no other
instances of the JavaMail classes, then most likely the problem is with the
class loader you're using. JavaMail uses JAF to figure out which type of
object to return for a given MIME type. JAF uses the mailcap configuration
file, accessed through the ClassLoader.getResource method, to do this
mapping.
Run your application with the System property "javax.activation.debug"
set to "true" and the debug output might help you figure out what's going
wrong. If not, post it here.
# 2
Thanks, I tried that with the following results... I can't fingure out why it can't find the content handler class - it's in the mail.jar which is on the classpath in the package specified. This is also only a problem when invoked from JNI, so I don't know if that has anything to do with how the classes are loaded?
MailcapCommandMap: createDataContentHandler for multipart/mixed
search DB #1
search DB #2
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
search DB #3
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
search fallback DB #2
search fallback DB #3
Devoa at 2007-7-12 10:11:00 >

# 3
Interesting. Maybe it does have something to do with JNI, but I'm not enough
of a JNI expert to even guess what it could be.
Try loading the class in your application code before you use JavaMail:
Object o = new com.sun.mail.handlers.multipart_mixed();
If that fails, you have a simple case you can debug further.
Double check your classpath setting. Are you sure there aren't any other
copies of the JavaMail classes?
# 4
I've added that line of code and it instantiates the object correctly, but the original error still persists. I've printed out the file location to check where it was loading it from, and it was the same mail.jar that the mailcap file is in.
Object o = new com.sun.mail.handlers.multipart_mixed();
URL location = o.getClass().getProtectionDomain().getCodeSource().getLocation();
System.out.println(o.getClass().getName() + ".getProtectionDomain().getCodeSource().getLocation():" + location.toString());
I've checked the classpath again and couldn't find any duplicates. The classpath is pretty simple (I printed it out using System.getProperty("java.class.path") to check) and aside from two custom jars containing packages specific to the app, the only external ones are: jaxb-libs.jar, jaxb-ri.jar, Junit.jar, log4j-1.2.7.jar, mail.jar, namespace.jar
Devoa at 2007-7-12 10:11:00 >

# 5
I'm running out of ideas.Anything in your jre/lib/ext directory?Using JNI with JDK 1.5 works, but it fails with JDK 1.6?Running the same program without JNI but with JDK 1.6 works?Can you create a simple test case?
# 6
The contents of my jre/lib/ext is: dnsns.jar, localedata.jar, sunjce_provider.jar, sunmscapi.jar, sunpkcs11.jar
Yep, our app works using JNI under 1.4.2 and 1.5, but not 1.6.
I wrote a little stand alone test that contained the line of code that was causing the issue and ran it under 1.6 (from the command prompt) and it worked fine. I'll post this on the JNI forum and see if anyone knows why the class can't be found.
In the meantime i'll also try to put together a simple test case using JNI.
Thanks.
Devoa at 2007-7-12 10:11:00 >

# 7
If you have a simple test case, please post it here as well orsend it to me at javamail@sun.com. If you don't get any helpfrom the JNI forum, let me know. I really want to get to thebottom of this!
# 8
JAF 1.1
JavaMail 1.4
JRE 1.6.0_01
JDK 1.4.2_13
IMAP ser ver: Exchange 2003
The following jars in the applet class path: signed.jar, mailapi.jar, activation.jar, httpclient.jar, logging.jar, codec.jar, lang.jar, bsh.jar, imap.jar, pop3.jar, smtp.jar
We can open and read mail from the server just fine. We CAN'T open/save attachments. We get the following error:
Exception in thread "Thread-9" java.lang.ClassCastException: com.sun.mail.imap.IMAPInputStream cannot be cast to javax.mail.Multipart
Which is caused by this piece of code:
Multipart mp = (Multipart) m.getContent();
Setting the system property -Djavax.activation.debug=true allows me to see this error infromation:
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
search fallback DB #2
The mailcap file in the mailapi.jar looks like this:
#
# @(#)mailcap1.8 05/04/20
#
# Default mailcap file for the JavaMail System.
#
# JavaMail content-handlers:
#
text/plain;;x-java-content-handler=com.sun.mail.handlers.text_plain
text/html;;x-java-content-handler=com.sun.mail.handlers.text_html
text/xml;;x-java-content-handler=com.sun.mail.handlers.text_xml
multipart/*;;x-java-content-handler=com.sun.mail.handlers.multipart_mixed; x-java-fallback-entry=true
message/rfc822;;x-java-content-handler=com.sun.mail.handlers.message_rfc822
#
# can't support image types because java.awt.Toolkit doesn't work on servers
#
#image/gif;;x-java-content-handler=com.sun.mail.handlers.image_gif
#image/jpeg;;x-java-content-handler=com.sun.mail.handlers.image_jpeg
I tried putting it outside or the jar in the META-INF directory of the applet codebase, no change, still get the error.
I can put the following code in and load the class that is claims it can't find:
Object o = new com.sun.mail.handlers.multipart_mixed();
URL location = o.getClass().getProtectionDomain().getCodeSource().getLocation();
System.out.println("\n"+o.getClass().getName() + ".getProtectionDomain().getCodeSource().getLocation():" + location.toString());
That prints:
com.sun.mail.handlers.multipart_mixed.getProtectionDomain().getCodeSource().getLocation():http://localhost:2696/MM_Web/jars/mailapi.jar
Which is clearly loading the class from the right place. What the heck is going on? This stuff works perfectly in 1.4 and 1.5.
I also tried removing the activation jar from the applet tag just in case the activation classes included in the 1.6 rt.jar where conflicting. No luck here either.
I also tried putting this in the code:
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);
No go still broken.
The mailcap.default file in the 1.6 resources.jar file looks like this:
#
# This is a very simple 'mailcap' file
#
image/gif;;x-java-view=com.sun.activation.viewers.ImageViewer
image/jpeg;;x-java-view=com.sun.activation.viewers.ImageViewer
text/*;;x-java-view=com.sun.activation.viewers.TextViewer
text/*;;x-java-edit=com.sun.activation.viewers.TextEditor
Also tried JavaMail 1.4.1ea from the glssfish project, still broken.
Message was edited by:
zparticle
# 9
Possibly the thread's context class loader isn't being set properly in theapplet, or possibly your security settings aren't allowing classes to beloaded from those jar files. If you put mailapi.jar in your local jre/lib/extdirectory, does it work?
# 10
Okay I tried putting the mail jar in the ext dir of the JRE and it still blows up with the same error. This code works perfectly in 1.4 and 1.5. The applet is signed and I have full access to the machine so I don't know what the problem is.
This is very frustrating, LOL. Any other ideas?
I also tried switching over to the single mail jar with all of the providers included, still no go. If this is really a problem with a class loader why does this work?
Object o = new com.sun.mail.handlers.multipart_mixed();
One other thing, it seems to work when we run the junits which hit the underlying classes without going through the applet.
According to the javadocs: "The object returned for a "multipart" content is always a Multipart subclass"
The interesting part is if I detect that the method returned an InputStream I can read the contents of the stream and it is the enitre mime message. Does that help?
Message was edited by:
zparticle
# 11
It seems clear that JAF can't load the required class. The
possibilities are:
1. The class isn't there. (We know that's not true.)
2. The class loader is broken.
3. The wrong class loader is being used.
4. The app doesn't have permission to load the class.
Since you can load the class explicitly in the app, #2 seems unlikely.
#3 depends on the thread's context class loader being set properly.
You can try using Thread.getContextClassLoader in your app, then
use that ClassLoader to load the class. If that works, that means #3
is unlikely.
#4 is sometimes the hardest to figure out. Remember it's the entire
call chain from the app, through all the intermediate jar files, and into
the system libraries (JAF in this case) that needs to have permission
to load these classes. You can try using the Java security debugging
properties, along with the javax.activation.debug property, to see if
security failures are preventing the class loading.
If you have a simple applet that reproduces this problem, let me know.
(Send it to javamail@sun.com.)
# 12
Thanks very much for your help.
I tried the context class loader using:
Thread t = Thread.currentThread();
ClassLoader cl = t.getContextClassLoader();
cl.loadClass("com.sun.mail.handlers.multipart_mixed");
The ClassLoader comes back as null, do I need to do something here? I'm sure I don't unserstand the purpose.
I also put in the -Djavax.activation.debug=true and no extra messages were dumped to the console so I assume that means I'm doing the security thing correctly. The download attachment thread is started inside of a
AccessController.doPrivileged(new PrivilegedExceptionAction() {
block which should be giving me all the privs I need.
If you have no other ideas I can try to create something to reproduce this but there is a large amount of code involved.
==================================
Thought I'd throw this in here to be complete. This is the debug output when I originally read the message before I go to download the attachment.
MailcapCommandMap: load HOME
new MailcapFile: file C:\Documents and Settings\sshaver\.mailcap
MailcapCommandMap: load SYS
new MailcapFile: file C:\PROGRA~1\Java\JRE16~1.0_0\lib\mailcap
MailcapCommandMap: load JAR
MailcapCommandMap: getResources
MailcapCommandMap: URL jar:http://localhost:2696/MM_Web/jars/mail.jar!/META-INF/mailcap
new MailcapFile: InputStream
parse: text/plain;;x-java-content-handler=com.sun.mail.handlers.text_plain
Type: text/plain
Command: content-handler, Class: com.sun.mail.handlers.text_plain
parse: text/html;;x-java-content-handler=com.sun.mail.handlers.text_html
Type: text/html
Command: content-handler, Class: com.sun.mail.handlers.text_html
parse: text/xml;;x-java-content-handler=com.sun.mail.handlers.text_xml
Type: text/xml
Command: content-handler, Class: com.sun.mail.handlers.text_xml
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
parse: message/rfc822;;x-java-content-handler=com.sun.mail.handlers.message_rfc822
Type: message/rfc822
Command: content-handler, Class: com.sun.mail.handlers.message_rfc822
MailcapCommandMap: successfully loaded mailcap file from URL: jar:http://localhost:2696/MM_Web/jars/mail.jar!/META-INF/mailcap
MailcapCommandMap: load DEF
new MailcapFile: InputStream
parse: image/gif;;x-java-view=com.sun.activation.viewers.ImageViewer
Type: image/gif
Command: view, Class: com.sun.activation.viewers.ImageViewer
parse: image/jpeg;;x-java-view=com.sun.activation.viewers.ImageViewer
Type: image/jpeg
Command: view, Class: com.sun.activation.viewers.ImageViewer
parse: text/*;;x-java-view=com.sun.activation.viewers.TextViewer
Type: text/*
Command: view, Class: com.sun.activation.viewers.TextViewer
parse: text/*;;x-java-edit=com.sun.activation.viewers.TextEditor
Type: text/*
Command: edit, Class: com.sun.activation.viewers.TextEditor
Merging commands for type text/*
MailcapCommandMap: successfully loaded mailcap file: /META-INF/mailcap.default
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
MailcapCommandMap: createDataContentHandler for multipart/alternative
search DB #1
search DB #2
search fallback DB #1
got content-handler
class com.sun.mail.handlers.multipart_mixed
MailcapCommandMap: createDataContentHandler for text/plain
search DB #1
got content-handler
class com.sun.mail.handlers.text_plain
MailcapCommandMap: createDataContentHandler for text/html
search DB #1
got content-handler
class com.sun.mail.handlers.text_html
MailcapCommandMap: createDataContentHandler for application/msword
search DB #1
search DB #2
search fallback DB #1
search fallback DB #2
Message was edited by:
zparticle
I also added the:
-Djava.security.debug="access,failure,jar"
and got a ton of output the all looked something like this:
access: access allowed (java.security.AllPermission <all permissions> <all actions>)
access: access allowed (java.lang.RuntimePermission createClassLoader)
They always say "access allowed" there were no errors.
Message was edited by:
zparticle
# 13
> Thanks very much for your help.
>
> I tried the context class loader using:
>
> Thread t = Thread.currentThread();
> ClassLoader cl = t.getContextClassLoader();
> cl.loadClass("com.sun.mail.handlers.multipart_mixed");
>
>
> The ClassLoader comes back as null, do I need to do
> something here? I'm sure I don't unserstand the
> purpose.
Do you get null when using 1.4 or 1.5?
The applet container should set the context class loader to point to the
class loader used to load the application classes. If it's not set, the JAF
code in the JRE has no way to find the correct class loader to use to
load these classes, it can only use its own class loader. Before 1.6, JAF
wasn't in the JRE, so when if it used its own class loader it was using the
same class loader that's used to load the application classes. In 1.6 the
JAF class loader is the system class loader, which is different than the
application's class loader.
This is likely a bug in the applet support in the JRE.
# 14
Yeah I just removed 1.6 and ran against 1.5 again. The ClassLoader comes back null there as well but the code runs perfectly and downloads the attachment.
Any ideas on how I can work around this 1.6 issue. We are getting close to having our product go to "Open Beta" status and this is really going to hurt us. Can I set the class loader myself somehow?
Message was edited by:
zparticle
# 15
If your app has sufficient privilege, it can call the Thread.setContextClassLoadermethod. It should set it to the ClassLoader used to load the application classes.
# 16
Okay that seemd to work, I'll have to test everything again with the old JREs to make sure this doesn't hose something else up though. Anyway thank you very much. So this is a bug in 1.6 right?
I added the following code to the top of the run method of the download thread:
setContextClassLoader(CCApplet1.getBaseClassLoader());
and this to the main applet class:
static public final ClassLoader getBaseClassLoader(){
try{
return Class.forName("com.cc.applet.CCApplet1").getClassLoader();
}catch(Exception x){
return null;
}
}
# 17
> So this is a bug in 1.6 right?And 1.5, and 1.4, and ...Yes, it seems like a bug to me. I'm trying to contact theappropriate people in the JRE group to get their opinion.
# 18
> return Class.forName("com.cc.applet.CCApplet1").getClassLoader();Nothing at all to do with JavaMail, but you should be able to write this like so:return CCApplet1.class.getClassLoader();
# 19
thanks that is a better way to write it.
# 20
Hi Guys,
Glad(?) to hear someone else is having a similar problem to the one I'm still having.
bshannon - I haven't posted up a sample app yet as the stand alone app I wrote works ok, but I can't figure out what the difference is (aside from complexity (of both the JNI and the Java), size and the use of reflection in the non-working version). Also, if this is a bug in the JRE, I don't think it is limited to applet support as I'm not using applets but am getting the same error. It does appear to me to be classloader related, so any advice is appreciated!
Devoa at 2007-7-21 21:15:59 >

# 21
You should try the same things to see if the context class loader is setin your application. Depending on the complexity of class loaders you'reusing, this may or may not be part of your problem.
# 22
"the stand alone app I wrote works ok"
Devo - Based on that I would say your are having the same problem I was with the context class loader not being set because our juints ran just fine. This is because they use a completely different method of class loading than the applet we have does.
Good luck with your project, this was extreemly frustrating.
# 23
Thanks, it seemed to be the same issue.
Devoa at 2007-7-21 21:15:59 >

# 24
bshannon - do you know if the JRE team has acknowledged this as a bug, and if so do you have a tracking number and potential release date/version for a fix. I need to prepare a report for my management on the issue we were experiencing.Thanks again for all of you assistance!
Devoa at 2007-7-21 21:15:59 >

# 25
I haven't heard back from them yet. I'll poke them again.
# 26
I checked with the JRE team and they assure me that the applet container
has always set the context class loader. And indeed a simple test applet
shows that it's set and that JAF is able to load the DataContentHandlers
from mail.jar.
Do you have a simple test applet that shows the problem?
Are you sure nothing else in your application is setting the context class loader?
# 27
Unfortunately the small app I wrote to reproduce the problem worked fine... It must be something else in the app that is complicating things. My problem wasn't caused by using applets though - I'm using JNI but unsure if that is what is causing the ClassLoader to behave the way it is.
Devoa at 2007-7-21 21:15:59 >

# 28
Sorry, I was addressing that to zparticle, who was using an applet.
# 29
I don't have a small example to show the issue. I'm not doing anything to set class loaders other than what we did to fix the problem. Clearly it isn't getting set or it wouldn't be coming back as null.
# 30
And yet, in my trivial test program, it clearly is being set.
There's something else about your environment that's causing the
problem and we're going to need to do some debugging in your environment
to figure it out.
Here's my simple test program. Try this in your environment.
import java.awt.*;
import java.applet.*;
import java.io.*;
import javax.activation.*;
import javax.mail.util.*;
public class ccltest extends Applet {
public void init() {
String text = "NONE";
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
text = cl.toString();
} catch (SecurityException sex) {
text = sex.toString();
}
add(new TextField(text));
String s = null;
try {
DataHandler dh = new DataHandler(
new ByteArrayDataSource("test data", "text/plain"));
s = (String)dh.getContent();
} catch (IOException ex) {
s = ex.toString();
}
add(new TextField(s));
}
}
Reference it using:
<applet code=ccltest.class width="400" archive="mail.jar">
# 31
worked in my environment too.
Devoa at 2007-7-21 21:16:04 >
