Hi everybody and welcome back to wm-explorer.
Wow. So it’s 2018. I wanted to start this post with “It has been a year since my last post”, but it’s too early in the year for such cheesy jokes, right? Yeah, I thought so.
The early 2000s seem so far away: no Facebook, no Instagram, Google was barely starting its dominance. Yet these were the years that marked the start of web services. So, without further ado, let’s dive in into the second part of the “Web Services – Headers and Handlers” talk.
If you did not catch the first part, I recommend checking it out here, before you start reading this blog post.
But, if you are in a hurry and do not have time for that let me just sum it for you:
- We started with a few web services history and trivia
- I advocated how easy it is to work with web services in Integration Server
- Then we talked about the SOAP Headers: structure and best practices
- We dove in Code First web services with a simple example
- We looked at Handlers: definition, usage, example and a “catch”
In this post, we will continue with Contract First web services and we will see if the handlers are used differently with this approach.
Contract First approach to Web Services
In this approach, you do not start with the code, but with the contract, i.e the WSDL. So basically it’s the other way around of the Code First approach.
Using Eclipse or any other tool that has a WSDL Editor (or Notepad++ for that matter if you feel super confident in your WSDL writing abilities) you create your WSDL file (and of course the associated XSD files, if necessary).
But why would anyone start with the WSDL? Why would anyone put that much amount of work upfront, before the actual services are created?
Well, these are all valid questions. This approach is harder than the Code First approach where the WSDL is a byproduct of the actual implementation, where if you change the implementation your contract will, most likely, change. In the Code First approach, the implementation drives the interface which is a clear anti-pattern.
Let’s see some of the advantages of using the Contract First approach.
If you are using this approach you are clearly decoupling the client from any logic that you have on the server or the server’s technology stack. You can change the framework or even the programming language of the provider and everything will still work as long as the contract is respected.
This happens so because the WSDL and the associated XSD files represent the source of truth. At all point, they represent the standard all the other parties have to conform with.
This was one of the strong points of web services, right? Interoperability! As I said in the previous post, web services are not biased to a certain programming language. A service can be created in .NET and consumed in Java (to give an overused example).
This is possible because web services use XML as a transport standard. You might say now: “Hold it! Both Contract First and Code First approach use XML. This is not a valid advantage”.
It is an advantage because, in case of Code First, different SOAP stacks will generate different WSDL and XSD files. Choose JAX-WS and you will get something. Axis will probably yield something different. CXF maybe something else. You get the point.
Do you really want to leave something as important as the CONTRACT to the mercy of a third party? The CONTRACT can make or break relationships. In this case the relationship between you as the provider and your client(s).
Better communication and efficiency
This point is related to the first one. The teams that are working on the provider and the consumer can work in parallel because they have agreed to a contract. In this way, the team that writes the client doesn’t need to wait until the provider team finishes work.
Because you have defined your web service contract model in XSD files it is easy to use the model in other WSDLs (either defined by you or by another team/department).
Better performance and control
If you are using the Code First approach you don’t really have control of what is sent over the wire. The marshaling process of different SOAP stacks is different and, especially if you have a complicated model with multiple references, you might be sending more that is required, thus affecting the general performance.
This should be enough for you to be convinced that the Contract First approach is the way to go for creating a timeless (whatever that means in this era of perpetual change) web service.
There are other considerations like the fact that the XML Schema is more descriptive than a programming language or the possibility to define XSD extensions, but this falls more or less into the categories above.
So my advice is to make an effort upfront, define the contract and then reap the fruits of this decision afterward.
At the end of the day, your life will be easier because you complied with the standards. And the standard is, in this case, the CONTRACT, i.e the WSDL.
Use the Code First approach only for tests, exercises or short-lived web services.
Contract First Web Services Example
When using the Contract First approach, you need to put every piece of needed information (this includes headers) in the WSDL and associated XSD files, because these artifacts will be shared with your clients.
Although Integration Server lets you add headers in the same way as for Code First web services, it is advisable to put them in the WSDL.
This section helps you define your WSDL with the header documents included so that when the WSP is created, headers are created as well.
Let’s start with the obvious: defining the header structure.
To make sure that I eat my own dog food, although the structure is trivial, I choose to create it in a separate XSD.
<?xml version="1.0"?> <xsd:schema xmlns:tns2="https://www.wm-explorer.com/ContractFirst/Header" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="https://www.wm-explorer.com/ContractFirst/Header"> <xsd:element name="ContractFirstRequestHeader" type="tns2:ContractFirstRequestHeaderType"/> <xsd:element name="ContractFirstResponseHeader" type="tns2:ContractFirstResponseHeaderType"/> <xsd:complexType name="ContractFirstRequestHeaderType"> <xsd:sequence> <xsd:element name="messageId" nillable="true" type="xsd:string"/> <xsd:element name="application" nillable="true" type="xsd:string"/> <xsd:element name="user" nillable="true" type="xsd:string"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="ContractFirstResponseHeaderType"> <xsd:sequence> <xsd:element name="messageId" nillable="true" type="xsd:string"/> <xsd:element name="correlationId" nillable="true" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:schema>
In the same way, I have defined the structure of the actual message being transported.
<?xml version="1.0"?> <xsd:schema xmlns:tns="https://www.wm-explorer.com/ContractFirst/Greeting" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="https://www.wm-explorer.com/ContractFirst/Greeting"> <xsd:element name="sayHelloRequest" type="tns:sayHelloInput"/> <xsd:element name="sayHelloResponse" type="tns:sayHelloOutput"/> <xsd:complexType name="sayHelloInput"> <xsd:sequence> <xsd:element name="personalInformation" nillable="true" type="tns:PersonalInformation"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="PersonalInformation"> <xsd:sequence> <xsd:element name="firstName" nillable="true" type="xsd:string"/> <xsd:element name="lastName" nillable="true" type="xsd:string"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="sayHelloOutput"> <xsd:sequence> <xsd:element name="greeting" nillable="true" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:schema>
And now we are getting to the good part, i.e the actual WSDL.
The first part is to import the header schema, like below:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:import namespace="https://www.wm-explorer.com/ContractFirst/Header" schemaLocation="WmExplorerHeader.xsd"/> </xsd:schema>
Next point is to add the request and response parts:
<wsdl:message name="RequestHeader"> <wsdl:part name="parametersReqHeader" element="hdr:ContractFirstRequestHeader"/> </wsdl:message> <wsdl:message name="ResponseHeader"> <wsdl:part name="parametersRespHeader" element="hdr:ContractFirstResponseHeader"/> </wsdl:message>
The last step is to add the header information also to the Bindings section:
<wsdl:binding name="WmExplorerGreeting" type="tns:WmExplorerGreeting"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="sayHello"> <soap:operation soapAction="https://www.wm-explorer.com/ContractFirst/Greeting/sayHello"/> <wsdl:input> <soap:body use="literal"/> <soap:header use="literal" part="parametersReqHeader" message="tns:RequestHeader"/> </wsdl:input> <wsdl:output> <soap:body use="literal"/> <soap:header use="literal" part="parametersRespHeader" message="tns:ResponseHeader"/> </wsdl:output> </wsdl:operation> </wsdl:binding>
And voila, you have now a WSDL with your headers defined in it. If you create a WSP based on this file you will see that the headers are automatically created.
Of course, you will find all the resources mentioned in this post in the Download Area, at the very end of the post.
The handlers are created similarly as in the Code First approach. You will have to use the new document types, however:
Fire up a soapUI project and give it a shot. It should output something like below:
Now that we have also a Contract First example under our belts, let go further to another section. Do not really know how to name this section so I will just name it:
What I forgot to say in the previous post
Registering the handler
I have mentioned in the previous post that the registration of the handler does not survive a server restart.
The recommendation was to create a startup service to register the handler by means of the pub.soap.handler:registerWmProvider service.
If you have reloaded the package and checked afterward the IS logs, you might have seen:
2018-01-11 12:18:02 EET [ISS.0028.0013E] Startup service exited with the following error: com.wm.app.b2b.server.ServiceException: [ISS.0088.9423] Service handler contractFirstHandler is already registered 2018-01-11 12:18:02 EET [ISC.0088.0028E] Service handler contractFirstHandler could not be registered. A service handler by that name already exists 2018-01-11 12:18:02 EET [ISS.0028.0012I] WmExplorerContractFirstWS: Startup service (com.explorer.wm.contract.first.init:initHandler) 2018-01-11 12:18:02 EET [ISS.0028.0005I] Loading WmExplorerContractFirstWS package
Whenever the package is reloaded this error will appear because the handler is already registered.
A more solid approach to registering the handlers would be the following:
- get all registered provider handlers using the service pub.soap.handler:listProvider (pub.soap.handler:listConsumer for consumer handlers)
- iterate over the result and check if the handler that you want to register already exists
- if it exists and has the same properties (request service, response service, etc.) do nothing; it means that no changes were made to the provider handler
- if it exists and the properties differ, unregister the handler using pub.soap.handler:unregisterProvider (pub.soap.handler:unregisterConsumer for consumer handlers) and register it again with the new properties using pub.soap.handler:registerProvider (pub.soap.handler:registerConsumer for consumer handlers)
- if it does not exist register it using the above-mentioned services
QNames comes from Qualified Names and it is a document structure that contains the namespace name and a local name. It can be associated with a handler at registration time and can provide some benefits like:
- mustUnderstand attribute checking at runtime
- straightforward and easy manipulation of handlers in the Designer for both providers and consumers
- automatic adding of handlers when the provider is created if a header with a matching name is detected
If you went step by step and created the WSP as suggested by me, for sure you got a warning message like the one below:
When you register the handler use the following QName:
- namespaceName: https://www.wm-explorer.com/ContractFirst/Header
- localName: ContractFirstRequestHeader (ContractFirstResponseHeader for the response handler)
and the warning message will disappear and you can reap the benefits I mentioned above.
Besides the services that I mentioned throughout these 2 posts, that are mostly used for handler registration/deregistration, and the ones that I used in the code, there are some others that you might find useful.
Mostly used this service for debugging purposes. In case you do not know exactly the QNames of the header(s), this service helps you find them out.
Call it from the request handler and it will output valuable information on how you can structure the IS document used for the header extraction.
Another service I used for debugging purposes. I used to put this in the handler service to see exactly how the SOAP message looks like.
This service determines if the SOAP message has a fault block. Can be used in the response handlers if you want to have a conditional processing based on the success/failure of the top-level service.
If you want to remove a header this is the way to go. Useful in cases when you want to replace a header.
I strongly recommend that you check the services from the pub.soap.handler folder in the WmPublic package. There are a lot of nice services there that you can use for your web service handlers.
Bypass header usage?
Do you think that header usage is too much of a hassle? Want something easier, simpler and more straightforward?
You can have that.
In your WSP, in the Properties panel on the right-hand side set the property “Pipeline Headers Enabled” to true.
This way the header document will be available in the top-level service, and you can process it there.
I am against this approach because it brakes the Separation of Concerns best practice and you end up having in your top-level service code that has nothing to do with the processing of the actual message.
So my dear webMethods explorers, this post concludes the discussions on “Web Services: Headers and Handlers”.
I hope that you find the information useful and helpful. If you have any comments do not hesitate to drop me a line below.
Until next time, happy exploring!