Consuming Exchange Web Services with Java using Glassfish and Metro: Creating the Web Service Client

Recently I was given the opportunity to write an online email client at my job. Throughout the application, at key points of contact, we will integrate message creation for students to seamlessly contact instructors, advisors, classmates, fellow club members, etc. Also, we wanted a full email client worked into the application. My point is, there are good reasons for writing the email client. This is not just some pipe dream.

Technology Background

Here's the technology background for this work:

Email Server: Exchange 2007 SP1 (with Exchange Web Services enabled)
Programming Language: Java 1.5 or later
Application Server: Oracle Application Server 10g (production)
  Glassfish v2 (local development)
Web Service Stack: Glassfish Metro (guide for deployment on OC4J later)
Development IDE: NetBeans 6.1 (guide for Eclipse 3.4 later)

Enable Exchange Web Services

The first step in this process, is to speak with your Exchange administrator. Have him or her turn on Exchange Web Services. If they're turned on, you should be able to get the WSDL at https://mail.example.com/EWS/exchange.asmx (which redirects to https://mail.example.com/ews/Services.wsdl in most cases, I believe). Remember this URL for later.

Set up your development environment

You will need two core pieces of technology to get started developing. The first is an application server. Download Glassfish v2 (or Glassfish v3 if you're savvy). You will also need a development environment. Install NetBeans 6.1. If you're using Linux, check your package manager first. The files may be available through your distribution. If you're on Ubuntu Linux, like I am, install the netbeans and glassfishv2 packages. If you're on Windows, Mac, or feel like doing a manual Linux install, use the links above and the corresponding installation directions.

Preparing NetBeans

We will need a few NetBeans plugins: Java EE, Web Applications, Web Services, and Glassfish. Install them through the plugin manager at Tools → Plugins. Also, do any recommended plugin/platform updates at this time as well.

Now we have to add Glassfishv2 as a server in NetBeans:

  1. Go to Tools → Servers → Add Server...
  2. In the 'Choose Server' dialog box that comes up, select Glassfishv2 as your server and click 'Next'.
  3. In the next screen, set 'Platform Location' to the directory where you installed Glassfish.
    • Note: On Ubuntu, if you installed from the repositories, this is /usr/share/glassfishv2
  4. Also, select 'Register Local Default Domain'. The default domain for most installs is ${glassfish_home}/domains.
    • I ran into several issues on Ubuntu setting 'Register Local Domain'. The following instructions are for Ubuntu users only:
      1. The pre-configured default domain from the package manager is et up at /var/lib/glassfishv2/domains/domain1 but if you try to enter this as the 'Domain Folder' you will get the error Cannot find valid domain.xml file. I believe this is because the platform location and domain folder are in two different locations.
      2. So I had to stop the default domain executing sudo /var/lib/glassfishv2/domains/domain1/bin/stopserv.
      3. Then, back in NetBeans at the 'Platform Folder Location' dialog, I selected 'Create Personal Domain' with the default profile.
      4. Go out to the operating system's file manager and create the folder /home/[your username]/glassfishv2/domains
      5. Back in NetBeans on the 'Domain Folder Location' dialog. Set the 'Domain Folder' to /home/[your username]/glassfishv2/domains/netbeans_domain and click next.
      6. Set your desired administrator username and password and click next.
      7. On the 'Creation Properties' dialog I noticed I was not suggested the standard ports for my application server. So if you want to use the standard port numbers (recommended) make sure they're all set to the following values:
        • Admin Port: 4848
        • Admin JMX Port: 8686
        • Domain HTTP Port: 8080
        • JMS Port: 7676
        • ORB Listener Port: 3700
        • HTTPS Port: 8181
        • ORB SSL Port: 3820
        • ORB MultiAuth Port: 3920
      8. Click 'Finish'
  5. After finishing you should now see the server startup messages show up in the NetBeans console. It will end in Domain netbeans_domain created.

Setting up the NetBeans Project

Create a new project of type Web → Web Application. I set mine up as a JavaEE 5 project. After the project has been created you'll see a generic NetBeans web project.

Remember that WSDL file from earlier? We're finally ready to start using it. Unfortunately, Microsoft didn't format it 100% correct. There's some discussion on the topic over at the MSDN forums. The WSDL being provided by Exchange is missing a wsdl:service element. If you tried to add a Web Service Client pointing at this WSDL you'd get the error.

Web Service Client can not be created by JAXWS:wsimport utility.
Reason: Could not find wsdl:service in the provide WSDL(s):

...

You need to provide at least one WSDL with at least one service definition.

To get around this error, download the file at https://mail.example.com/ews/Services.wsdl and save a copy locally in your project's web folder, eg. [NetBeans Project Folder]/web/exchange.wsdl. If you tried to create a Web Service Client from this WSDL you'll see that the relative references to messages.xsd and types.xsd are broken. So you also need to download copies of https://mail.example.com/ews/messages.xsd and https://mail.example.com/ews/types.xsd and save them in the same local folder as the WSDL.

So now that we have all the files necessary, let's edit that WSDL to be valid. Open it up in NetBeans from your 'Web Pages' folder. At the second to last line, before the final tag place the following XML code:


      
             
      

Microsoft's provided XML is still not ready for parsing. If you tried to read the file now, you'd get the error:

Web Service Client can not be created by JAXWS:wsimport utility.
Reason: Undefined Attribute 'xml:lang'

There might be a problem during java artifacts creation: for example a name conflict in generated classes.

The offending line is around line 3370 of types.xsd. It reads:


Someone please tell me how to get around this error if you have a better solution. For the time being I just commented the line out.


Generating the Java code

Finally! We're done fixing up the WSDL's and XSD files that Microsoft has presented. Let's generate some code. In NetBeans, right click on the project and select New → Web Service Client.

In the dialogs that come up:
Local File: [NetBeans Project Folder]/web/exchange.wsdl
Client Style: JAX-WSl

Click 'Finish' and you'll start the generation of your web services client from the WSDL file. You should see a bunch of build script output and compiling going on below in the NetBeans output area. If it's successful, it will end with something like:

 

wsimport-client-generate:
Created dir: /home/[your username]/ExchangeWebServices/build/web/WEB-INF/classes
wsimport-client-compile:
Compiling 409 source files to /home/[your username]/ExchangeWebServices/build/web/WEB-INF/classes
BUILD SUCCESSFUL (total time: 19 seconds)

Now, to develop code using these web services, the NetBeans documentation recommends using their "Web Service Client Resources → Call Web Service Operation" context menus and code generation. I find this terribly annoying and would rather just copy the generated Java files into the src folder of my project:

  1. Open up your operating system's file manager.
  2. Browse to [NetBeans Project Folder]/build/generated/wsimport/client
  3. You'll see one folder in here named 'com'. This is the start of the com.microsoft.schemas.exchange.services._2006 packages. So copy the 'com' folder.
  4. Navigate back to [NetBeans Project Folder]/src/java and paste the 'com' folder in here.

Now open up NetBeans again and you should see all the 409 generated Exchange Web Service files in your source package split into two package groups. Congratulations! The toughest part of this entire tutorial series is over.

Conclusion

The frustrations I went through figuring all this out is 50% of what inspired this tutorial series you're reading now :) I felt there were no good Java/Exchange Web Service guides out there, esp. tailored to Metro/NetBeans. In my next article, I will go over authenticating through web services and returning a successfully authenticated ExchangeServicePortType.

Note: Please, if you have suggested improvements to this article, or any other comments I encourage you to leave me feedback. I'd love to get some constructive criticism, or hear back if the guide helped you.

Revisions

I've updated this article a few times as I receive user comments and look into questions further:

  • Jan 15, 2009: Updated the "Generating the Java code" section, and included a link to the NetBeans documentation for how they recommend invoking web service operations, and how I think their way is annoying.

14 Comments

Thank you!

Thanks a lot for this guide! I was searching for something like this for two days. I still can't believe how incredibly complicated or wrong all other guides on this subject were!

Kudos!

Web service calls in XML format?

I am developing an email connector that synchronizes PIM data (email, contact, calendar, tasks) with wireless devices. I am looking for captures of Exchange web services application protocol XML between the endpoints (WS client and server). In particular, I would like to see how updateItem is applied to contact item?

After I copy in the 'com'

After I copy in the 'com' folder some of the files are failing e.g., package-info.java is fine built package-info_1.java "error parsing file". This is the same error for are the troubled files. It looks as if the files are duplicated all through the packages but some are fine in others give the ‘noted above’ error message.

Thoughts?

Thanks, Eric

Missing ExchangeServicePort.java

Thanks for this excellent guide.
I am using JDeveloper 11g to generate the EWS java classes. It generated
101 java files under: com\microsoft\schemas\exchange\services\_2006\messages, plus ExchangeServices.jaxrpc
and 307 java files under: com\microsoft\schemas\exchange\services\_2006\types
so total 408 files, instead of 409 files as you mentioned.

When I tried to follow your 2nd topic, Using ExchangeServicePortType with Authentication Credentials, it refers to ExchangeServicePortType class which I don't see under com\microsoft\schemas\exchange\services\_2006\messages.

To generate the java files, on JDeveloper I did:
- File > New > (New GAllery) dialog > + Business Tier > Web Services > Java Web Services from WSDL

Some one suggested to do: Web Services > Web Service Proxy.
But while doing that (during 'Binding Model' progress dialog) it shows
Validation Failed
oracle.jdeveloper.websercie.model.WebServiceException:
The WSDL document is invalide for the following reason: (null)
oracle.jdeveloper.webservices.model.WebServiceException: The WSDL document is invalid for the following reason:

(null)
at oracle.jdeveloper.webservices.model.CoreHashStructureModel.validate(CoreHashStructureModel.java:226)
at oracle.jdeveloper.webservices.model.CoreHashStructureModel.validate(CoreHashStructureModel.java:153)
at oracle.jdevimpl.webservices.wizard.jaxrpc.proxy.ProxyJaxWsSpecifyWSDLPanel.validate(ProxyJaxWsSpecifyWSDLPanel.java:250)
at oracle.jdevimpl.webservices.wizard.jaxrpc.common.SpecifyWsdlPanel.buildModel(SpecifyWsdlPanel.java:1086)
at oracle.jdevimpl.webservices.wizard.jaxrpc.common.SpecifyWsdlPanel$6.run(SpecifyWsdlPanel.java:644)
at oracle.ide.dialogs.ProgressBar.run(ProgressBar.java:643)
at java.lang.Thread.run(Thread.java:619)

The Services.wsdl (types.xsd) file(s) are modified according to your guide, successfully generated java classes by:
Web Services > Java Web Services from WSDL
but did not create the ExchangeServicePort.java file.

I will appreciate if anyone can help me with the fix for this.

Thanks

Missing ExchangeServicePort.java

Thanks for this excellent guide.
I am using JDeveloper 11g to generate the EWS java classes. It generated
101 java files under: com\microsoft\schemas\exchange\services\_2006\messages, plus ExchangeServices.jaxrpc
and 307 java files under: com\microsoft\schemas\exchange\services\_2006\types
so total 408 files, instead of 409 files as you mentioned.

When I tried to follow your 2nd topic, Using ExchangeServicePortType with Authentication Credentials, it refers to ExchangeServicePortType class which I don't see under com\microsoft\schemas\exchange\services\_2006\messages.

To generate the java files, on JDeveloper I did:
- File > New > (New GAllery) dialog > + Business Tier > Web Services > Java Web Services from WSDL

Some one suggested to do: Web Services > Web Service Proxy.
But while doing that (during 'Binding Model' progress dialog) it shows
Validation Failed
oracle.jdeveloper.websercie.model.WebServiceException:
The WSDL document is invalide for the following reason: (null)
oracle.jdeveloper.webservices.model.WebServiceException: The WSDL document is invalid for the following reason:

(null)
at oracle.jdeveloper.webservices.model.CoreHashStructureModel.validate(CoreHashStructureModel.java:226)
at oracle.jdeveloper.webservices.model.CoreHashStructureModel.validate(CoreHashStructureModel.java:153)
at oracle.jdevimpl.webservices.wizard.jaxrpc.proxy.ProxyJaxWsSpecifyWSDLPanel.validate(ProxyJaxWsSpecifyWSDLPanel.java:250)
at oracle.jdevimpl.webservices.wizard.jaxrpc.common.SpecifyWsdlPanel.buildModel(SpecifyWsdlPanel.java:1086)
at oracle.jdevimpl.webservices.wizard.jaxrpc.common.SpecifyWsdlPanel$6.run(SpecifyWsdlPanel.java:644)
at oracle.ide.dialogs.ProgressBar.run(ProgressBar.java:643)
at java.lang.Thread.run(Thread.java:619)

The Services.wsdl (types.xsd) file(s) are modified according to your guide, successfully generated java classes by:
Web Services > Java Web Services from WSDL
but did not create the ExchangeServicePort.java file.

I will appreciate if anyone can help me with the fix for this.

Thanks

Missing ExchangeServicePort.java

Missing ExchangeServicePort.java

Comment out the

and it's end tag

This will cause the generator to generate the 409 files instead of the 407 files.
those two lines kept the binding and the ports hidden from the service stanza.

I am using Netbeans 6.7.1 and was having the same problem but when those lines were commented out, it took care of the issue and I was even able to run the testdemo that was created by Reid by just changing the URL, the domain and user credentials.

Hope it helps

Undefined Attribute 'xml:lang' workaround

Hi Reid,
To work around Undefined Attribute 'xml:lang' issue you can replace
<xs:import namespace="http://www.w3.org/XML/1998/namespace"/>
with
<xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>
at the beginning of types.xsd.
For explanation see:
http://social.technet.microsoft.com/forums/en-US/exchangesvrdevelopment/...

com.ctc.wstx.exc.WstxParsingException while reading some emails

Hi,
We have a java standalone job running for years, which connects to exchange webservice api and reads emails. Only 90% of the email were successfully read and 5% to 10% are failing throwing exception as below
[com.ctc.wstx.exc.WstxLazyException] com.ctc.wstx.exc.WstxParsingException: In
valid character reference: null character not allowed in XML content.
at javax.xml.stream.SerializableLocation@eab0eab
at com.ctc.wstx.exc.WstxLazyException.throwLazily(WstxLazyException.java:45)
at com.ctc.wstx.sr.StreamScanner.throwLazyError(StreamScanner.java:704)
at com.ctc.wstx.sr.BasicStreamReader.safeFinishToken(BasicStreamReader.java:3657)
at com.ctc.wstx.sr.BasicStreamReader.getTextCharacters(BasicStreamReader.java:830)
at com.sun.xml.ws.util.xml.XMLStreamReaderFilter.getTextCharacters(XMLStreamReaderFilter.java:140)
at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.handleCharacters(StAXStreamConnector.java:323)
at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:187)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:360)
at com.sun.xml.bind.v2.runtime.BridgeImpl.unmarshal(BridgeImpl.java:120)
at com.sun.xml.bind.api.Bridge.unmarshal(Bridge.java:233)
at com.sun.xml.ws.message.stream.StreamMessage.readPayloadAsJAXB(StreamMessage.java:260)
at com.sun.xml.ws.client.sei.ResponseBuilder$Body.readResponse(ResponseBuilder.java:469)
at com.sun.xml.ws.client.sei.ResponseBuilder$Composite.readResponse(ResponseBuilder.java:193)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:121)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:89)
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:130)
at $Proxy38.getItem(Unknown Source)
at com.company.eai.ews.EwsItemHelper.callGetItems(EwsItemHelper.java:511)
at com.company.eai.ews.EwsItemHelper.getItemsById(EwsItemHelper.java:433)
at com.company.eai.ews.EwsItemHelper.getItemsById(EwsItemHelper.java:438)
at com.company.eai.ews.EwsItemHelper.getItemsById(EwsItemHelper.java:438)
at com.company.eai.ews.EwsItemHelper.getItemsById(EwsItemHelper.java:438)
at com.company.eai.ews.EwsItemHelper.getItemsById(EwsItemHelper.java:438)
at com.company.eai.ews.EwsItemHelper.getItemsById(EwsItemHelper.java:438)
at com.company.eai.ews.EwsItemHelper.getItemsById(EwsItemHelper.java:393)
at com.company.eai.ews.EwsItemHelper.getItems(EwsItemHelper.java:362)

Same exception with different unicode characters
[com.ctc.wstx.exc.WstxLazyException] com.ctc.wstx.exc.WstxParsingException: Il
legal character entity: expansion character (code 0x1b) not a valid XML character
at javax.xml.stream.SerializableLocation@71357135

The exchange server version is Exchange2007_SP1.
There is one more scenaio, where it is unable to read the folder contents (i think the method is findFolderItem(), as some emails have special characters. Do you have any idea, why Exchange webservice is not able to handle those unicode characters? any known fix? Thanks for your help.

Regards,
Hari

Add new comment