Showing posts with label SOA. Show all posts
Showing posts with label SOA. Show all posts

Monday, November 9, 2009

Configure Tomcat 6 for JAX-WS

As a servlet container, Tomcat is not a full J2EE container hence requires some additional configuration in order to support JAX-WS. This article describes this configuration step-by-step:

  1. Download a copy of the JAX-WS library – Instructions here were tested this on version 2.1.5 (https://jax-ws.dev.java.net/2.1.5/)
  2. Copy the file JAXWS2.1.5-20081030.jar to an appropriate folder on your computer – say, /root/tools/ JAXWS2.1.5-20081030.jar
  3. Execute the jar: java –jar JAXWS2.1.5-20081030.jar
  4. This in effect unpacks the files contained in this jar. You should get a folder /root/tools/jaxws-ri containing the supporting libraries, docs, tools and samples. Let’s call this path $JAX-WS-HOME$.
  5. Copy the jar files in $JAX-WS-HOME$/lib/ to the Tomcat (let’s call the install path for this $CATALINA_HOME$) library$CATALINA_HOME$/lib and $CATALINA_HOME$/shared/lib path.
  6. Create an folder in the Tomcat installation directory called “endorsed”. The full path should be$CATALINA_HOME$/endorsed.
  7. Copy the files “jaxb-api.jar” and “jaxws-api.jar” from the “$JAX_WS_HOME$/lib” to the “$CATALINA_HOME$/endorsed/”folder.
  8. Edit the Tomcat configuration file “$CATALINA_HOME$/conf/catalina.properties” (You might want to take a backup before you edit it). There should be a property with the key “shared.loader”. Make sure the path $CATALINA_HOME$/shared/lib/*.jar is included in the comma separated list of paths in the value for this property. For example it could look like :shared.loader=/etc/tomcat/shared/lib/*.jar. That’s the main set of configuration that needs to be done.
  9. Before running tomcat, ensure the following property is to be set on the script used to start tomcat:
    -Djava.endorsed.dirs=$CATALINA_HOME$\endorsed
  10. This ensures the two jars we copied into the endorsed folder are picked up.
  11. Start tomcat.
UPDATE: This post makes assumptions about your source and web-archive being correctly set up. When I was doing this, I was working in a Contract-First approach and I wrote a quick guide to that here. This covers how your web.xml and sun-jaxws.xml file should be done. When I get sometime I'll upload a redacted version of my code as it has some work stuff. Good luck!

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!

Friday, November 6, 2009

Contract-First Web Services in practice - part 4 of 5 (.NET)

Welcome to part 4 of 5 of this article series putting contract first SOA through its paces. In the last article, I looked at how to create a php implementation of a wsdl defined in part two. In this article we look at how to create a service implementation in the .NET framework. This will be done in Visual Basic but could work for C#.NET.

Contract-first web service development is generally aspired to but often ignored in favour of code-first approaches because it is often quicker to do so. This is more so prevalent in the .NET framework where the ease of developing ".asmx" web services in Visual Studio drives people down this path. One way of creating "contract-first" web services is create a "code-first" one that closely resembles the contract you want to implement. This is may or may not work depending on the complexity of the contract and is not advised. The approach I advise is to create a binding interface using the "wsdl.exe" program that ships as part of the .NET framework or visual studio. With the path to this executable added to the %PATH% environment variable run the following command in a directory containing the wsdl file.

wsdl /language:VB /serverInterface SampleService.wsdl

If C#.NET artifacts are required replace the language with CS. This creates a single file "SampleServiceInterfaces.vb" that contains the data structures defined in the schema of the wsdl. It is heavily annotated to ensure that when the objects are serialized they are in compliance with wsdl. To complete the web service creation follow the steps below:
  1. Add the bindings interface to an existing or newly created ASP.NET web site.
  2. Now add a new "asmx" web service.
  3. Open the code behind class for the new web service. Make this class implement the binding interface instead of extending Service. Visual Studio should now create the stubs for the methods to be implemented.
  4. Implement the wsdl methods.
For our example, this should work brilliantly for our wsdl defined except for the typed exceptions. In the "infinite wisdom" of the designers of the .NET framework including thrown exceptions in the method signature is not supported. In fact, the binding interface does not create any types for the exceptions. I've created a custom class that creates the required xml for the exceptions.
  1. Imports System.Web.Services.Protocols  
  2. Imports System.Xml  
  3.    
  4.    
  5.       Public Class CustomSoapException  
  6.             Inherits SoapException  
  7.    
  8.             Public Enum ExceptionCode  
  9.                   ServiceException  
  10.             End Enum  
  11.    
  12.             Public Sub New(ByVal ExCode As ExceptionCode, ByVal Message As String)  
  13.                   Me.New(ExCode, SoapException.ClientFaultCode, Message)  
  14.             End Sub  
  15.    
  16.             Public Sub New(ByVal ExCode As ExceptionCode, ByVal SoapFaultCode As XmlQualifiedName, ByVal Message As String)  
  17.                   MyBase.New(ExCode.ToString(), SoapFaultCode, Nothing, CreateXmlNode(ExCode, Message))  
  18.             End Sub  
  19.    
  20.             ''' <summary>  
  21.             '''  
  22.             ''' <ServiceException>  
  23.             '''    <message>The exception message goes here.</message>  
  24.             ''' </ServiceException>  
  25.             '''  
  26.             ''' </summary>  
  27.             ''' <returns></returns>  
  28.             ''' <remarks></remarks>  
  29.             Private Shared Function CreateXmlNode(ByVal ExCode As ExceptionCode, ByVal Message As String) As XmlNode  
  30.                   Dim doc As New System.Xml.XmlDocument  
  31.    
  32.                   Dim detailNode As System.Xml.XmlNode = doc.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, _  
  33.                               SoapException.DetailElementName.Namespace)  
  34.    
  35.                   Dim excType As System.Xml.XmlNode = _  
  36.                               doc.CreateNode(XmlNodeType.Element, _  
  37.                               ExCode.ToString(), _  
  38.                               "http://kimenye.com/soap/")  
  39.    
  40.                   Dim messageNode As System.Xml.XmlNode = _  
  41.                               doc.CreateNode(XmlNodeType.Element, _  
  42.                               "message", _  
  43.                               "http://kimenye.com/soap/")  
  44.    
  45.    
  46.                   messageNode.InnerText = Message  
  47.    
  48.                   excType.AppendChild(messageNode)  
  49.    
  50.                   detailNode.AppendChild(excType)  
  51.    
  52.                   Return detailNode  
  53.             End Function  
  54.       End Class  
Throw an instance of this class to comply with the wsdl. That's it for the .NET implementation. In the next article I'll detail how to do the implementation in Java and wrap up my thoughts on the process.

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.

Wednesday, October 14, 2009

Contract-First Web Services in practice - part 1 of 5.

I've recently been working on a SaaS project where I'm integrating different ISVs (Independent Software Vendors) into a SaaS aggregation platform with a market place environment. Users come to the marketplace, purchase subscriptions to the ISVs' applications and realtime provisioning is expected. An exciting opportunity to test a SOA paradigm as SOAP web-services are a crucial medium of the SOA model and was the first solution to jump to my mind.

Once my approach was selected, my next task was to define a service specification that I would expect the ISVs to implement. This started out as a document with a list of methods and attributes encompassing the functionality I would expect the ISVs to expose. Once this was done, I needed to create a reference implementation in .NET, Java and PHP, and that's where the problems began.

My aim was to create a single WSDL which could easily be implemented in the 3 main programming languages so that only a single client is written. The main issues I had to address are summarized below:

  • Creating a consistent doc/literal format style WSDL
  • Working effectively with arrays in XSD schema
  • Working effectively with typed exceptions - what is the best way of creating Soap Faults?
  • JAX-WS, JAX-RPC, Axis, .NET ASMX, WCF, PHP-SOAP, NU-SOAP - just a snapshot of the different SOAP stacks in common use.
  • Contract-First SOA
I'll be detailing the challenges I faced in a series of 5 tutorials on how to create a non-trivial WSDL and implement it in the 3 programming languages.