There are a number of ways to exchange binary data other than through SOAP with Attachments (Sw/A). We briefly mention a couple of complex methods, but then go into details about the simplest method.
If your Web service implementation supports DIME, then you could write a wrapper WSDL whose binding deals with the DIME protocol instead of the Sw/A protocol. There are two big concerns with this approach, however:
- The DIME note in W3C expired at the end of 2002, so there is no official specification for what is called DIME. Without an official specification, it is unlikely anyone other than Microsoft will continue to support this protocol.
- There is no official WSDL binding for DIME. Without an official WSDL binding, it is unlikely a given binding will be supported by all vendors and, therefore, DIME bindings would not interoperate.
SOAP Message Transmission Optimization Mechanism (MTOM) is another binary data attachment protocol that stands a good chance of being supported by all Web services vendors. However, as of this writing, the MTOM specification is not yet completed (see Resources), so it will likely be a year or two before interoperable MTOM implementations begin to show up in the marketplace.
The simplest way to exchange binary data is as a byte array -- in XML, as a xsd:hexBinary or a xsd:base64Binary type. In short, you are turning attachments into non-attachments. Instead of the attachment's raw data appearing in the message in an attachment part separate from the SOAP message part, the raw data appears within the SOAP message itself. The drawback to this solution is that, since the raw data is within the SOAP message, it must be scanned by any XML parser, even if it is never used (for example, in an intermediary, like a router). So it is not a particularly efficient option. It is merely the simplest option.
Assume you have a legacy Web service with the interface in Listing 1.
Listing 1. Legacy API with attachments.
package com.ibm.developerWorks.attachment;
public interface ImageBag extends java.rmi.Remote {
public void sendImage(java.awt.Image i);
public java.awt.Image getImage();
} |
This interface contains java.awt.Images. Assume they're jpeg images. According to JAX-RPC, these images map to attachments of type image/jpeg. But you don't want to use attachments. So what you do next is create a wrapper application which delegates calls to the original application. This wrapper's API (see Listing 2) contains byte arrays instead of images. This wrapper's WSDL contains xsd:hexBinary data types. (You can find the WSDL files for both the original service and for the wrapper service by peeling apart the EAR file which you can get by clicking the code icon at the top or bottom of this tip and downloading the zip file.)
Listing 2. Wrapper API
package com.ibm.developerWorks.attachment;
public interface ImageBagWithNoAttachments extends java.rmi.Remote {
public void sendImage(byte[] i);
public byte[] getImage();
} |
So far, so good. But now you come to the hard part: the actual implementation of the wrapper. The wrapper implementation must convert between byte[] and java.awt.Image before delegating the method calls to the real service. See Listing 3 for a complete implementation of the wrapper API. This is one possible J2SE 1.4 solution. It is rather complex, and while it is not the purpose of this tip to explain in detail how to convert between byte[] and java.awt.Image, we present it here for your information.
Listing 3. Implementation of the wrapper API
package com.ibm.developerWorks.attachment;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
public class ImageBagWithNoAttachmentsSoapBindingImpl
extends Component implements ImageBagWithNoAttachments{
private ImageBag imageBag = new ImageBagSoapBindingImpl();
public void sendImage(byte[] i) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(i);
ImageIO.setUseCache(false);
imageBag.sendImage(ImageIO.read(bais));
}
catch (Exception e) {
e.printStackTrace();
}
}
public byte[] getImage() {
try {
Image image = imageBag.getImage();
Iterator iter = ImageIO.getImageWritersByMIMEType("image/jpeg");
ImageWriter writer = iter.hasNext() ? (ImageWriter) iter.next() : null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
writer.setOutput(ios);
BufferedImage rendImage = null;
if (image instanceof BufferedImage) {
rendImage = (BufferedImage) image;
} else {
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(image, 0);
tracker.waitForAll();
rendImage = new BufferedImage(image.getWidth(null),
image.getHeight(null), 1);
Graphics g = rendImage.createGraphics();
g.drawImage(image, 0, 0, null);
}
writer.write(new IIOImage(rendImage, null, null));
writer.dispose();
return baos.toByteArray();
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
} |
The client-side situation is similar to the server-side situation. You want to convert between byte[] and java.awt.Image. So the client-side code is similar to the server-side code. (See the zip file that comes with this tip (click on the code icon at the top or bottom of this tip) for some actual client-side code.)
The other JAX-RPC-supported attachment types all map to Java types which you can convert to and from a byte[] (usually through a stream). So while the implementations for each type will be different than this example, the basic idea is the same.
Since one primary reason for converting a binary attachment to inlined binary data is that .NET doesn't support Sw/A, we chose to show you a C# client for this service as well. In this case, we build a client based on the .NET Framework SDK version 1.1.
As noted in the service implementation above, we converted a java.awt.Image object into a byte[]. In this case, we go back and forth from byte[] to System.Drawing.Image. Just as in the Java case, there are many ways to perform this conversion; this is just one example.
The C# client code in Listing 4 loads an image from the file system and sends it to the server through the available sendImage(byte[] i) operation. Once sent, the client retreives the same image using the getImage() operation and saves it to the file system with a different name.
Listing 4. Implementation of the C# client
using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Web.Services;
namespace ImageClient
{
class ImageClient
{
[STAThread]
static void Main(string[] args)
{
if (args.Length < 4) {
Console.WriteLine("Insufficient argument list: <host> " +
"<port> <sendImageName> <newImageName>");
}
else {
string host = args[0];
string port = args[1];
string sendImageName = args[2];
string newImageName = args[3];
ImageBagWithNoAttachmentsService service =
new ImageBagWithNoAttachmentsService();
service.Url = "http://" + host + ":" + port +
"/AttachmentWar/services/ImageBagWithNoAttachments";
// Create a cookie container so that the session will be saved
// and we can retreive the image.
service.CookieContainer = new System.Net.CookieContainer();
ImageClient client = new ImageClient();
// Turn the image into a byte[] and send it.
Console.WriteLine("Sending data to the server.");
service.sendImage(client.createByteArray(sendImageName));
Console.WriteLine("");
// Get the byte[] from the service and turn it into an image.
Console.WriteLine("Retreiving image data from the server.");
client.saveAsImage(service.getImage(), newImageName);
}
}
public byte[] createByteArray(string imageName)
{
FileInfo fileInfo = new FileInfo(imageName);
FileStream fileStream = fileInfo.OpenRead();
byte[] byteArray = new byte[fileInfo.Length];
int bytesRead = fileStream.Read(byteArray, 0, byteArray.Length);
Console.WriteLine("{0} bytes have been read from {1}",
bytesRead.ToString(), imageName);
return byteArray;
}
public void saveAsImage(byte[] bytes, string imageName)
{
MemoryStream memStream = new MemoryStream(bytes);
System.Drawing.Image image =
System.Drawing.Image.FromStream(memStream);
image.Save(imageName);
Console.WriteLine("{0} was created successfully.", imageName);
}
}
}
|
NOTE: The ImageBagWithNoAttachmentsService object in Listing 4 represents the .NET client proxy. This object was created using the wsdl.exe tool that comes with the .NET Framework SDK 1.1. The Resources section below contains more information about how to generate this proxy.
There are a number of ways that you can send binary data other than through Sw/A. In this tip we detailed the simplest: inlining the binary data using xsd:hexBinary. We wrapped a service using java.awt.Image with a service using xsd:hexBinary.
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code for the example in this tip | ws-tip-noattachcode.zip | 28 KB | HTTP |
Information about download methods
- Download the client code and the EAR file associated with this article by clicking on the code icon at the top or bottom of this tip. The EAR file contains both the attachment service, and the
byte[]wrapper service. - Learn more about Passing files to a Web service in this tip (developerWorks, February 2004).
- Explore using SOAP attachments with JAX-RPC in this tip (developerWorks, February 2004).
- Read W3C's SOAP with Attachments specification.
- Check out the W3C's latest draft of the MTOM specification.
- Go to Java API for XML-Based RPC (JAX-RPC) Downloads & Specifications, which provides links to the JAX-RPC 1.1 specification.
- Download the .NET Framework SDK 1.1. This requires installation of the .NET Framework Redistributable package as a prerequisite.
- Learn how to create a .NET Web services client proxy with this article from MSDN.
- Access Web services knowledge, tools, and skills with Speed-start Web services, which offers the latest Java-based software development tools and middleware from IBM (trial editions), plus online tutorials and articles, and an online technical forum.
- Browse for books on these and other technical topics.
- Want more? The developerWorks SOA and Web services zone hosts hundreds of informative articles and introductory, intermediate, and advanced tutorials on how to develop Web services applications.
Nicholas Gallardo works as a software engineer on the IBM WebSphere Web Services engine, where he is focused on serviceability and interoperability. Nicholas came to IBM in 2001 after contributing development efforts to two different technology start-ups in Austin, Texas.
Russell Butek is an IBM Web services consultant and is one of the developers of the IBM WebSphere Web services engine. He is also the IBM representative on the JAX-RPC Java Specification Request (JSR) expert group. He was involved in the implementation of Apache's AXIS SOAP engine, driving AXIS 1.0 to comply with JAX-RPC 1.0. Previously, he was a developer of the IBM CORBA ORB and an IBM representative on a number of OMG task forces: the portable interceptor task force (of which he was chair), the core task force, and the interoperability task force.
Comments (Undergoing maintenance)





