none
Exchange 2007 - Web Services - Standard compliance

    Question

  • Hello Forum.

     

    Beeing excited about the possiblities offered by the Exchange Server 2007 Web Service features I'm also courious about some facts that seeem a little bit odd to me.

     

    As a Java programmer I face some difficulties when I want to use the Web Services. The problems start at the time I want to create the necessary Java classes using Maven and JAXWS. It is not possible to create the Java classes without modifying the WSDL document provided by the Exchange server manually. If you try to execute the following Maven task with the original wsdl document you end up with an error message complaining about a missing service definition in the wsdl:

     

    Background: I've created a simple Java project in Eclipse and downloaded the documents: Services.wsdl, messages.xsd and types.xsd form our Exchange server and placed them in folder: ${basedir}/src/main/config/exSrvWsdl.

     

    Code Snippet

    <plugin>

    <groupId>org.codehaus.mojo</< FONT>groupId>

    <artifactId>jaxws-maven-plugin</< FONT>artifactId>

    <executions>

    <execution>

    <id>ex</< FONT>id>

    <phase>generate-sources</< FONT>phase>

    <goals>

    <goal>wsimport</< FONT>goal>

    </< FONT>goals>

    <configuration>

    <wsdlLocation>http://exchange.company.com/EWS/Services.wsdl</< FONT>wsdlLocation>

    <wsdlDirectory>${basedir}/src/main/config/exSrvWsdl</< FONT>wsdlDirectory>

    <wsdlFiles>

    <wsdlFile>services.wsdl</< FONT>wsdlFile>

    </< FONT>wsdlFiles>

    <sourceDestDir>${basedir}/src/main/jaxws-gen</< FONT>sourceDestDir>

    <staleFile>${project.build.directory}/jaxws/stale/.sourcingChanges</< FONT>staleFile>

    </< FONT>configuration>

    </< FONT>execution>

    </< FONT>executions>

    </< FONT>plugin>

     

    Executing mvn clean install leads to the following error:

     

    Code Snippet
    [ERROR] failed.noservice=Could not find wsdl:service in the provided WSDL(s):
    file:/C:/var/prj/eclipseWksp/exchangeWebServiceTestTmp/src/main/c
     At least one WSDL with at least one service definition needs to be provided.

     

    After modifying the wsdl document manually and adding the lines (As outlined in post http://tinyurl.com/4mvb2s)...

     

    Code Snippet

    "ExchangeServices">    

      "ExchangeServicePort" binding="tns:ExchangeServiceBinding">
      "https://my.exchange.com/EWS/Exchange.asmx"/>

     

    ...the Java classes can be generated. That is ok in the first place but leads to another problem:

    If you want to use the following code snippet to obtain the proxy object used for the Exchange server communication you end up with yet another error message:

     

    Code Snippet

    final ExchangeServices exService = new ExchangeServices();

    final ExchangeServicePortType exServiceProxy = exService.getExchangeServicePort();

     

    Code Snippet
    Exception in thread "main"
    javax.xml.ws.WebServiceException: {http://schemas.microsoft.com/exchange/services/2006/messages}ExchangeServices is not a valid service. Valid services are:

     

    Of course it is not possible to use the wsdl document provided by Exchange directly because it is missing the lines added to the document manually. So you end up with the situation that you have to host the manually modified document on your own server and use the code:

     

    Code Snippet

    final String wsdlLocation = "http://localhost:8080/myModifiedServices.wsdl";

    URL url = null;

    URL baseUrl;

    baseUrl = ExchangeServices.class.getResource(".");

    try {

      url = new URL(baseUrl, wsdlLocation);

    }

    catch (final MalformedURLException e) {

      e.printStackTrace();

    }

    final ExchangeServices exService =

    new ExchangeServices(url, new QName("http://schemas.microsoft.com/exchange/services/2006/messages", "ExchangeServices"));

     

    After that you are able to instantiate the proxy object and use the web services - theoretically. Unfortunately it turns out that you have to modify an interface of the generated Java files. Also like outlined in post http://tinyurl.com/4mvb2s you cannot hand over null values for certain parameters. If you call the method resolveNames(...), defined in interface ExchangeServicePortType without modification...

     

    Code Snippet

    /**

    *

    * @param resolveNamesResult

    * @param request

    * @param serverVersion

    * @param impersonation

    * @param requestVersion

    * @param mailboxCulture

    * @param s2SAuth

    */

    @WebMethod(operationName = "ResolveNames", action = "http://schemas.microsoft.com/exchange/services/2006/messages/ResolveNames")

    public void resolveNames(

    @WebParam(name = "ResolveNames", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/messages", partName = "request")

    ResolveNamesType request,

    @WebParam(name = "ExchangeImpersonation", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/types", header = true, partName = "Impersonation")

    ExchangeImpersonationType impersonation,

    @WebParam(name = "SerializedSecurityContext", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/types", header = true, partName = "S2SAuth")

    SerializedSecurityContextType s2SAuth,

    @XmlJavaTypeAdapter(CollapsedStringAdapter.class)

    @WebParam(name = "MailboxCulture", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/types", header = true, partName = "MailboxCulture")

    String mailboxCulture,

    @WebParam(name = "RequestServerVersion", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/types", header = true, partName = "RequestVersion")

    RequestServerVersion requestVersion,

    @WebParam(name = "ResolveNamesResponse", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/messages", mode = WebParam.Mode.OUT, partName = "ResolveNamesResult")

    Holder resolveNamesResult,

    @WebParam(name = "ServerVersionInfo", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/types", header = true, mode = WebParam.Mode.OUT, partName = "ServerVersion")

    Holder serverVersion);

     

     

    ... and the following line of code:

     

    Code Snippet

    exServiceProxy.resolveNames(resolveNamesType,null, null, "DE", requestServerVersion, resolveNamesResponseHolder, serverVersionHolder);

     

    ...you end up with the error message:

     

    Code Snippet

    Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: The request failed schema validation: If the 'nillable' attribute is false in the schema, the 'xsi:nil' attribute must not be present in the instance.

     

    If you change the method signature to:

     

    Code Snippet

    /**

    *

    * @param resolveNamesResult

    * @param request

    * @param serverVersion

    * @param requestVersion

    * @param mailboxCulture

    */

    @WebMethod(operationName = "ResolveNames", action = "http://schemas.microsoft.com/exchange/services/2006/messages/ResolveNames")

    public void resolveNames(

    @WebParam(name = "ResolveNames", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/messages", partName = "request")

    ResolveNamesType request,

    @XmlJavaTypeAdapter(CollapsedStringAdapter.class)

    @WebParam(name = "MailboxCulture", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/types", header = true, partName = "MailboxCulture")

    String mailboxCulture,

    @WebParam(name = "RequestServerVersion", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/types", header = true, partName = "RequestVersion")

    RequestServerVersion requestVersion,

    @WebParam(name = "ResolveNamesResponse", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/messages", mode = WebParam.Mode.OUT, partName = "ResolveNamesResult")

    Holder resolveNamesResult,

    @WebParam(name = "ServerVersionInfo", targetNamespace = "http://schemas.microsoft.com/exchange/services/2006/types", header = true, mode = WebParam.Mode.OUT, partName = "ServerVersion")

    Holder serverVersion);

     

    ..your are able to call the method with the following line of code:

     

    Code Snippet

    exServiceProxy.resolveNames(resolveNamesType, "DE", requestServerVersion, resolveNamesResponseHolder, serverVersionHolder); 

     
     
    We don't need the impersonation feature in our case and if the facts outlined in post http://tinyurl.com/4mvb2s are correct the parameter SerializedSecurityContext is used for server to server communcation and is also not necessary for our case.
     
    An interesting point to all said above is the fact when using a web service reference within a c# project developed in an MS ide you don't have to deal with anything of the modifications mentioned.
     
    Like outlined in one blog article (http://tinyurl.com/5pxseq): It's all about interoperability...
     
    Don't get me wrong - I don't want to start a flame war about Java, OpenSource and close source technology here. I hope that all of us agree that the goal is to provide high quality, maintainable software systems to our customers. If you end up modifying standardized documents, hosting them on your own, modifying generated code you end up with a piece of software that will be broken on the next wsdl document change or another developer re-generating the Java class files without modifying the method signatures...
     
    I hope all of the stuff above is only related to lack of knowledge on my side as I'm very new to the Exchange Web Service technology.
     
     
    Hoping for constructive comments,
     
     
    Henning Malzahn
     
     
     
     
    Friday, July 04, 2008 6:16 AM