Saturday, February 26, 2011

Exchange Web Services EWS from Java

Update 20120629: The managed API looks a little bit better after a year. I have updated the project to reflect so. Read more here.

I have tested the EWS SOAP API using a trial account of Microsoft Exchange Online. I opened a trial account for 30 days and I got availability for 20 users with 25GB per user. Of course you can setup Exchange 2007 and go through this journey yourself. It should work there too.

For the impatient here is the whole source code. Be sure you read README file.

The first thing you want to do is to get the Services.WSDL. Using a valid account you will be able to get it from one of the URLs provided to you, in my case https://red001.mail.microsoftonline.com/ews/Services.wsdl. As a side note https://red001.mail.microsoftonline.com/ews/Exchange.asmx redirects to the first URL.

The WSDL by default cannot be consumed as it lacks the wsdl:service node. Here is a corrected version of the wsdl file and the xsd files as well. You need to download it locally as well as types.xsd and messages.xsd from the same path. Once downloaded just add the below node to the wsdl file. Note that the Exchange Server URL might be different for you:
<wsdl:service name="ExchangeService">
  <wsdl:port name="ExchangeServicePort" binding="tns:ExchangeServiceBinding">
    <soap:address location="https://red001.mail.microsoftonline.com/EWS/Exchange.asmx"/>
  </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

Now open SoapUi and point to the local modified wsdl copy.

Go to GetFolder node on the left. Double click "Request 1" and click on the Aut button, then provide Username and Password for HTTP Authentication. Replace the request content by the below:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:mes="http://schemas.microsoft.com/exchange/services/2006/messages">
   <soapenv:Body>
      <mes:GetFolder>
         <mes:FolderShape>
            <typ:BaseShape>AllProperties</typ:BaseShape>
         </mes:FolderShape>
         <mes:FolderIds>
            <typ:DistinguishedFolderId Id="inbox"/>
         </mes:FolderIds>
      </mes:GetFolder>
   </soapenv:Body>
</soapenv:Envelope>

Hit the run button (green arrow) and you will get a response like the below. Look how you get the number of unread items in your inbox together with other useful information:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <soap:Header>
      <t:ServerVersionInfo MajorVersion="8" MinorVersion="3" MajorBuildNumber="83" MinorBuildNumber="4" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"/>
   </soap:Header>
   <soap:Body>
      <m:GetFolderResponse xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
         <m:ResponseMessages>
            <m:GetFolderResponseMessage ResponseClass="Success">
               <m:ResponseCode>NoError</m:ResponseCode>
               <m:Folders>
                  <t:Folder>
                     <t:FolderId Id="AAAhAG51cnF1aXphQGtyZnMubWljcm9zb2Z0b25saW5lLmNvbQAuAAAAAAD++u3ArxgdQrBBhvckACnkAQB8H0u8rlXuQomN7tXZc6ZqAAATKH0iAAA=" ChangeKey="AQAAABQAAADy++j7kEQqS5TFatdF3GSPAAAIEA=="/>
                     <t:ParentFolderId Id="AAAhAG51cnF1aXphQGtyZnMubWljcm9zb2Z0b25saW5lLmNvbQAuAAAAAAD++u3ArxgdQrBBhvckACnkAQB8H0u8rlXuQomN7tXZc6ZqAAATKH0fAAA=" ChangeKey="AQAAAA=="/>
                     <t:FolderClass>IPF.Note</t:FolderClass>
                     <t:DisplayName>Inbox</t:DisplayName>
                     <t:TotalCount>5</t:TotalCount>
                     <t:ChildFolderCount>0</t:ChildFolderCount>
                     <t:UnreadCount>1</t:UnreadCount>
                  </t:Folder>
               </m:Folders>
            </m:GetFolderResponseMessage>
         </m:ResponseMessages>
      </m:GetFolderResponse>
   </soap:Body>
</soap:Envelope>

This is really fun and in fact you can develop plain XML over HTTP applications but there must be something there for the OO fans right?

It is easier if you actually use Stubs. Of course Objects are easier to manipulate than plain XML responses. Be sure though you understand the overhead inherent to serialization/deserialization: using Objects simplifies the work at expenses of a performance penalty which for most cases is not big deal but I have been through projects in which basically I needed to create XML and send it over HTTP directly and then parse XML responses coming back over the wire as well because of unacceptable performance overhead.

Before we jump into coding you will notice at least one schema error in types.xsd file.

Replace the commented line by the next one:
<!-- <xs:import namespace="http://www.w3.org/XML/1998/namespace"/> -->
  <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>


Microsoft Managed EWS Java API

It would be really nice if you would have an EWS Java (Managed) API like .NET developers enjoy. Microsoft did some effort in the past but I tried hard to get to the team involved in that effort through Microsoft Online Services Customer Support and got not positive feedback. It looks like that API is abandoned. Anyway that was my first attempt as I explain below. If you want to jump directly into something that really works start at JAX-WS section. I have put everything together on Google code SVN. Checkout the project and look for "Overloaded method to work around the infamous xsl:nill attribute error" to see the code I changed from ExchangeServicePortType class. Take a look at README file from there for instructions how to get the project working.

Even though it did not work at the end as I got "Server redirected too many times" error when trying https://red001.mail.microsoftonline.com/ews I am documenting here the steps to follow in case you want to give this a try with any instance of Exchange 2007.
  1. Download EWS Java API http://archive.msdn.microsoft.com/ewsjavaapi
  2. Build the jar
    cd EWSJavaAPI1.1 
    ant
    
  3. Include file EWSJavaAPI1.1/EWSAPI1.1.jar in your project. Below is how I installed in my local maven repo
    mvn install:install-file -DgroupId=ews -DartifactId=ews-api -Dversion=1.1 -Dpackaging=jar -Dfile=/Users/nestor/Downloads/EWSJavaAPI1.1/EWSAPI1.1.jar
    
  4. Generate a jar scaffold project
    mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=5-SNAPSHOT -DgroupId=com.nestorurquiza.microsoft -DartifactId=ews -Dversion=1.0.0-SNAPSHOT
    
  5. As I will use Eclipse as IDE I add the eclipse plugin to the pom.xml
    <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-eclipse-plugin</artifactId>
            <version>2.8</version>
          </plugin>
        </plugins>
      </build>
    </project>
    
  6. Create the necessary eclipse files
    cd ews
    mvn eclipse:eclipse
    
  7. Open Eclipse an give it a try with this commented out code

JAXB

I could not get it working with JAXB. If you were successful please share. Below is a little bit of history.

You will need to create a new file named for example jaxb-custom-bindings.xml with the below content:
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               jaxb:version="2.0">
  <jaxb:bindings >
      <jaxb:globalBindings typesafeEnumMaxMembers="1000"/>
  </jaxb:bindings>
</jaxb:bindings>


You can use a command to generate all necessary Java classes, for example:
java -jar jaxb-ri-20110115/lib/jaxb-xjc.jar -p com.nestorurquiza.axis2.client.ews -wsdl "file:///Users/nestor/Downloads/Services.wsdl" -d /Users/nestor/Downloads/ews/jaxb/ -b jaxb-custom-bindings.xml

Somehow this was not generating the main Stub class for me.

Axis2

Again I could not get it working but if you could then share your experience please.

You can use axis2 to generate the stubs. Here is an example in commands:
./axis2.sh org.apache.axis2.wsdl.WSDL2Java -uri ~/Downloads/Services.wsdl  -o ~/Downloads/ews/axis2
cd ~/Downloads/ews/axis2
ant

This generated an ant project but I got several unresolved symbols.

JAX-WS

One could be tempted to build a jar with the stubs but you will be disappointed because of the infamous "xsl:nill attribute error". Just Google it!

Download JAX-WS http://jax-ws.java.net/2.2.1/ and run similar to the below commands (change paths to adjust to your environment) to get your sources files and include them in your standalone (jar) project.
mkdir -p /Users/nestor/Downloads/ews/jaxws/src
mkdir /Users/nestor/Downloads/ews/jaxws/classes
cd /Users/nestor/Downloads/jaxws-ri/bin
wsimport /Users/nestor/Downloads/Services.wsdl -p com.nestorurquiza.ews.jaxws -d /Users/nestor/Downloads/ews/jaxws/classes -s /Users/nestor/Downloads/ews/jaxws/src
cp -R /Users/nestor/Downloads/ews/jaxws/src/* ~/eclipse-workspace-test/ews/src/main/java/

You have now the API ready to be used. I have to tell you this is half of the path. Hopefully in the web you will find snippets that will help you consuming the services as they are not that straightforward I have to say. Any single step needs several commands. For a test of it if you did not do it already checkout sample working classes I have shared with the hope more snippets will come and perhaps even a developer willing to take this to the extra mile and build a real abstraction around this API. Feel free to GitHub my code. I would appreciate if you give me some credit. All the work I make publicly available is published with a BSD license BTW. Happy coding!

9 comments:

Pavel Moukhataev said...

Thank you man! Your article is really-really helpful. BTW it seems that Microsoft updated EWS Java API on Feb 25 2011 and it seems to work.

Nestor Urquiza said...

@ Pavel You are very welcome. I have revisited the URL and downloaded the Microsoft EWS code. I have made there some suggestions. I would love to see more support for that project but for at this time I think I will stick to JAX-WS.

Florian Kammermann said...

Made a github project: https://github.com/haschibaschi/java-microsoft-exchange-webservice-access

Nestor Urquiza said...

@Florian way to go!
-Nestor

srikanth said...

"The first thing you want to do is to get the Services.WSDL. Using a valid account you will be able to get it from one of the URLs provided to you, in my case https://red001.mail.microsoftonline.com/ews/Services.wsdl. As a side note https://red001.mail.microsoftonline.com/ews/Exchange.asmx redirects to the first URL."

Can you explin me on this do we need some kind of special rights to use the wsdl? or can we simply just get the wsdl you provided and we it...

Nestor Urquiza said...

I think you should get the WSDL easily from your service. Wether it is protected or not will depend on your sysadmin so the Exchange administrator should be able to help.

I shared the one I used when I worked in that project but I cannot tell if that will work 100% of the cases. You will need to test that yourself I guess.

Best,
-Nestor

Chandrakanth Achyuta said...

You can have a look at http://archive.msdn.microsoft.com/ewsjavaapi and http://sourceforge.net/projects/j-xchange/

John said...

Thanks. This is very helpful. I created the stubs very quickly. Now I am attempting to read a folder.

Has anyone encountered and overcome this problem: (coderanch). One suggestion on that site was to use 'domain\username' as user name, but it did not work for me. I get the same error (server redirect) with: new URL("").openConnection().getContent()

Nestor Urquiza said...

@John I would start trying to access the WSDL from SOAPUI tool. Your problem is server side and you should be able to troubleshoot it with your Ops guys. Best, - Nestor

Followers