Skip to Main Content

Java EE (Java Enterprise Edition) General Discussion

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

SOAP Message Attachment MIME Problem

843834Mar 6 2002 — edited Mar 9 2005
Hi there,

I'm having problem trying to send a binary file with SOAP.
I can send an XML Doc (MIME type text/xml) but when I try to put the ByteArrayInputStream that holds the binary file and the content to application/octec-stream
I got this exception:

javax.activation.UnsupportedDataTypeException: no object DCH for MIME type application/octet-stream

at javax.activation.ObjectDataContentHandler.writeTo(DataHandler.java:851)

at javax.activation.DataHandler.writeTo(DataHandler.java:305)

at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1089)

at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:635)

at javax.mail.internet.MimeMultipart.writeTo(MimeMultipart.java:233)

at com.sun.xml.messaging.soap.MessageImpl.saveChanges(MessageImpl.java:371)

Anyone had this problem ?
Any work around ?
Or this is a problem because the JAXM is not fineshed yet ?

Thanks,

Marcio

Comments

843834
I am having the same problem. Does anyone have an answer?
843834
having a similar problem anybody with any solutions?
843834
http://forum.java.sun.com/thread.jsp?forum=31&thread=258706&start=0&range=100#976477
843834
OK - here it is. I don't know why this is so hard. Seems like the activation framework was meant for email programs, but it's been overloaded for use with SOAP.

I finally got this working by following these steps:

1. Use a DataHandler/DataSource to push the binary data into the message on the client side.

2. On the server side, you need to create a DataContentHandler implementation and register it with the activation framework.

Step 1 - Adding the binary attachment

Implement a simple DataSource for getting the data:
import javax.activation.*;

    class BinaryDataSource implements DataSource {
        InputStream _is;
        
        public BinaryDataSource(InputStream is) {
            _is = is;
        }
        public String getContentType() { return "application/binary"; }
        public InputStream getInputStream() throws IOException { return _is; }
        public String getName() { return "some file"; }
        public OutputStream getOutputStream() throws IOException {
            throw new IOException("Cannot write to this file");
        }
    }
Now use this code to add the attachment:
        InputStream data = ...
        SOAPMessage msg = ...
        DataHandler dh = new DataHandler(new BinaryDataSource(data));
        AttachmentPart attachment = msg.createAttachmentPart(dh);
        msg.addAttachmentPart(attachment);
Step 2 - Setup the server side

[Note: this worked for me]

Create a DataContentHandler which handles the incoming attachment of type "application/binary".
import javax.activation.*;
import java.io.*;

public class BinaryDataHandler implements DataContentHandler {
    
    /** Creates a new instance of BinaryDataHandler */
    public BinaryDataHandler() {
    }

    /** This is the key, it just returns the data uninterpreted. */
    public Object getContent(javax.activation.DataSource dataSource) throws java.io.IOException {
        System.out.println("BinaryDataHandler: getContent called with: " + dataSource);
        return dataSource.getInputStream();
    }
    
    public Object getTransferData(java.awt.datatransfer.DataFlavor dataFlavor, 
                         javax.activation.DataSource dataSource) 
                          throws java.awt.datatransfer.UnsupportedFlavorException, 
   java.io.IOException {
        return null;
    }
    
    public java.awt.datatransfer.DataFlavor[] getTransferDataFlavors() {
        return new java.awt.datatransfer.DataFlavor[0];
    }
    
    public void writeTo(Object obj, String str, java.io.OutputStream outputStream) 
     throws java.io.IOException {
        // You would need to implement this to have
        // the conversion done automatically based on
        // mime type on the client side.
    }
}    
Now, you can use this code to get the data of the attachment:
     SOAPMessage msg = ... //received message
     Iterator ats = msg.getAttachments();
     if( ats.hasNext() ){
          AttachmentPart attachment = (AttachmentPart)ats.next();
          InputStream contents = (InputStream)attachment.getContent();
     }
Finally, you need to register your DataContentHandler so that the activation framework will use it. There are a couple of ways (see MailcapCommandMap in the activation framework javadocs). What I did was to create a file called "mailcap" in the lib directory used by my "java" interpreter.

This file looks like this:
application/binary; BinaryDataHandler
application/binary;;	x-java-content-handler=BinaryDataHandler
This tells the activation framework to use your handler for the indicated
MIME type.

Simple.
843834
I use the class below to get around the javax.activation.UnsupportedDataTypeException problem. It subclasses DataSource. It works with any type of file.
public class InputStreamDataSource implements DataSource
{
    public InputStreamDataSource( InputStream inputStreamIn,
			  	  String contentTypeIn,
				  String nameIn ) {
	inputStream = inputStreamIn;
	contentType = contentTypeIn;
	name = nameIn;
    }


    public InputStream getInputStream()
	throws java.io.IOException {
	return inputStream;
    }


    public OutputStream getOutputStream()
	throws java.io.IOException {
        throw new IOException("Cannot write to this stream");
	return null;
    }


    public String getContentType() {
	return contentType;
    }


    public java.lang.String getName() {
	return name;
    }

    private InputStream inputStream;
    private String contentType;
    private String name;

}
843834
Thank you sdkeens. My specific problem was application/pdf, but your class would work for any Content-type. For those that come later, here is how I used sdkeens' class:
...
import javax.xml.transform.stream.StreamSource;
import javax.activation.DataHandler;
...
/*
** GSoap is the SOAPMessage object
*/
private void AddSOAPAttachment(String type, String cId, String inSource) throws GException
{
    try {
        AttachmentPart ap = GSoap.createAttachmentPart();
        FileInputStream fis = new FileInputStream(inSource);
        if(type.equals("application/pdf")) {
            DataHandler dh = new DataHandler(new InputStreamDataSource(fis, "application/pdf", cId));
            ap.setDataHandler(dh);
        } else {
            StreamSource ss = new StreamSource(fis);
            ap.setContent(ss, type);
        }
        ap.addMimeHeader("Content-Id", cId);
        GSoap.addAttachmentPart(ap);
    }
    catch (Exception e) {
        throw new GException("CreateSOAPAttachment(" + type + ", " + cId + ", " + inSource + ") failure", e);
    }
}
843833
Hey guys... I pray you are all still listening.

I have been on the most mind numbing cookie trail ever (I'm blatantly lying - I'm a Java programmer!) following these blasted exception trails to an 'Unknown Source'

After initially reading this thread I quickly knocked up ObjectDataSource as follows:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

import javax.activation.DataSource;

public class ObjectDataSource
implements DataSource
{
	public static final long serialVersionUID;
	public static final String CONTENT_TYPE;
	
	static {
		serialVersionUID = 42L;
		CONTENT_TYPE = "application/x-java-serialized-object";
	} // end static initializer
	
	private byte[] objectBytes;
	private String contentId;
	
	public ObjectDataSource(Serializable object, String contentId)
	throws IOException
	{
		if (contentId == null || contentId.equals(""))
			this.contentId = object.getClass().getName();
		else
			this.contentId = contentId;

		ByteArrayOutputStream baoStream = new ByteArrayOutputStream();
		
		ObjectOutputStream ooStream = new ObjectOutputStream(baoStream);
		ooStream.writeObject(object);
		ooStream.flush();
		ooStream.close();
		objectBytes = baoStream.toByteArray();
	} // end primary constructor
	
	public InputStream getInputStream()
	throws java.io.IOException
	{
		ByteArrayInputStream baiStream = new ByteArrayInputStream(objectBytes);
		return new ObjectInputStream(baiStream);
	} // end getInputStream
	
	public OutputStream getOutputStream()
	throws java.io.IOException
	{
		throw new IOException("Cannot write to this stream");
	} // end getOutputStream
	
	public String getContentType()
	{
		return CONTENT_TYPE;
	} // end getContentType
	
	public String getName()
	{
		return contentId;
	} // end getName
} // end ObjectDataSource class
and use it as follows:
AttachmentPart attachment = soapResponse.createAttachmentPart();

ObjectDataSource ods = new ObjectDataSource(requestedObject, null);
attachment.setDataHandler(new DataHandler(ods));
attachment.setContentId(ods.getName());
soapResponse.addAttachmentPart(attachment);
soapResponse.saveChanges();
... this is within my 'SOAPServlet extends JAXMServlet' servlet, which happens to work fine when testing the servlet outside a web-container. But testing with Tomcat 5.5.x It doesn't work... infact, it damn well dissappears. I try readObject after retrieving and casting the InputStream to an ObjectInputStream from the AttachmentPart, but I get a:
Exception in thread "main" java.lang.ClassCastException: java.io.ByteArrayInputStream
which is totally bizzare!
So I do a bit of investigation to discover ....
application/x-java-serialized-object
java.lang.String
javax.mail.internet.MimePartDataSource
java.io.ByteArrayInputStream
0 // this would be the available bytes in the ByteArrayInputStream
Now the first two lines confirm my ObjectDataSource is there somewhere or that that data was sent over the wire (ha, i'm on the same damn machine), but where the hell did MimePartDataSource come from?

I have previously encountered the various exceptions including java.io.EOFException when trying to read from the byte stream and java.io.StreamCorruptedException when wrapping that byte stream with an object stream.

These setbacks have royally ****ed my project; cahnces are slim that it will get finished. I'm ready to give up.

Any help will be appreciated?

Warm regards,

Darren B
1 - 7
Locked Post
New comments cannot be posted to this locked post.

Post Details

Locked on Apr 6 2005
Added on Mar 6 2002
7 comments
5,562 views