Friday, 25 October 2013

Access unread emails and save attachments from MS Exchange WebServices using Java Client


I had business scenario recently to read new emails from Microsoft Exchange, extract the excel attachments and load them to Oracle Data Integrator. Loading the excel to oracle integrator, I will show as part of my next blog. In this blog, I just wanted to post my experience in accessing EWS using java.

If Exchange Admin has enabled web services, it is available for web access through the url <<https://[exchange web mail url]/EWS/Exchange.asmx>>. This same url when access through browser will redirect to <<https://[exchange web mail url]/EWS/Services.wsdl>> url and will show WSDL for accessing the web service. Save the wsdl file to the local folder. This wsdl refer to couple of XSDs internally  which also need to be downloaded to the same folder where WSDL is saved. The url for the XSDs will be like
<<https://[exchange web mail url]/EWS/messages.xsd>>
<<https://[exchange web mail url]/EWS/types.xsd>>

The WSDL provided by EWS is not a concrete WSDL and will be missing the service information. We need to add the service information manually to the file. Add below service information to WSDL between definitions and binding. Make sure that you provide the web mail url in the location attribute.


When we created the jave client from here will cause xml:lang exception due namespace declaration issue. Replace the <xs:import namespace="http://www.w3.org/XML/1998/namespace"> the line in types.xsd with the line <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/> to avoid this issue. Lets go ahead and create the client.

Create the Java client for the web service using the WSDL saved in the local folder. I am using the Jdeveloper in this example to create the java client. Create a Java project and create a webservice client and proxy from the BusinessTier->Web Services category.


If you need to generate asynchronous method, you can choose to do so in the wizard otherwise select not to generate asynchronous method. I have have selected not to generate asynchronous methods in the wizard. we dont need to select OWSM policies here as we will handle the security part in the java client itself. Complete the wizard. It will take good amount of time to generate Java objects using JAX-WS and JAXB from the WSDL and XSD. The generated java stubs look like the below.



Next, we just need to write java class to build the service object, construct request object, invoke the service operation by passing constructed request object. The service returns response object which needs to be accessed for the result.

There are three web service operations mentioned below which I used for my business case to find unread emails, get specific email details and save attachments. The web service operations with their request and response information is available in the Microsoft site http://msdn.microsoft.com/enus/library/exchange/bb409286(v=exchg.150).aspx.

WS Operations:
1. findItem (retrive all unread emails with ItemId)
2. getItem (retrieve details for specific email by passing ItemId. Fetches all attachments with AttachmentId)
3. getAttachment (Get the attachment with content by passing AttachmentId)

Creating Service with authentication
Following code does creates the service object with authentication details. Please change the user.domain, password details here.



package testmailprj;



import com.microsoft.schemas.exchange.services._2006.messages.ExchangeServicePortType;

import com.microsoft.schemas.exchange.services._2006.messages.ExchangeServices;

import java.net.MalformedURLException;

import java.net.URL;

import java.util.logging.Level;

import java.util.logging.Logger;

import java.net.*;

import javax.xml.ws.BindingProvider;





public class EWSTest {



    public static void main(String[] args) {



        try {

            Authenticator.setDefault(new RetrieveWSDLAuthenticator("domain\\username", "password"));

            URL wsdlURL = new URL("https://<exchange web url>/ews/Services.wsdl");

            ExchangeServicePortType port = getExchangeServicePort("email id", "password",

                                                             "domain", wsdlURL);

            ItemAttachments itemAttachments = new ItemAttachments();

            itemAttachments.getAttachments(port);

        } catch (MalformedURLException ex) {

            Logger.getLogger(EWSTest.class.getName()).log(Level.SEVERE, null, ex);

        }

    }



    static class RetrieveWSDLAuthenticator extends Authenticator {

        private String username, password;



        public RetrieveWSDLAuthenticator(String user, String pass) {

            username = user;

            password = pass;

        }



        @Override

        protected PasswordAuthentication getPasswordAuthentication() {

            return new PasswordAuthentication(username, password.toCharArray());

        }

    }

    public static ExchangeServicePortType getExchangeServicePort(String username, String password, String domain, URL wsdlURL) throws MalformedURLException {

        String uid = domain + "\\" + username;

  

        ExchangeServices exchangeWebService = new ExchangeServices();

        ExchangeServicePortType port = exchangeWebService.getExchangeServicePort();

        ((BindingProvider)port).getRequestContext().put(BindingProvider.USERNAME_PROPERTY, uid);

        ((BindingProvider)port).getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);

        

        return port;

    }



}


The ItemAttachments class has three methods for invoking the findItem, getItem, getAttachment operations. The methods are explained below.

FindItems
EWS represents calender, email, task as items in the web service. Here we are trying to retrieve unread emails by constructing the Request object with restriction (for finding unread messages). Once the response is received, it is looped for each messages which is returned in the response. findItems operation returns only limited information for the message and if we need more information on the message, it is available through the getItem operation. Especially, findItem doesnt return attachment information but getItem return attachment information without the actual attachment content.

getItem
getItem operation uses the request object with specific ItemId to retrieve the message information. The response return all To,CC, Body, Attachment (without content) information.

getAttachment
getAttachment operation uses the request object with specific attachmentId to retrieve attachment information with context and saves the attachment to disk.

All three operation method looks like below.

package testmailprj;
import javax.xml.bind.JAXBElement;
import javax.xml.ws.Holder;
 
import com.microsoft.schemas.exchange.services._2006.messages.*;
import com.microsoft.schemas.exchange.services._2006.types.*;

import java.io.FileOutputStream;

import java.util.*;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class ItemAttachments {

    private ExchangeSettings exchangeEnvironmentSettings = new ExchangeSettings();

    public List getAttachments(ExchangeServicePortType port) {

        final DistinguishedFolderIdType distinguishedFolderIdType = new DistinguishedFolderIdType();
        distinguishedFolderIdType.setId(DistinguishedFolderIdNameType.INBOX);

        NonEmptyArrayOfBaseFolderIdsType nonEmptyArrayOfBaseFolderIdsType = new NonEmptyArrayOfBaseFolderIdsType();
        nonEmptyArrayOfBaseFolderIdsType.getFolderIdOrDistinguishedFolderId().add(distinguishedFolderIdType);
 

        final ItemResponseShapeType itemResponseShapeType = new ItemResponseShapeType();
        itemResponseShapeType.setBaseShape(DefaultShapeNamesType.ALL_PROPERTIES);
 

        FindItemType request = new FindItemType();
        request.setTraversal(ItemQueryTraversalType.SHALLOW); 
        request.setItemShape(itemResponseShapeType);
        request.setParentFolderIds(nonEmptyArrayOfBaseFolderIdsType);
        
        RestrictionType restriction = new RestrictionType();
        IsEqualToType isEqualTo = new IsEqualToType();
        PathToUnindexedFieldType pathToFieldType = new PathToUnindexedFieldType();
        pathToFieldType.setFieldURI(UnindexedFieldURIType.MESSAGE_IS_READ);
        FieldURIOrConstantType constantType = new FieldURIOrConstantType();
        ConstantValueType constantValueType = new ConstantValueType();
        constantValueType.setValue("0");
        constantType.setConstant(constantValueType);
        isEqualTo.setFieldURIOrConstant(constantType);
        com.microsoft.schemas.exchange.services._2006.types.ObjectFactory objectFactory = new com.microsoft.schemas.exchange.services._2006.types.ObjectFactory();
        JAXBElement fieldUriExpression = objectFactory.createFieldURI(pathToFieldType);
        isEqualTo.setPath(fieldUriExpression);
        JAXBElement isEqualToExpression = objectFactory.createIsEqualTo(isEqualTo);
        restriction.setSearchExpression(isEqualToExpression);
        request.setRestriction(restriction);
 
        FindItemResponseType findItemResponse = new FindItemResponseType();
        Holder findItemResult = new Holder(findItemResponse);
        //CommonUtils.displayXML(request);
        port.findItem(request, exchangeEnvironmentSettings.getMailboxCulture(), exchangeEnvironmentSettings.getRequestServerVersion(), findItemResult, exchangeEnvironmentSettings.getServerVersionInfoHolder()); 
        List items = new ArrayList();
 
        FindItemResponseType response = (FindItemResponseType) findItemResult.value;
        //CommonUtils.displayXML(response);
        ArrayOfResponseMessagesType arrayOfResponseMessagesType = response.getResponseMessages();
        List responseMessageTypeList = arrayOfResponseMessagesType.getCreateItemResponseMessageOrDeleteItemResponseMessageOrGetItemResponseMessage(); // Note: Best method name... ever!
 
        Iterator responseMessagesIterator = responseMessageTypeList.iterator();
 

        while (responseMessagesIterator.hasNext()) {
            JAXBElement jaxBElement = (JAXBElement) responseMessagesIterator.next();
            FindItemResponseMessageType findItemResponseMessageType = (FindItemResponseMessageType) jaxBElement.getValue();
            FindItemParentType findItemParentType = findItemResponseMessageType.getRootFolder();
 
            if (findItemParentType != null) {

                ArrayOfRealItemsType arrayOfRealItemsType = findItemParentType.getItems();
                List itemList = arrayOfRealItemsType.getItemOrMessageOrCalendarItem();
                 
                Iterator itemListIter = itemList.iterator();
                while (itemListIter.hasNext()) {
                    ItemType itemType = (ItemType) itemListIter.next();
                    MessageType messageType = (MessageType) itemType;

                    System.out.println(itemType.getSubject() + " has attachments ? " + itemType.isHasAttachments());
                    if(messageType.isHasAttachments()){
                        System.out.println("-----------Message has attachments. Getting more details here-----------");
                        getItem(port,messageType.getItemId());
                        System.out.println("-----------Message has attachments. Getting more details here-----------");
                    }
                    items.add(itemType);
                }
            }

        }
 
        return items;
    }
    public List getItem(ExchangeServicePortType port,ItemIdType id) {
 
        NonEmptyArrayOfBaseItemIdsType neabaseIds = new NonEmptyArrayOfBaseItemIdsType();
        neabaseIds.getItemIdOrOccurrenceItemIdOrRecurringMasterItemId().add(id);
        ItemResponseShapeType itemResponseShape = new ItemResponseShapeType();
        itemResponseShape.setBaseShape(DefaultShapeNamesType.ALL_PROPERTIES);
        itemResponseShape.setBodyType(BodyTypeResponseType.BEST);
        GetItemType request = new GetItemType();
        request.setItemIds(neabaseIds);
        request.setItemShape(itemResponseShape);

        GetItemResponseType getItemResponse = new GetItemResponseType();
        Holder getItemResult = new Holder(getItemResponse);
        //CommonUtils.displayXML(request);

        port.getItem(request, exchangeEnvironmentSettings.getMailboxCulture(), exchangeEnvironmentSettings.getRequestServerVersion(), getItemResult, exchangeEnvironmentSettings.getServerVersionInfoHolder()); 

        List items = new ArrayList();
    
        GetItemResponseType response = (GetItemResponseType) getItemResult.value;
        //CommonUtils.displayXML(response);
        ArrayOfResponseMessagesType arrayOfResponseMessagesType = response.getResponseMessages();
        List responseMessageTypeList = arrayOfResponseMessagesType.getCreateItemResponseMessageOrDeleteItemResponseMessageOrGetItemResponseMessage(); // Note: Best method name... ever!
    
        Iterator responseMessagesIterator = responseMessageTypeList.iterator();
    

        while (responseMessagesIterator.hasNext()) {
            JAXBElement jaxBElement = (JAXBElement) responseMessagesIterator.next();
            ItemInfoResponseMessageType getItemResponseMessageType = (ItemInfoResponseMessageType) jaxBElement.getValue();
            ArrayOfRealItemsType arrayOfRealItemsType = getItemResponseMessageType.getItems();

                List itemList = arrayOfRealItemsType.getItemOrMessageOrCalendarItem();
                 
                Iterator itemListIter = itemList.iterator();
                while (itemListIter.hasNext()) {
                    ItemType itemType = (ItemType) itemListIter.next();
                    MessageType messageType = (MessageType) itemType;

                    System.out.println(itemType.getSubject() + " has attachments ? " + itemType.isHasAttachments());
                    if(messageType.isHasAttachments()){
                        System.out.println("Message has attachments");
                        List attachments = messageType.getAttachments().getItemAttachmentOrFileAttachment();
                        for (AttachmentType attachment: attachments){
                            System.out.println(" attachment details id: " + attachment.getAttachmentId().getId());
                            System.out.println(" attachment details content location: " + attachment.getContentType());
                            System.out.println(" attachment details name and size: " + attachment.getName() + " " + attachment.getSize());
                            getAttachment(port,attachment.getAttachmentId());
                        }
                    }
                    items.add(itemType);
                }
        }
    
        return items;
    }
    public List getAttachment(ExchangeServicePortType port,AttachmentIdType id) {

        NonEmptyArrayOfRequestAttachmentIdsType neabaseIds = new NonEmptyArrayOfRequestAttachmentIdsType();
        neabaseIds.getAttachmentId().add(id);
        AttachmentResponseShapeType attachmentResponseShape = new AttachmentResponseShapeType();
        attachmentResponseShape.setBodyType(BodyTypeResponseType.BEST);
        attachmentResponseShape.setIncludeMimeContent(Boolean.TRUE);
        GetAttachmentType request = new GetAttachmentType();
        request.setAttachmentIds(neabaseIds); // SHALLOW means it doesn't look for "soft deleted" items.
        request.setAttachmentShape(attachmentResponseShape);

        GetAttachmentResponseType getAttachmentResponse = new GetAttachmentResponseType();
        Holder getAttachmentResult = new Holder(getAttachmentResponse);
        CommonUtils.displayXML(request);
        port.getAttachment(request, exchangeEnvironmentSettings.getMailboxCulture(), exchangeEnvironmentSettings.getRequestServerVersion(), getAttachmentResult, exchangeEnvironmentSettings.getServerVersionInfoHolder());

        List items = new ArrayList();
    
        GetAttachmentResponseType response = (GetAttachmentResponseType) getAttachmentResult.value;
        CommonUtils.displayXML(response);
        ArrayOfResponseMessagesType arrayOfResponseMessagesType = response.getResponseMessages();
        List responseMessageTypeList = arrayOfResponseMessagesType.getCreateItemResponseMessageOrDeleteItemResponseMessageOrGetItemResponseMessage(); // Note: Best method name... ever!
    
        Iterator responseMessagesIterator = responseMessageTypeList.iterator();
    

        while (responseMessagesIterator.hasNext()) {
            JAXBElement jaxBElement = (JAXBElement) responseMessagesIterator.next();
            AttachmentInfoResponseMessageType getAttachmentResponseMessageType = (AttachmentInfoResponseMessageType) jaxBElement.getValue();
            ArrayOfAttachmentsType arrayOfAttachmentsType = getAttachmentResponseMessageType.getAttachments();

                List itemList = arrayOfAttachmentsType.getItemAttachmentOrFileAttachment();
                 
                Iterator attachmentListIter = itemList.iterator();
                while (attachmentListIter.hasNext()) {
                    FileAttachmentType attachmentType = (FileAttachmentType) attachmentListIter.next();

                    System.out.println( attachmentType.getName()+ " attachment is " + attachmentType.getContentType());
                    byte[] content = attachmentType.getContent();
                    try {
                        FileOutputStream fos = new FileOutputStream("c:\\temp\\" + attachmentType.getName());
                        fos.write(content);
                        fos.close();
                    } catch (Exception ex){
                        ex.printStackTrace();    
                    }
                    items.add(attachmentType);
                }
        }
    
        return items;
    }
}

The Jdeveloper project is attached for download here. EWSProject

No comments:

Post a Comment