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.

bshannona at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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?

bshannona at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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?
bshannona at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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!
bshannona at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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

zparticlea at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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?
bshannona at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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

zparticlea at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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.)

bshannona at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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

zparticlea at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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.

bshannona at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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

zparticlea at 2007-7-12 10:11:00 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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.
bshannona at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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;

}

}

zparticlea at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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.
bshannona at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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();
DrClapa at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 19
thanks that is a better way to write it.
zparticlea at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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.
bshannona at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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.

zparticlea at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 23
Thanks, it seemed to be the same issue.
Devoa at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 25
I haven't heard back from them yet. I'll poke them again.
bshannona at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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?

bshannona at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 28
Sorry, I was addressing that to zparticle, who was using an applet.
bshannona at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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.
zparticlea at 2007-7-21 21:15:59 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 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">

bshannona at 2007-7-21 21:16:04 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 31
worked in my environment too.
Devoa at 2007-7-21 21:16:04 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...