FileDocCategorySizeDatePackage
BookImageServlet.javaAPI DocExample12101Tue Oct 08 10:25:44 BST 2002ora.jwsnut.chapter3.bookimageservice

BookImageServlet.java

package ora.jwsnut.chapter3.bookimageservice;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.Detail;
import javax.xml.soap.DetailEntry;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPMessage;
import ora.jwsnut.saaj.SAAJServlet;

/**
 * A servlet that uses SAAJ attachments to 
 * serve images to a client.
 */
public class BookImageServlet extends SAAJServlet {
    
    // The XML Schema namespace
    private static final String XMLSCHEMA_URI = "http://www.w3.org/2001/XMLSchema";
    
    // The XML Schema instance namespace
    private static final String XMLSCHEMA_INSTANCE_URI = 
                                        "http://www.w3.org/2001/XMLSchema-instance";

    // Namespace prefix for XML Schema
    private static final String XMLSCHEMA_PREFIX = "xsd";

    // Namespace prefix for XML Schema instance
    private static final String XMLSCHEMA_INSTANCE_PREFIX = "xsi";    
    
    // The namespace prefix used for SOAP encoding
    private static final String SOAP_ENC_PREFIX = "SOAP-ENC";
    
    // The URI used to qualify elements for this service
    private static final String SERVICE_URI = "urn:jwsnut.bookimageservice";
    
    // The namespace prefix used in elements for this service
    private static final String SERVICE_PREFIX = "tns";
    
    // MessageFactory for replies from this service
    private static MessageFactory messageFactory;
     
    // SOAPFactory for message pieces
    private static SOAPFactory soapFactory;
    
    // The name of the element used to request a book name list
    private static Name BOOK_LIST_NAME;
    
    // The name of the element used to reply to a book name list request
    private static Name BOOK_TITLES_NAME;
    
    // The name of the element used to request a book image
    private static Name BOOK_IMAGE_REQUEST_NAME;
    
    // The name of the element used to respond to a book image request
    private static Name BOOK_IMAGES_NAME;
    
    // The name of the attribute used to hold the image encoding
    private static Name IMAGE_TYPE_ATTRIBUTE;
    
    // The name of the href attribute
    private static Name HREF_ATTRIBUTE;
   
    /**
     * Handles a received SOAP message.
     */
    public SOAPMessage onMessage(SOAPMessage message) throws SOAPException {
        if (messageFactory == null) {
            // Create all static data on first call
            messageFactory = MessageFactory.newInstance();
            soapFactory = SOAPFactory.newInstance();
            BOOK_LIST_NAME = soapFactory.createName("BookList", SERVICE_PREFIX, SERVICE_URI);
            BOOK_TITLES_NAME = soapFactory.createName("BookTitles", SERVICE_PREFIX, SERVICE_URI);
            BOOK_IMAGE_REQUEST_NAME = 
                        soapFactory.createName("BookImageRequest", SERVICE_PREFIX, SERVICE_URI);
            BOOK_IMAGES_NAME = soapFactory.createName("BookImages", SERVICE_PREFIX, SERVICE_URI);
            IMAGE_TYPE_ATTRIBUTE = soapFactory.createName("imageType", SERVICE_PREFIX, SERVICE_URI);
            HREF_ATTRIBUTE = soapFactory.createName("href");
        }
        
        // Create the reply message and define the namespace
        // and encoding for the elements used in the reply.
        SOAPMessage reply = messageFactory.createMessage();
        SOAPEnvelope replyEnvelope = reply.getSOAPPart().getEnvelope();
        replyEnvelope.getHeader().detachNode();
        replyEnvelope.addNamespaceDeclaration(SERVICE_PREFIX, SERVICE_URI);
        replyEnvelope.addNamespaceDeclaration(SOAP_ENC_PREFIX, 
                                            SOAPConstants.URI_NS_SOAP_ENCODING);
        replyEnvelope.addNamespaceDeclaration(XMLSCHEMA_PREFIX, XMLSCHEMA_URI);
        replyEnvelope.addNamespaceDeclaration(XMLSCHEMA_INSTANCE_PREFIX, 
                                                        XMLSCHEMA_INSTANCE_URI);        
        replyEnvelope.setEncodingStyle(SOAPConstants.URI_NS_SOAP_ENCODING);
        SOAPBody replyBody = reply.getSOAPPart().getEnvelope().getBody();        
        
        // There are two requests - one for the list of
        // book titles, the other for the image for a book.
        SOAPBody requestBody = message.getSOAPPart().getEnvelope().getBody();
        Iterator iter = requestBody.getChildElements();
        if (iter.hasNext()) {
            // The child element contains the request
            SOAPElement element = (SOAPElement)iter.next();
            Name elementName = element.getElementName();
            if (elementName.equals(BOOK_LIST_NAME)) {
                handleBookListRequest(replyBody);
            } else if (elementName.equals(BOOK_IMAGE_REQUEST_NAME)) {
                handleBookImageRequest(element, reply);
            } else {
                // Unrecognized request - this is a fault.
                createFault(replyBody, "soap-env:Client.UnknownRequest", "Unrecognized request", 
                                            SERVICE_URI, elementName.getLocalName());
            }
        } else {
            // No request - this is a fault
            createFault(replyBody, "soap-env:Client.MissingRequest", "Missing request", 
                                            SERVICE_URI, "No request found");
        }             
        return reply;
    }
    
    /**
     * Handles a request for list of book names.
     */
    private void handleBookListRequest(SOAPBody replyBody) throws SOAPException {
        // Create a BookTitles element containing an entry
        // for each book title.
        SOAPBodyElement bodyElement = replyBody.addBodyElement(BOOK_TITLES_NAME);
        
        // Add 'xsi:type = "SOAP-ENC:Array"'
        bodyElement.addAttribute(
            soapFactory.createName("type", XMLSCHEMA_INSTANCE_PREFIX, XMLSCHEMA_INSTANCE_URI),
                                    SOAP_ENC_PREFIX + ":Array");
        
        // Add 'SOAP-ENC:arrayType = "xsd:string[]"
        bodyElement.addAttribute(
            soapFactory.createName("arrayType", SOAP_ENC_PREFIX, SOAPConstants.URI_NS_SOAP_ENCODING),
                                    XMLSCHEMA_PREFIX + ":string[]");
        
        // Add an array entry for each book 
        String[] titles = BookImageServletData.getBookTitles();
        for (int i = 0; i < titles.length; i++) {
            SOAPElement titleElement = bodyElement.addChildElement("item");
            titleElement.addTextNode(titles[i]);
        }        
    }
    
    /**
     * Handles a request for the images for a given list of book.
     */
    private void handleBookImageRequest(SOAPElement element, SOAPMessage reply) 
                                            throws SOAPException {
        // The request element contains an attribute that holds the 
        // type of image requested and a nested string for each title.
        // The reply body has a BookImages element and a nested item with
        // a reference to the image which is sent as an attachment                                                
        SOAPBody replyBody = reply.getSOAPPart().getEnvelope().getBody();
                
        // Determine whether to use JPEG or GIF images
        String imageType = element.getAttributeValue(IMAGE_TYPE_ATTRIBUTE);
        boolean gif = imageType.equalsIgnoreCase("image/gif");
        
        // Build the BookImages element containing all of the replies
        SOAPBodyElement bodyElement = replyBody.addBodyElement(BOOK_IMAGES_NAME);
        bodyElement.addAttribute(
            soapFactory.createName("type", XMLSCHEMA_INSTANCE_PREFIX, XMLSCHEMA_INSTANCE_URI),
                                    SOAP_ENC_PREFIX + ":Array");
        bodyElement.addAttribute(
            soapFactory.createName("arrayType", SOAP_ENC_PREFIX, SOAPConstants.URI_NS_SOAP_ENCODING),
                                    XMLSCHEMA_PREFIX + ":anyType[]");
        
        // Index of the next attachment to use
        int index = 0;
        
        // Handle each nested element.
        Iterator iter = element.getChildElements();
        while (iter.hasNext()) {
            // Get the next child element from the request message
            SOAPElement childElement = (SOAPElement)iter.next();
            
            // Get the book title
            String title = childElement.getValue();
            
            // Get the image data
            byte[] imageData = BookImageServletData.getBookImage(title, gif);
            if (imageData != null) {
                // Got the data - attach it.
                AttachmentPart attach = reply.createAttachmentPart();   
                attach.setDataHandler(new DataHandler(
                                        new ByteArrayDataSource("Image Data",
                                            imageData,
                                            gif ? "image/gif" : "image/jpeg")));
                attach.setContentId("ID" + index);
                reply.addAttachmentPart(attach);  

                // Add an element in the reply pointing to the attachment
                bodyElement.addChildElement("item").addAttribute(HREF_ATTRIBUTE, "cid:ID" + index); 
                
                // Increment the index
                index++;
            } else {
                // No data - this is a fault.
                // Clear the reply and install the fault
                reply.removeAllAttachments();
                bodyElement.detachNode();
                createFault(replyBody, "soap-env:Client.Title", "Unknown title",
                                        SERVICE_URI, title);
                return;
            }
        }       
    }
    
    /**
     * Creates a fault in the reply body.
     */
    private void createFault(SOAPBody replyBody, String faultCode, String faultString,
                                String faultActor, String detailString) throws SOAPException {
                                    
        SOAPFault fault = replyBody.addFault();
        fault.setFaultCode(faultCode);
        fault.setFaultString(faultString);
        fault.setFaultActor(faultActor);
        if (detailString != null) {
            Name detailName = soapFactory.createName("BookFaultDetail", SERVICE_PREFIX, SERVICE_URI);
            Detail detail = fault.addDetail();
            DetailEntry detailEntry = detail.addDetailEntry(detailName);
            detailEntry.addTextNode(detailString);
        }
    }
}

/**
 * A private DataSource implementation that
 * allows byte streams of arbitrary types to
 * be associated with a DataHandler.
 */
class ByteArrayDataSource implements DataSource {

    private String contentType;
    
    private byte[] data; 
    
    private String name;
        
    ByteArrayDataSource(String name, byte[] data, String contentType) {
        this.name = name;
        this.data = data;
        this.contentType = contentType;
    }
    
    public String getContentType() {
        return contentType;
    }
    
    public InputStream getInputStream() throws IOException {
        return new ByteArrayInputStream(data);
    }
    
    public String getName() {
        return name;
    }
    
    public OutputStream getOutputStream() throws IOException {
        throw new IOException("ByteArrayDataSource cannot support getOutputStream()");
    }
}