Free/Busy Data from Exchange Web Services via Java

Exchange Server 2007 and 2010 offer web services that can be accessed via standard SOAP messages. This post will hopefully help you in consuming a very specific web service operation (free/busy data) from your Java environment. I expect that you can take the information found in this post and extend it to other operations. Please note that I will not discuss how to setup Exchange Web Services and will instead assume that such a service is already operational and online.

I cannot guarantee that this is the best, fastest, or most efficient way to get this working. These are the steps that I have compiled through self-teaching, reading various Microsoft documentation, and sifting through a large amount of blogs on separate issues that collectively guided me to the solution. Unlike most of the guides I found, this post should take you from the beginning of the coding process all the way to the end.

The first step is to obtain the WSDL and XSD files that describe the Exchange Web Services. These are usually hosted in the same location as the actual ASMX web service. I only have experience with the setup I use at my company, so results my differ for your Exchange setup.

  • Download Services.wsdl to local folder from URL to Exchange Web Service (ie.
  • Download messages.xsd to local folder from URL to Exchange Web Service (ie.
  • Download types.xsd to local folder from URL to Exchange Web Service (ie.

Microsoft likes to make things complicated and this is true with the files we just downloaded. The WSDL does not contain a ‘services’ tag so the importer we use to create the proxy classes will not work properly.

  • Edit the Services.wsdl file and add the following as the last tag in the main tag.
<wsdl:service name="ExchangeWebService">
  <wsdl:port name="ExchangeWebPort" binding="tns:ExchangeServiceBinding">
    <soap:address location="" />
  • Download this XSD file to the same directory as the above three:
  • Edit the types.xsd file that we downloaded by changing the <import namespace> tag to the following:
<xs:import namespace=“” schemaLocation=“xml.xsd”/>
  • Edit Services.wsdl by adding <services> section to end. See [1] for tag to add to .wsdl.
  • Edit types.xsd to point to correct xml.xsd schema. <xs:import namespace=“” schemaLocation=“xml.xsd”/>

Alright, enough editing, time to create the proxy classes so that we can finally call this service.

  • Open a command prompt, change directory to the folder that contains your WSDL and XSD files from above
  • Type wsimport -keep -Xnocompile Services.wsdl -wsdllocation http://localhost/wsdl/ExchangeWebService.wsdl to generate proxy classes

wsimport is the name of the Java tool that will parse the WSDL and generate Java class files from the schema. Here is what the flags do:

  • keep: keeps the generated files
  • Xnocompile: does not create compiled .class files (you’ll see why in a second)
  • Services.wsdl: name of the WSDL file to parse
  • -wsdllocation http://localhost/wsdl/ExchangeWebService.wsdl: this will generate the proxy classes and tell them to point to this temporary and non-existent location (you’ll see why in a minute)

The generated files are in a long tree of folders.

  • Go all the way down to the messages folder
  • Edit by commenting out every parameter of the getUserAvailability method except for the Request and Response types
  • Use javac command to compile the generated source code (ie.  javac messages/*.java types/*.java)
  • Use jar command to create jar for entire com/microsoft/* structure (ie. jar cf ExchangeWebService.jar com)
  • Copy the generated jar to /WEB-INF/lib of web project and add it to your build path in Eclipse

Finally you’ve created the proxy classes after making annoying edits to the WSDL, the XSDs, and the generated source code. Now we need to tell our web application that the previously non-existent localhost path that we provided the proxy classes should be redirected to the real path of the WSDL.

  • Create a file called jax-ws-catalog.xml in /WEB-INF/classes/META-INF of your web project using the following code:
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
  <system systemId="http://localhost/wsdl/ExchangeWebService.wsdl" uri="wsdl/ews/Services.wsdl"/>
  • Copy the WSDL and all XSD files to /WEB-INF/classes/META-INF of your web project

The reason we’ve provided a dummy location to the proxy classes and forced it to redirect to the real location is so that you can have a flexible path to the WSDL files. This approach will remove the hard coded references to a file and instead allow you to configure the jax-ws-catalog.xml file with a real path.

The final step is option for those of you using SSL on your Exchange Web Services. If you need to access the web services and you find that they are encrypted with HTTPS (SSL), you need to follow the next steps.

  • Use Firefox to save the SSL certificate of the Exchange Web Services host to %JAVA_HOME%/lib/security
  • Run the keytool command to import this SSL certificate as trusted to the Java keystore (ie. keytool -import -alias ca -file -keystore cacerts -storepass changeit)

And finally, after all that insanity, you can reference the code below to understand how to query for free/busy information from the classes that we’ve created.

public static void main(String[] args)
		// factory to generate service ports
		ExchangeWebService serviceFactory = new ExchangeWebService();
		// get the port to the exchange web service
		ExchangeServicePortType service = serviceFactory.getExchangeWebPort();
		// we are going to send a UserAvailability request
		GetUserAvailabilityRequestType request = new GetUserAvailabilityRequestType();
		// this will be the standard time for the time zone to query
		SerializableTimeZoneTime standardTime = new SerializableTimeZoneTime();
		standardTime.setBias(5 * 60); // this is the offset from UTC in minutes (UTC-5 is +300)
		// this will be the daylight time for the time zone query
		SerializableTimeZoneTime daylightTime = new SerializableTimeZoneTime();
		daylightTime.setBias(4 * 60); // this is the offset from UTC in minutes (UTC-4 is +240)
		SerializableTimeZone timezone = new SerializableTimeZone();
		// setup the duration to check for free/busy
		DatatypeFactory datatypeFactory = DatatypeFactory.newInstance();
		Duration duration = new Duration();
			new GregorianCalendar(2012, Calendar.AUGUST, 6, 0, 00))); // change to your own date
			new GregorianCalendar(2012, Calendar.AUGUST, 6, 23, 59))); // change to your own date

		FreeBusyViewOptionsType fbViewOptions = new FreeBusyViewOptionsType();

		// setup the list of email addresses to check for free/busy
		ArrayList<String> emails = new ArrayList<String>();
		emails.add(""); // change to your email addresses
		ArrayList<EmailAddress> emailAddresses = new ArrayList<EmailAddress>();
		for(String email : emails)
			EmailAddress emailAddress = new EmailAddress();
		ArrayOfMailboxData mailboxes = new ArrayOfMailboxData();
		for(EmailAddress emailAddress : emailAddresses)
			MailboxData mailbox = new MailboxData();
		Holder<GetUserAvailabilityResponseType> responseHolder = new Holder<GetUserAvailabilityResponseType>();
		service.getUserAvailability(request, responseHolder);
		List<FreeBusyResponseType> responses = responseHolder.value.getFreeBusyResponseArray().getFreeBusyResponse();
		if(responses.size() < 1)
			throw new Exception("No free/busy data available.");
			for (FreeBusyResponseType response : responses) 
				if(response.getResponseMessage().getResponseClass() == ResponseClassType.ERROR)
					System.out.println("Error: " + response.getResponseMessage().getMessageText());
					FreeBusyView fbv = response.getFreeBusyView();
					System.out.println("Merged free/busy data: " + fbv.getMergedFreeBusy());
	catch (DatatypeConfigurationException e) 
	catch(Exception ex)