Showing posts with label XML. Show all posts
Showing posts with label XML. Show all posts

Monday, November 9, 2009

Contract-First Web Services in practice - part 5 of 5 (Java / J2EE)

This is the last article in the series of 5 and today we're looking at contract first development in Java/J2EE. I've left this for last after considering PHP and NET due to the various application servers and technologies in which this is possible. Java is also my forte hence I'm not shying away from this challenge. We'll look at JAX-WS on J2EE and Tomcat.

JAX-WS / EE5 Application Servers
If you prefer the blue pill and like the neat, java 5+, annotation heavy, jaxws-does-everything-for-you alternate reality, then you're in for a treat. The JAX-WS RI is available in most J2EE servers. I've picked JBOSS (the 4.2.2 GA) which is what I was using for work. JBOSS ships with wstools in the bin directory. Run the command "wsconsume -k SampleService.wsdl". This creates client stubs and a server interface "com.kimenye.SampleServicePortType". This is the class to be implemented. Once this is done, create a web.xml file and create a servlet with the servlet-class as the implementation created. Package this as a web-archive, and pop it into the deploy directory of the app server.

Tomcat 6
Tomcat 6 is a servlet container and not a full J2EE container hence it is not compulsory for it to provide JAX-WS. See my post on how to configure Tomcat 6 to use JAX-WS. Follow the steps below to implement the sample service:
  1. Use "wsimport" tool to create the service stubs. This tool is available from the bin directory of an unpacked JAX-WS installation. The full command to run is : wsimport -keep SampleService.wsdl
  2. Create a web application in your preffered IDE. Copy the artifacts generated from the wsimport into your source. Implement the port type interface. For our example this class will be com.kimenye.ProvisionServicePortType. Compile the implementations and the generated classes.
  3. Copy the wsdl into a folder named wsdl under the WEB-INF directory.
  4. Create a file named sun-jaxws.xml in the WEB-INF directory and fill in the content below:
    1. <endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
    2. version="2.0">

    3. <endpoint name="SampleService" interface="com.applicationsnet.service.soap.SampleServicePortType"
    4. implementation=com.kimenye.SampleServiceImpl" wsdl="WEB-INF/wsdl/SampleService.wsdl"
    5. service="{http://kimenye.com/}SampleService" port="{http://kimenye.com/}SampleServicePort"
    6. url-pattern="/SampleService" />

    7. </endpoints>
  5. Edit the web.xml file to include the content below:
    1. <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2. xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    3. version="2.4">
    4. <description>sample</description>
    5. <display-name>sample</display-name>
    6. <listener>
    7. <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
    8. </listener>
    9. <servlet>
    10. <description>Sample End-Point</description>
    11. <display-name>Sample End-Point</display-name>
    12. <servlet-name>SampleServicePortType</servlet-name>
    13. <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
    14. <load-on-startup>1</load-on-startup>
    15. </servlet>
    16. <servlet-mapping>
    17. <servlet-name>SampleServicePortType</servlet-name>
    18. <url-pattern>/SampleService</url-pattern>
    19. </servlet-mapping>
    20. </web-app>
  6. Create the web-archive file and copy it to your tomcat web applications directory.
  7. Edit the startup script for tomcat to include the property : -Dcom.sun.xml.ws.fault.SOAPFaultBuilder.disableCaptureStackTrace=false – This is related to how the soap faults are formatted and is IMPORTANT!
  8. Start tomcat and enjoy!

Thursday, November 5, 2009

Contract-First Web Services in practice - part 3 of 5 (PHP)

Welcome to the third article in this series of 5 where I'm putting contract-first service oriented architecture through its paces. For a recap, in article one I defined the rules of engagement, and article two I outlined the contract for the service to be developed. With this behind us, the next challenge is to begin the representation of this service in the 3 programming languages and PHP is first of the blocks.

There are several libraries for use in tackling SOAP in PHP (Pear, Nu Soap, PHP Soap). Not being my forte, I selected the inbuilt soap library PHP Soap that ships with PHP 5. I needed a simple approach that supported WSDL 1.1 and "document literal". I didn't think this would be too much to ask until I discovered that there was an affinity for "RPC encoded" style of web services which fits in well with positional parameters which are well supported in php.

The first step in implementing the service is to create the endpoint handler. This is the php file at the url which will respond to this service and is set in the soap:address element of the wsdl. In the example wsdl the endpoint is "http://localhost:8080/index.php", so the endpoint handler is the index.php. The content for this file is shown below:
  1. <?php
  2. //cache the wsdl
  3. ini_set("soap.wsdl_cache_enabled", "0");
  4. //load the code for the service handler
  5. require_once('./SampleServiceClass.php');
  6. //create a new soap server. Refer to the wsdl file
  7. $server = new SoapServer('SampleService.wsdl');
  8. //set the class which will be handling the request
  9. $server->setClass('SampleServiceClass');
  10. //begin the server
  11. $server->handle();
  12. ?>
  1. The first statement is a directive to cache the wsdl for better performance (suitable for production environments but reasonable in this case as it's contract first so we're taking the WSDL as given).
  2. The next statement imports a file SampleServiceClass.php which we have not yet created. More on this soon.
  3. The third statement creates a soap server instance. The constructor takes as one of its arguments, the wsdl file defined in article 2.
  4. The fourth statement sets the class SampleServiceClass (yet to be defined in the file SampleServiceClass.php)
  5. The final statement starts the server instance.
With those five statements, we're setting up the endpoint by tying in the wsdl to the service handler which we will create in the next step.
  1. <?php
  2. //Exception Types
  3. define('SERVICE_EXCEPTION', 'ServiceException'); //thow this error when the user does not exist
  4. //Soap Exception Fault Codes
  5. define('ERROR_CODE_CLIENT', "Client");
  6. define('ERROR_CODE_SERVER', "Server");
  7. /**
  8. *
  9. * This class handles the Sample Service Soap Requests
  10. *
  11. */
  12. class SampleServiceClass
  13. {
  14. /**
  15. * Create an exception
  16. *
  17. * <ns2:SampleException xmlns:ns2="http://kimenye.com/soap/">
  18. * <message>Some weird error occurred</message>
  19. * </ns2:SampleException>
  20. *
  21. * @param object $exception
  22. * @return
  23. */
  24. function createException($exception,$message)
  25. {
  26. $typedException = new StdClass();
  27. $typedException->message = $message;
  28. $detail = null;
  29. $error_code = ERROR_CODE_CLIENT;
  30. if ($exception == SERVICE_EXCEPTION)
  31. {
  32. $detail->ServiceException = $typedException;
  33. }
  34. return new SoapFault($error_code,$exception,null,$detail,$exception);
  35. }
  36. /**
  37. * This is the sample operation.
  38. *
  39. * @param object $SampleOperationRequest
  40. *
  41. * @return
  42. */
  43. function SampleOperation($SampleOperationRequest)
  44. {
  45. $num_of_args = func_num_args();
  46. //In this example the custom fields are compulsory
  47. if(func_num_args()<1)
  48. {
  49. return $this->createException(SERVICE_EXCEPTION, "SampleOperationRequest -Incorrect parameters passed in.");
  50. }
  51. $Demo = $SampleOperationRequest->DemoField;
  52. $CustomFields = $SampleOperationRequest->SampleFields;
  53. $num_custom_fields = count($CustomFields->SampleField);
  54. /**
  55. * This is to demonstate how to access the custom fields.
  56. *
  57. * If only one custom field is required, access is
  58. *
  59. * $fieldOne = $CustomFields->CustomField->FieldValue
  60. */
  61. if($num_custom_fields == 2)
  62. {
  63. if($CustomFields->SampleField[0]->FieldName == 'a field')
  64. {
  65. $fieldOne = $CustomFields->CustomField[0]->FieldValue;
  66. $fieldTwo = $CustomFields->CustomField[1]->FieldValue;
  67. }
  68. if($CustomFields->SampleField[0]->FieldName == 'another field')
  69. {
  70. $fieldTwo = $CustomFields->CustomField[0]->FieldValue;
  71. $fieldOne = $CustomFields->CustomField[1]->FieldValue;
  72. }
  73. }
  74. $ro1 = new StdClass();
  75. $ro1->DemoField = "Scott";
  76. $custom_logo = new StdClass();
  77. $custom_logo->FieldName = 'logo';
  78. $custom_logo->FieldValue = 'A logo';
  79. $nickname = new StdClass();
  80. $nickname->FieldName = 'nickname';
  81. $nickname->FieldValue = 'Scotty';
  82. $custom_logo2 = new StdClass();
  83. $custom_logo2->FieldName = 'logo';
  84. $custom_logo2->FieldValue = 'A logo';
  85. $nickname2 = new StdClass();
  86. $nickname2->FieldName = 'nickname';
  87. $nickname2->FieldValue = 'Scotty';
  88. $custom_fields = array($custom_logo, $nickname);
  89. $custom_fields2 = array($custom_logo2, $nickname2);
  90. $ro2->SampleFields = $custom_fields;
  91. $ro2 = new StdClass();
  92. $ro2->DemoField = "Hello";
  93. $ro2->SampleFields = $custom_fields2;
  94. $objs = array($ro1, $ro2);
  95. return $objs;
  96. }
  97. }
  98. ?>
This class is a lot more substantial than the previous as it handles the Soap requests.
  1. First, is the definition of a couple of exception constants.
  2. Second, the method "createException" throws the contract specified exception.
  3. Finally, the SampleOperation method reads the input and returns the array output.

That's it!

Monday, October 19, 2009

Contract-First Web Services in practice - part 2 of 5

This is article is the second part of a five article series on a working example of SOA at work. In the last article I introduced the challenge, coming up with a fairly robust WSDL which could be implemented in multiple different programming languages consistently.

The service we are creating has one operation. The input for this sample operation takes an xml message with one element as well as an array of a nested object. The output returns an array of the same input objects (I had to redact the more meaningful input and output messages as they were work related). It initially seems complicated because I'm trying to capture all the complexities of xml structures and how the map to data structures that programmers in modern programming languages often take for granted. Ok, enough introduction, let's get onto the WSDL.

WSDL
Click the expand source link and voila! That's the WSDL. Now to run you through the xml elements that constitute this WSDL.

Exceptions
Exceptions in SOAP are xml messages that can be defined in the schema. For this example, the exception is aptly named ServiceException, and it has an element named "message" which happens to be a string.

  1. <!-- Exception message, contains a string with the field message -->
  2. <xsd:complexType name="ServiceException">
  3. <xsd:sequence>
  4. <xsd:element name="message" nillable="true" type="xsd:string"/>
  5. </xsd:sequence>
  6. </xsd:complexType>
Arrays & Collections
There are several ways to represent arrays in XML. I've chosen for this sample, the simplest way which is to use the xml attribute "maxOccurs" and set it to "unbounded".
  1. <!-- A sample field with two elements, FieldName and FieldValue -->
  2. <xsd:complexType name="SampleField">
  3. <xsd:sequence>
  4. <xsd:element minOccurs="1" maxOccurs="1" name="FieldName" type="xsd:string"/>
  5. <xsd:element minOccurs="1" maxOccurs="1" name="FieldValue" type="xsd:string"/>
  6. </xsd:sequence>
  7. </xsd:complexType>
  8. <!-- An array of sample fields -->
  9. <xsd:complexType name="ArrayOfSampleField">
  10. <xsd:sequence>
  11. <xsd:element minOccurs="0" maxOccurs="unbounded" name="SampleField" nillable="true" type="tns:SampleField"/>
  12. </xsd:sequence>
  13. </xsd:complexType>
Declarative SOAP Exceptions
For this sample, soap exceptions are declared in the WSDL, this is in order to detect multiple exceptions. In this sample, we only have ServiceException, but could have included others, to programmatically detect which exception was thrown. This declaration is achieved by using the "fault" element. Note that the exception needs to be declared in both the binding and port type.
  1. <binding name="SampleServiceBinding" type="tns:SampleServicePortType">
  2. <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
  3. <operation name="SampleOperation">
  4. <soap:operation soapAction="urn://SampleService#SampleOperation"/>
  5. <input>
  6. <soap:body use="literal"/>
  7. </input>
  8. <output>
  9. <soap:body use="literal"/>
  10. </output>
  11. <fault name="ServiceException">
  12. <soap:fault name="ServiceException" use="literal"/>
  13. </fault>
  14. </operation>
  15. </binding>
That's it for the most part. In the next article, I'll be looking at the PHP implementation of this WSDL.