|
|
Contents
- Prerequisites
- Need for Dynamic Discovery in SOA
- Introducing WS-Discovery
- WS-Discovery Support in WSO2 Carbon
- Implementing an Inventory Management System - Mission Briefing
- Iteration 1 - Running the Pilot Project
- Iteration 2 - Discovery Enabled Inventory Management System
- Iteration 3 - Going Public
- Room for Improvement
- Summary and Conclusion
Prerequisites
This tutorial contains a number of sample services and applications. If you wish to try them out, you will need following software. Please have them all installed on a single machine.- JDK 1.6 (version 1.6.0_23 recommended)
- WSO2 Data Services Server 2.6.0 or higher
- WSO2 Application Server 4.1.0 or higher
- WSO2 Enterprise Service Bus 4.0.0 or higher
- WSO2 Governance Registry 4.0.0 or higher
- MySQL
- MySQL JDBC driver 5.1 or higher (version 5.1.8 recommended)
Need for Dynamic Discovery in SOA
Services are the fundamental building blocks of service oriented architecture. They are reusable, functional entities that can be consumed by both human users and applications. In addition to the services, a typical SOA deployment may also consist of an enterprise service bus, a registry/repository, a business process engine and various other tools for master data management, identity management and data visualization. Despite all the benefits offered by SOA, connecting all these diverse components together is never an easy task. The developers should be aware of the endpoints of all the services and applications in order to be able to piece them together. Needless to mention when there are dozens of services to be interconnected this becomes a cumbersome task as it requires the developers to keep track of many host names, IP addresses, port numbers and URLs. To make things worse sometimes different services are managed by different teams, departments or organizations. In such situations, service endpoints may change time to time without prior notice thus breaking other services and applications that depend on them. Such practical problems make it absolutely necessary to have a mechanism in place for discovering the target services at runtime.Another problem that many SOA developers and architects face is moving applications across different environments. Most SOA development teams have to deal with at least three different environments, namely development, QA and production. When an application is moved from one environment to another all the components with address URLs embedded within them would break. A similar problem arises when building a service oriented architecture in a dynamic environment such as an enterprise cloud. In cloud deployments it is almost impossible to know the exact hosts and ports on which a particular service will be deployed as they change rather frequently. Therefore some mechanism for dynamically discovering the services is required. With dynamic discovery capabilities, developers no longer have to hard code URLs and endpoints into the services and configuration artifacts they develop. Rather they would configure the services and artifacts with some generic, well known keywords and identifiers and let the applications resolve them to the actual endpoints at runtime. This way applications will continue to work even if a particular URL is changed while the system is running.
Introducing WS-Discovery
WS-Discovery is a standard protocol for dynamically discovering service endpoints. Using this protocol, service provider applications (target services) can advertise their endpoints to interested parties. On the other hand service consumer applications (clients) can use WS-Discovery to find the required endpoints by either listening for the notifications published by the target services or querying a central repository that contains endpoint details. WS-Discovery standard has two versions as of 2011 with the version 1.1 published in 2009 being the latest. This version of the protocol defines two main operational modes.- Ad hoc mode
- Managed mode
WS-Discovery also allows room for a hybrid operational mode where ad hoc mode and managed mode can coexist side by side. In such an environment, target services will multicast the endpoint update notifications as if they were running in ad hoc discovery mode. A discovery proxy will receive these notifications and maintain a directory of endpoints. Clients can then either query the discovery proxy or directly receive the endpoint updates from the services over multicast. The protocol also allows room for clients to seamlessly switch between ad hoc mode and managed mode.
WS-Discovery Concepts
WS-Discovery protocol associates a unique identifier with each service. This is referred to as the stable identifier of the service as it does not change over time or along with other variable parameters of the service. This ID can be used across the SOA deployment to uniquely identify a particular service instance. WS-Discovery may also associate a given service with a set of scopes, types and transport addresses (endpoints). A scope is a user defined category label (a tag) attached to the service. Any valid URI can be used as a scope. A service may belong to zero, one or more scopes as configured by the user. Scopes are used mainly to group and categorize services so they can be indexed and searched. Types are also used to further categorize and search for services. Any qualified XML name (QName) can be used as a type. In most scenarios it makes sense to treat the WSDL port types of a service as its WS-Discovery types. Transport addresses represent the endpoint references over which the service can be reached. A service may have zero or more transport addresses depending on its availability level. A service which does not have any transport addresses is considered to be down or unavailable.When WS-Discovery is used to publish and discover endpoints, target services should always publish the endpoints along with their stable ID values, scopes and types. Clients can find the necessary service endpoints if they know the exact ID of the service they wish to communicate with. If not they can perform a search by scopes or types to find a service ID that matches with the provided service metadata. Once a matching service ID is found, more look ups can be performed to resolve the service ID into a set of endpoints.
WS-Discovery Messages
WS-Discovery protocol defines the messages that need to be exchanged in order to successfully publish or discover service endpoints. Altogether there are six types of messages defined in the standard.- Hello messages
- Bye messages
- Probe messages
- Probe match messages
- Resolve messages
- Resolve match messages
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing"> <wsa:To>http://localhost:8080/services/DiscoveryProxy</wsa:To> <wsa:ReplyTo> <wsa:Address>http://www.w3.org/2005/08/addressing/none</wsa:Address> </wsa:ReplyTo> <wsa:MessageID>urn:uuid:d5c85841-abff-45d1-85ee-933cb824b2d5</wsa:MessageID> <wsa:Action>http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/Hello</wsa:Action> </soapenv:Header> <soapenv:Body> <wsd:Hello xmlns:wsd="http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01"> <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing"> <wsa:Address>urn:uuid:50bccd63-8c1f-4f9d-a0db-8b0f290306e3</wsa:Address> </wsa:EndpointReference> <wsd:Types xmlns:axis2ns2="http://echo.services.core.carbon.wso2.org">axis2ns2:echoPortType</wsd:Types> <wsd:Scopes>http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/DefaultScope</wsd:Scopes> <wsd:XAddrs>https://localhost:8243/services/echo http://localhost:8280/services/echo</wsd:XAddrs> <wsd:MetadataVersion>1</wsd:MetadataVersion> </wsd:Hello> </soapenv:Body> </soapenv:Envelope>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing"> <wsa:To>http://localhost:8080/services/DiscoveryProxy</wsa:To> <wsa:ReplyTo> <wsa:Address>http://www.w3.org/2005/08/addressing/none</wsa:Address> </wsa:ReplyTo> <wsa:MessageID>urn:uuid:98861fea-da54-47c3-8253-802f29419d51</wsa:MessageID> <wsa:Action>http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/Bye</wsa:Action> </soapenv:Header> <soapenv:Body> <wsd:Bye xmlns:wsd="http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01"> <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing"> <wsa:Address>urn:uuid:50bccd63-8c1f-4f9d-a0db-8b0f290306e3</wsa:Address> </wsa:EndpointReference> <wsd:Types xmlns:axis2ns6="http://echo.services.core.carbon.wso2.org">axis2ns6:echoPortType</wsd:Types> <wsd:Scopes>http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/DefaultScope</wsd:Scopes> <wsd:XAddrs>https://localhost:8243/services/echo http://localhost:8280/services/echo</wsd:XAddrs> <wsd:MetadataVersion>1</wsd:MetadataVersion> </wsd:Bye> </soapenv:Body> </soapenv:Envelope>Probe messages are sent by clients in managed mode to query a discovery proxy. A probe may contain the scopes and types for which a matching service ID is required. Discovery proxy should respond to a Probe request by sending a Probe Match message. A Probe Match will contain a list of services that match the search criterion specified in the Probe. If no matching services can be found, discovery proxy should send back an empty Probe Match. Clients running in the ad hoc mode may send Probe messages over multicast. In this case Probe messages are sent out as in-only requests. Any target services or discovery proxies listening for such multicast probes may respond to the client with a Probe Match over a unicast channel.
Clients running in the managed mode can send Resolve messages to the discovery proxy to resolve a service ID into one or more endpoints. Resolve messages contain the service ID which needs to be translated. Discovery proxy should respond with a Resolve Match message with a list of endpoints upon receiving a Resolve message. If a service does not exist by the specified ID, an empty Resolve Match should be returned. Similar to the case of Probe messages, clients running in ad hoc mode may multicast Resolve messages.
WS-Discovery Support in WSO2 Carbon
WSO2 Carbon comes with a complete implementation of WS-Discovery 1.1 managed mode. Using this feature, service hosting products like Application Server, Data Services Server and Mashup Server can publish service endpoints to a user defined WS-Discovery proxy. Message routing and brokering products like WSO2 ESB can use the discovery protocol to find the service endpoints in the network and create proxy services and mediation sequences on top of them. WSO2 Governance Registry ships with the basic WS-Discovery proxy components and hence can be used as a discovery proxy. Also WS-Discovery libraries of Carbon can be used in other Java applications to construct WS-Discovery aware tools.WS-Discovery support of Carbon is implemented as a set of OSGi bundles:
- org.wso2.carbon.discovery.core - Contains the core WS-Discovery message definitions, management utilities, service observers and notification system
- org.wso2.carbon.discovery.proxy - Contains the WS-Discovery proxy implementation
- org.wso2.carbon.discovery.ui - UI components for connecting to a remote discovery proxy and browsing/searching the discovered services
- org.wso2.carbon.discovery.admin.ui - UI components for enabling and disabling WS-Discovery service notifications
- org.wso2.carbon.discovery.mediation.ext - Contains a WS-Discovery based mediation registry extension for the ESB
- org.wso2.carbon.discovery.module - A WS-Discovery based Axis2 client module
The org.wso2.carbon.discovery.mediation.ext component can be installed on the ESB through the Carbon Feature Manager to support proxy services with dynamically discovered endpoints (this is discussed in detail towards the latter part of the article). The org.wso2.carbon.discovery.admin.ui can be installed on any service hosting product based on Carbon. This component will become a part of the standard product distributions in the near future. It is also part of the WSO2 Stratos based cloud platform. The org.wso2.carbon.discovery.module is currently not used in any Carbon based WSO2 product. It is intended to be used in client side applications as an Axis2 client extension.
Note that even though these components are shipped only with certain WSO2 products, there is nothing preventing a user from deploying these bundles in other Carbon based products and using them. Thanks to the highly modular and componentized architecture provided by Carbon, a user can simply use the Carbon Feature Manager UI/API to get them installed to any Carbon server. All these components and associated features are available in the WSO2 P2 feature repository.
The rest of this article describes how to build a loosely coupled system based on SOA using the WS-Discovery support provided by WSO2 Carbon. We will use WSO2 Application Server for hosting the core business services of the system. WSO2 Governance Registry will be used as the central discovery proxy of the overall architecture. We will also use WSO2 ESB as the integration framework which enables consumers to communicate with the business services. WS-Discovery protocol will be used to discover all the available web services, data services and proxy services in the system.
Implementing an Inventory Management System - Mission Briefing
We are going to create a simple inventory management system for "Trader Paul's", your friendly neighborhood department store. Users will interact with the system through a web interface to add items, browse catalogs and modify items available in the inventory. Most of the core system features will be implemented as web services so they can be easily consumed by other applications of the store. Some of the services will also be exposed to the public Internet over a secure channel so that customers, partners and suppliers can integrate their own applications with the "Trader Paul's" inventory manager.The goal of this effort is to see how WS-Discovery can be used to construct complex distributed systems. Therefore we will not pay too much attention to proper management information system design. We will also keep concerns such as identity management aside for the moment and keep the system absolutely simple.
It's clear that we are going to need some kind of a persistent store for this system. We will be using a RDBMS such as MySQL for this purpose. WSO2 Data Services Server will be used to service enable the data. The web front end will be developed as a Java Web application and deployed on WSO2 Application Server along with any other Java Web Services we may have to develop. We can use WSO2 ESB to expose services to the Internet securely. The centerpiece of the solution is going to be WSO2 Governance Registry which will be used by the ESB and other client applications to discover the necessary service endpoints.
A Word on Documentation Conventions
The rest of this article describes a sequence of steps that you can carry out using a set of WSO2 products. Most of these action items involve signing into the web based management console of a WSO2 server and performing certain tasks. The menu of the WSO2 management console consists of four tabs:- Main
- Configure
- Monitor
- Tools
Also the management console contains menus and submenus. Following notation is used to refer various options available in these menus.
Manage > Web Services > List (Select the 'List' option under 'Manage' menu and 'Web Services' submenu)
Manage > Applications > Add (Select the 'Add' option under 'Manage' menu and 'Applications' submenu)
Iteration 1 - Running the Pilot Project
A possible first cut version of the overall solution is available in the solution1.zip archive. This solution is made of following components:- A relational database based on MySQL
- A single data service (InventoryDataService) which enables applications to access the database
- A couple of Java web services (ItemKeyGenerator and PriceFinder) that play supporting roles in the overall business logic
- The web front end implemented as a Java webapp
Here are the steps to deploy the initial solution on a single box:
1. Install WSO2 Data Services Server (DSS) by extracting the downloaded archive.
2. Install WSO2 Application Server (AS) by extracting the downloaded archive.
3. Copy the MySQL JDBC driver to the repository/components/lib directory of DSS.
4. Open the repository/conf/carbon.xml file of the Application Server and set the port offset to '1'. This is required to avoid any port conflicts between the DSS and AS.
<Ports> <Offset>1</Offset> ... </Ports>5. Launch both servers. Go to the bin directory of each server and execute the relevant start up script.
Unix/Linux: sh wso2server.sh Windows: wso2server.bat6. Login to MySQL server and create a new database named 'inventory'. If needed create a separate user account in MySQL and grant the necessary permissions so the user can read and write to the database.
mysql> create database inventory;7. Download and extract the solution1.zip file
8. Run the SQL script file named inventory.sql against the inventory database. That will create a set of tables and populate them with some test data.
9. Open the InventoryDataService.dbs file in a text or XML editor. Locate and modify the database connection settings section so that it points to the inventory database you just created. You will have to modify the JDBC connection URL and the user credentials.
<config id="mysql"> <property name="org.wso2.ws.dataservice.driver">com.mysql.jdbc.Driver</property> <property name="org.wso2.ws.dataservice.protocol">jdbc:mysql://localhost:3306/inventory</property> <property name="org.wso2.ws.dataservice.user">root</property> <property name="org.wso2.ws.dataservice.password">password</property> </config>10. Sign in to the management console of DSS (https://localhost:9443/carbon). Click on 'Manage > Web Services > Add > Data Service > Upload'. Browse for the modified InventoryDataService.dbs file and upload it. The InventoryData service will get deployed on the server in a few seconds. Click on the 'Manage > Web Services > List' option and verify the service has been deployed successfully.
11. Sign in to the management console of AS (https://localhost:9444/carbon). Click on 'Manage > Applications > Add'. Browse for the TraderPaulsIMS-1.0.0.car file and upload it. It will take a few seconds for the services to get deployed. Once done, two new web services will be listed under 'Manage > Web Services > List'. The services are called ItemKeyGenerator and PriceFinder. In addition, a new web application named inventory will be listed under 'Manage > Web Applications > List'.
[2011-08-28 11:30:04,759] INFO {org.wso2.carbon.application.deployer.internal.ApplicationManager} - Successfully Deployed Carbon Application : TraderPaulsIMS {super-tenant} [2011-08-28 11:30:14,125] INFO {org.wso2.carbon.core.deployment.DeploymentInterceptor} - Deploying Axis2 service: PriceFinder {super-tenant} [2011-08-28 11:30:14,815] INFO {org.apache.axis2.deployment.DeploymentEngine} - Deploying Web service: PriceFinder.aar - file:/home/hiranya/Desktop/discovery/wso2as-4.1.0/repository/deployment/server/axis2services/PriceFinder.aar [2011-08-28 11:30:14,982] INFO {org.wso2.carbon.core.deployment.DeploymentInterceptor} - Deploying Axis2 service: ItemKeyGenerator {super-tenant} [2011-08-28 11:30:15,232] INFO {org.apache.axis2.deployment.DeploymentEngine} - Deploying Web service: ItemKeyGenerator.aar - file:/home/hiranya/Desktop/discovery/wso2as-4.1.0/repository/deployment/server/axis2services/ItemKeyGenerator.aar [2011-08-28 11:30:17,782] INFO {org.wso2.carbon.server.TomcatGenericWebappsDeployer} - Deployed webapp: StandardEngine[Tomcat].StandardHost[defaulthost].StandardContext[/inventory].File[/home/hiranya/Desktop/discovery/wso2as-4.1.0/repository/deployment/server/webapps/inventory.war]Now we are all set for a pilot run of the Trader Paul's inventory management system. Launch a web browser and enter the URL http://localhost:9764/inventory. That should take you to the home page of the inventory manager which looks similar to the following:
Click on some of the available options and play around with it. Add a couple of items to the inventory and make sure everything works fine.
Most of the time the web application communicates with the InventoryData service. The webapp contacts the ItemKeyGenerator when adding a new item to the inventory. This service is responsible for generating a unique key for the item being inserted. PriceFinder service is invoked when you try to view the details of an item. The marked price value and the discount rate is calculated by the PriceFinder.
Planning for Inevitable Change
So far so good. We have the inventory management system up and running. What's even more impressive is that we have implemented the entire system using web services. As a result, the overall solution architecture is quite flexible and the individual components (services, data access and webapp) are not tightly coupled with each other. Moving to SOA seems to be the best thing Trader Paul's IT department has ever done. But then comes the inevitable change.Let's assume the Trader Paul's system administrators wanted to do a major rearrangement of their network infrastructure. As a result of this, the InventoryData service was moved to a new server which has a different host name and a port. How will the web application cope up with this change?
To see what will happen simply shutdown the data services server. Open up its repository/conf/carbon.xml file and change the port offset value to 2.
<Ports> <Offset>2</Offset> ... </Ports>Basically we are incrementing the port number of DSS by 2. So the data service which was previously exposed on port 9763 will now be exposed on port 9765. Start the DSS instance with the above change and now try to browse the inventory web application. You should be able to access the home page without any issues, but pretty much everything else will fail with an error page like the following.
An exception similar to the following will be logged on the Application Server logs.
[2011-08-28 11:14:54,651] INFO {org.apache.axis2.transport.http.HTTPSender} - Unable to sendViaPost to url[http://localhost:9763/services/InventoryData] java.net.ConnectException: Connection refused at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333) at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195) at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366) at java.net.Socket.connect(Socket.java:525) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.commons.httpclient.protocol.ReflectionSocketFactory.createSocket(ReflectionSocketFactory.java:140) at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:125) at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)Ouch! The inventory manager is not so loosely coupled after all. The webapp has assumed that the service endpoints will not change over time. While the system is impervious to any changes in the actual service implementations, it is vulnerable to changes in service endpoints. The root cause of the problem is the following 3 classes of the web application:
- InventoryDataClient
- ItemKeyGeneratorClient
- PriceFinderClient
public InventoryDataClient() throws InventoryException { try { stub = new InventoryDataStub("http://localhost:9763/services/InventoryData"); } catch (AxisFault axisFault) { handleException("Error while initializing the data service stub", axisFault); } }So this means, every time the endpoint of a service changes, we have to modify the source code of the web application, recompile and redeploy it. Needless to say this is a not an acceptable solution since it requires a lot of manual intervention and can cause lot of unnecessary system downtime. So what choices do we have? How can we make the system impervious to service endpoint changes? Here's a couple of common solutions developers tend to implement when faced with a problem of this nature.
- Specify service endpoints as init parameters of the web applications
- Read service endpoints dynamically from a custom configuration file
This is where WS-Discovery comes into the picture. WS-Discovery provides a fully automated, fully dynamic approach for managing and discovering service endpoints. It doesn't require any manual intervention at all and as a developer or a system administrator you only need to worry about the URL of the WS-Discovery proxy.
Now let's try to redo the existing inventory management solution by introducing WS-Discovery.
Iteration 2 - Discovery Enabled Inventory Management System
WS-Discovery enabled services and the web application can be found in solution2.zip archive. Let's deploy this new version of the inventory management system and see what are the changes we have made to support WS-Discovery and how adding dynamic discovery support is going to help us. We are not quite read to being ESB into the picture yet. But in this iteration we are going to setup WSO2 Governance Registry which will act as our discovery proxy.As described in a previous section, WS-Discovery enabled systems identify services based on their scopes and types. So the first thing we have to do is decide on the appropriate scopes for our three web services. A scope is nothing more than a label that developer decides to stick on a service. Later we can use these labels to index and search the services. The listing below shows a possible scope allocation for the web services used in the inventory manager.
- InventoryData - http://ims.traderpauls.com/data/main
- ItemKeyGenerator - http://ims.traderpauls.com/data/keygen
- PriceFinder - http://ims.traderpauls.com/sales/pricing
In WSO2 Carbon, the scope of a service is defined by adding a parameter named 'wsDiscoveryParams' to the service artifact. This can be done via the management console after a service has been deployed. Alternatively this can be mentioned in the service meta descriptor file (services.xml file) at development time. The service artifacts found in the solution2.zip archive are already updated with the above parameter. Other than that no changes have been made to the service implementations.
Follow these steps carefully to deploy the new version of the inventory manager. This assumes you have the first cut version of the system already deployed on Application Server and Data Services Server.
1. To start with, login to the management console of Application Server and delete the old TraderPaulsIMS Carbon application we deployed earlier. This can be done by navigating to 'Manage > Applications > List' and clicking on the 'Delete' option next to the application name. It will remove the ItemKeyGenerator, PriceFinder and the inventory webapp from the system.
2. Shutdown the Application Server. We will restart this server shortly with some minor configuration changes.
3. Shutdown the Data Services Server. Do not remove the InventoryData service from the server. We can use it later as it is.
4. Install WSO2 Governance Registry (GReg)
5. Open the repository/conf/carbon.xml file of GReg and set the port offset to 10 (We don't want GReg port number to be anywhere near the other servers)
<Ports> <Offset>10</Offset> ... </Ports>6. Start WSO2 GReg by executing the relevant start up script in the bin directory.
7. Open the repository/conf/axis2.xml file of the Data Services Server and add the following parameter. There are already some parameters defined in the axis2.xml. You can add this one somewhere around the same region.
<parameter name="DiscoveryProxy">http://localhost:9773/services/DiscoveryProxy</parameter>This parameter tells the server that a discovery proxy is available in the above URL to which service metadata can be published by sending discovery notifications (HELLO/BYE messages).
8. Open the repository/conf/axis2.xml file of the Application Server and add the same 'DiscoveryProxy' parameter we added to DSS.
9. Download and extract the solution2.zip archive.
10. Copy the InventoryData_services.xml file to the repository/deployment/server/dataservices directory of DSS.
11. Launch Data Services Server and Application Server.
12. Login to the Application Server and upload the TraderPaulsIMS-2.0.0.car file as a Carbon application (Manage > Applications > Add). This will redeploy the ItemKeyGenerator, PriceFinder and the inventory webapp.
We have finished deploying the new version of the solution. At first glimpse it looks like a long process. But if you carefully study the steps we followed, you will realize that we have done only four changes to the setup we already had.
- Installing WSO2 GReg
- Adding the 'DiscoveryProxy' parameter to the axis2.xml file of AS and DSS
- Adding the wsDiscoveryParams parameter to all the services that need to be discovered
- Deploying a new and improved inventory webapp
If you get a page similar to this then rest assured that all the services have been published to the discovery proxy. You see more than three entries here because AS and DSS publish all the services deployed on them. That includes some of the sample services such as Hello service and Version service.
So far everything is good. Now it's time to check up on the new version of the web application we just deployed. Navigate to the home page of the inventory manager and click on a few links to see whether everything is working fine as before. If all is well, shutdown the Data Services Server. Open its repository/conf/carbon.xml file and change the port offset value to something like 5. (Avoid the values 1 and 10 because they are already used by AS and GReg)
<Ports> <Offset>5</Offset> ... </Ports>Now restart the Data Services Server and go back to the web application. Click on a few links. Unlike the previous solution, the new system will deal with the endpoint change gracefully. The web application will automatically discovery and bind with the new endpoint reference of the InventoryData service. The user (you) will not see any errors nor notice any differences in the UI.
Reviewing the Code
So it seems we finally have a truly loosely coupled system at hand. Let's go through the changes we have done to make this possible and try to understand how the new system works.First of all we added a parameter named DiscoveryProxy to the axis2.xml file of AS and DSS.
<parameter name="DiscoveryProxy">http://localhost:9773/services/DiscoveryProxy</parameter>This parameter is configured with a URL which points to the discovery proxy service that runs on GReg (this service is available on GReg by default). The parameter tells the servers that a discovery proxy is available to which service metadata can be published. When configured with this parameter, AS and DSS send notifications to the discovery proxy whenever a service is deployed, updated or undeployed. The same parameter can be used in several other WSO2 products like ESB and Mashup Server to trigger discovery notifications.
Then we explicitly defined a scope for each of the services by setting a service level parameter named 'wsDiscoveryParams'.
<parameter name="wsDiscoveryParams"> <Scopes>http://ims.traderpauls.com/data/keygen</Scopes> </parameter>When DSS or AS fires discovery notifications for a service, it looks for this parameter to find the set of scopes to which the service belongs. This piece of information will be included in the HELLO notifications sent to GReg where it is stored as a resource.
The last important change we did was the following utility method we added to the webapp. The InventoryDataClient, ItemKeyGeneratorClient and PriceFinderClient no longer have hard coded endpoints within them. Instead they all invoke this method to find the service endpoints. This method executes a WS-Discovery probe on GReg to retrieve the most up-to-date endpoint details.
public static String findEndpoint(String scope) throws DiscoveryException { // Create a DiscoveryClient instance InventoryConfigHolder configHolder = InventoryConfigHolder.getInstance(); DiscoveryClient client = new DiscoveryClient(configHolder.getClientConfigurationContext(), configHolder.getDiscoveryProxyURL()); // Execute a probe URI[] scopes = new URI[] { URI.create(scope) }; TargetService[] services = client.probe(null, scopes); if (services != null && services.length > 0) { URI[] endpoints = services[0].getXAddresses(); // The service may have multiple endpoints // We pick the HTTP endpoint for (URI endpoint : endpoints) { if (InventoryConstants.HTTP_PROTOCOL.equals(endpoint.getScheme())) { return endpoint.toString(); } } } throw new DiscoveryException("Unable to locate a service for the scope: " + scope); }Note that the method accepts a scope value as an argument. This is passed in by each of the client implementations. The scope string is included in the probe request sent to GReg which performs a search based on that. It will return all the services that match the given scope. Since we have a one-to-one mapping between scopes and services, each scope will bring back exactly one service. Each service in the Probe Match may have more than one endpoint. So we simply pick the http endpoint and ignore the rest.
The end result of this exercise is that we no longer have to be concerned about the changes that may occur in data service or business service endpoints. Given that there can be hundreds of services in a complex system, it indeed is a relief. We only need to worry about the changes that may occur in the discovery proxy endpoint. In this example implementation we're reading the discovery proxy URL as an init-parameter of the webapp.
Iteration 3 - Going Public
Time to expose some of our services to the public Internet. But we don't want external users to directly hit our service interfaces. And we definitely don't want to expose all our services and operations to external parties. Therefore let's define the scope of this development iteration as follows.- Expose the PriceFinder service and all the read operations of the InventoryData service to the Internet.
- All interfaces exposed to the Internet must be secured with UsernameToken security.
To start with we are going to install WSO2 ESB and create a proxy service for the PriceFinder service. We will use WS-Discovery to find the endpoint of the PriceFinder.
1. Install WSO2 ESB by extracting the zip file.
2. Open the repository/conf/carbon.xml file of the ESB and set the port offset to 20.
<Ports> <Offset>20</Offset> ... </Ports>3. Open the repository/conf/axis2.xml file and add the DiscoveryProxy parameter so that any services we create in the ESB will be discovered by GReg.
<parameter name="DiscoveryProxy">http://localhost:9773/services/DiscoveryProxy</parameter>4. Start WSO2 ESB by executing the start up script in the bin directory and sign in to the ESB management console (https://localhost:9463/carbon).
5. Download the TraderPaulsPublicIMS-1.0.0.car file and upload it to the ESB by using the 'Manage > Applications > Add' option. This will upload two WSDL files named PriceFinder.wsdl and InventoryData.wsdl to the embedded registry of the ESB. Files will be uploaded to the /_system/config/tutorial collection. These files contain the interfaces that we intend to expose to the Internet from the ESB. The PriceFinder.wsdl is the WSDL of the PriceFinder service without any modifications. The InventoryData.wsdl is the WSDL of the InventoryData service with all the write operations taken out.
6. Select the 'Configure' tab and click on the 'Configure > WS-Discovery' option in the menu. You will be taken to the WS-Discovery Control Panel.
7. Click on the 'Add Discovery Proxy' option. The 'Configure WS-Discovery Proxy' page will be displayed.
8. Enter any name for the discovery proxy (eg: GRegOnLocalhost) and enter the URL http://localhost:9773/services/DiscoveryProxy. Click 'Save' to apply the settings. You will be taken back to the WS-Discovery Control Panel which will list the discovery proxy you just configured. If GReg is already running, server status will be shown as On-line.
9. Click on the 'View' option under the available Actions for the discovery proxy. A list of all discovered services will be displayed.
10. You can use the search box at the top to execute probes on the discovery proxy and reduce the number of services listed. Try searching for following scopes.
- http://testme.org - Doesn't bring back any results
- http://ims.traderpauls.com - Returns all 3 services used by the inventory manager
- http://ims.traderpauls.com/data - Returns the InventoryData service and the ItemKeyGenerator
- http://ims.traderpauls.com/sales - Returns the PriceFinder service
11. Once you have located the PriceFinder service on the list, click on its UUID. You will be taken to a page where you can see more specific data related to the PriceFinder and its endpoints.
12. Expand the 'Create Proxy Services' panel. Enter the name PriceFinder for the proxy and select the HTTP endpoint of the service as the Target Address. Leave the 'Dynamic Address' box unchecked for the moment and click 'Save' to create the proxy service.
13. Select the Main tab and go to the 'Manage > Web Services > List'. You should see the PriceFinder proxy already listed there. Also click on 'Manage > Service Bus > Source View'. You should be able to see the actual configuration of the PriceFinder service as follows.
<proxy name="PriceFinder" startOnLoad="true" trace="disable"> <target> <endpoint> <address uri="http://127.0.0.1:9764/services/PriceFinder"/> </endpoint> <outSequence> <send/> </outSequence> </target> </proxy>This still has the PriceFinder endpoint hard coded. We will fix that later. For now let's continue by making a few adjustments to the proxy service. We should secure the proxy service and link it with the PriceFinder.wsdl we have uploaded to the registry.
14. Go to 'Manage > Web Services > List' and click on the PriceFinder service. You will be taken to the Service Dashboard.
15. Click on 'Security' under the 'Quality of Service Configuration'.
16. Select 'Yes' for 'Enable Security?' option.
17. Select 'UsernameToken' from the list of choices and click 'Next'.
18. Select the admin role from the list of roles. This means only the users in admin role can invoke the service. Click 'Finish' to apply security.
19. Finally go to 'Manage > Service Bus > Source View'. Add a publishWSDL element and the wsDiscoveryParams parameter to the proxy service. Modify the proxy configuration as follows in the source view and click 'Update'.
<proxy name="PriceFinder" startOnLoad="true" trace="disable"> <target> <endpoint> <address uri="http://127.0.0.1:9764/services/PriceFinder"/> </endpoint> <outSequence> <send/> </outSequence> </target> <policy key="conf:/repository/axis2/service-groups/PriceFinder/services/PriceFinder/policies/UTOverTransport"/> <enableSec/> <publishWSDL key="conf:tutorial/PriceFinder.wsdl"/> <parameter name="wsDiscoveryParams"> <Scopes>http://public.ims.traderpauls.com/sales/pricing</Scopes> </parameter> </proxy>We are done configuring the PriceFinder proxy service with security. In the process we also specified a scope for the service. Head over to the WS-Discovery Control Panel in the 'Configure' tab and search for the scope http://public.ims.traderpauls.com/sales/pricing. The PriceFinder proxy will be listed as the only hit which means the proxy service has been successfully discovered by GReg. Note that the service has only a HTTPS endpoint. When we applied UsernameToken security on the proxy service, the ESB disabled the HTTP endpoint of the service. ESB does not expose UsernameToken secured services over HTTP.
This is another opportunity to experiment with the hierarchical search capability of the discovery proxy. Perform a search for the scope http://ims.traderpauls.com. This will return all the services deployed in DSS and AS. Now perform a search for the scope http://public.ims.traderpauls.com. This will return the PriceFinder proxy we just created in the ESB. Note how the search results vary depending on the search string we provide. By specifying proper scopes for the services we can improve our capacity to manage and look up services in a WS-Discovery enabled environment.
By following a similar process as above we can create a proxy service for the InventoryData service. That is left as an exercise to the reader. You will end up with a proxy configuration similar to the following.
<proxy name="InventoryData" startOnLoad="true" trace="disable">
<target>
<endpoint>
<address uri="http://127.0.0.1:9768/services/InventoryData"/>
</endpoint>
<outSequence>
<send/>
</outSequence>
</target>
<policy key="conf:/repository/axis2/service-groups/PriceFinder/services/PriceFinder/policies/UTOverTransport"/>
<enableSec/>
<publishWSDL key="conf:tutorial/InventoryData.wsdl"/>
<parameter name="wsDiscoveryParams">
<Scopes>http://public.ims.traderpauls.com/data/main</Scopes>
</parameter>
</proxy>
Getting Rid of Hard Coded Addresses
One of the limitations in the proxy services we created above is that they have hard coded address endpoints. Therefore if the back end service endpoints change, the proxy services will not work. Ideally the proxy services should use WS-Discovery to dynamically look up the service endpoints. This can be achieved by installing an additional plug-in called WS-Discovery Mediation Extensions Kit. Follow these steps to install the plug-in and configure the proxy services with dynamic endpoint look up capability.1. Sign in to the ESB management console.
2. Select the 'Configure' tab and click on 'Configure > Features'. You will be taken to the Carbon Feature Manager which consists of multiple horizontal tabs.
3. Select the 'Repository Management' horizontal tab and click on 'Add Repository'.
4. Enter any name for the repository (eg: WSO2MainRepo) and provide the URL http://dist.wso2.org/p2/carbon/releases/3.2.0. Then click the Add button to apply the settings.
5. Now select the Available Features tab and click on Find Features (note that the repository you created is already selected). ESB will connect to the plug-in repository and retrieve a list of all available features which will be displayed on the management console.
6. Scroll down to locate the WS-Discovery Mediation Extensions plug-in. Check the box next to it and click Install. This will start the plug-in installation wizard.
7. Simply proceed through the wizard by clicking the Next button. Accept the license agreement when prompted. This will start the plug-in installation. This could take a few minutes (depending on the speed of your network connection).
8. Once the installation is complete, you will be asked to restart the server. Click on the Finish button and simply logout from the management console. Now restart the ESB.
9. Now the required plug-in is installed. Sign in to the ESB management console again. Select 'Manage > Service Bus > Source View'.
10. Notice the mediation registry declaration at the top of the configuration. We should add a couple of new parameters to this configuration before we can setup proxy services with dynamic endpoint look up. The modified registry declaration should look like this.
<registry provider="org.wso2.carbon.mediation.registry.WSO2Registry"> <parameter name="cachableDuration">15000</parameter> <parameter name="extensions">org.wso2.carbon.discovery.mediation.ext.WSDiscoveryRegistryExtension</parameter> <parameter name="discoveryProxy">http://localhost:9773/services/DiscoveryProxy</parameter> </registry>11. Now we are all set to create proxy services with dynamic endpoint look up feature. Select the Configure tab and click on WS-Discovery. Search for the PriceFinder service and click on the service ID in the result list.
12. Click to expand the Create Proxy Services panel. Enter DynamicPriceFinder as the service name and check the Dynamic Address check box. Select the HTTP endpoint as the Target Address and click Save to create the proxy service. What we did differently here was checking the Dynamic Address check box.
13. Now select the Main tab and click on Source View. You will see a proxy configuration similar to the following.
<proxy name="DynamicPriceFinder" startOnLoad="true" trace="disable"> <target endpoint="wsdd://urn:uuid:23a79ddc-f524-421e-9770-32931d381c82/http"> <outSequence> <send/> </outSequence> </target> </proxy>Note that it does not contain any hard coded URLs. Instead it has the target service ID prefixed by wsdd:// as the endpoint. This will be used by the ESB mediation registry to execute a WS-Discovery resolve operation. Also note that the endpoint contains the suffix /http. This is because we selected the HTTP endpoint when creating the proxy service. This tells the ESB to use the HTTP endpoint if the resolve operation returns more than one endpoint.
14. Let's add a WSDL and a discovery scope for this proxy service (I'm not going describe how to secure a service again. But if needed it can be done by following the same exact steps we went through earlier). Modify the configuration as follows and click 'Update' on 'Manage > Service Bus > Source View'.
<proxy name="DynamicPriceFinder" startOnLoad="true" trace="disable"> <target endpoint="wsdd://urn:uuid:23a79ddc-f524-421e-9770-32931d381c82/http"> <outSequence> <send/> </outSequence> </target> <publishWSDL key="conf:tutotial/PriceFinder.wsdl"/> <parameter name="wsDiscoveryParams"> <Scopes>http://public.ims.traderpauls.com/sales/dynamic_pricing</Scopes> </parameter> </proxy>Now you can try this out using a client tool such as SOAP UI. The WSDL of the proxy service can be found at http://localhost:8280/services/DynamicPriceFinder?wsdl. Send a few messages and make sure it works. Restart the Application Server on a different port and see if the proxy service continues to respond as expected. Please note that whenever the ESB performs a WS-Discovery resolve, it caches the results for a certain period. This is done in order to reduce the number of WS-Discovery look ups performed, thereby improving the response time of the calls. By default the cache duration is set to 15 seconds. This is configured by the following parameter in the mediation registry declaration.
<parameter name="cachableDuration">15000</parameter>Therefore it will take about 15 seconds for the endpoint changes to be reflected on the proxy service. In production deployments a higher value can be set for the cachableDuration parameter depending on the frequency of the endpoint changes.
Room for Improvement
We have reached the end of our little inventory manager project. We have all the pieces of the system implemented as web services and all of them are dynamically wired using the WS-Discovery protocol. An architecture such as this is highly flexible and virtually invulnerable to any kind of change in the individual components. However that doesn't mean it's perfect for all situations. There are still certain limitations and plenty of room for improvement.1. Single point of failure at GReg -
All the components in the system rely on WSO2 GReg to discover the service endpoints at runtime. Therefore a failure in GReg would cause all other components to fail. We can avoid this by implementing a high availability plan for GReg. WSO2 GReg supports clustering. So we can run multiple instances of GReg pointed to the same database (database should also be high available).
In a clustered GReg setup there will be multiple discovery proxy endpoints. So we will have to implement some load balancing or fail over routing on the endpoints. WSO2 ESB can be used to implement such requirements. Individual components of the system will use the ESB as the discovery proxy which will simply forward the messages to an actual discovery proxy endpoint on one of the available GReg instances.
2. Performance hit -
In the final version of the inventory manager, every time the webapp need to invoke a back end service, it has to actually perform two service calls. The first call is against the discovery proxy to find the service endpoint. The second call is against the actual service endpoint. This in turns increases the response time of the system. If GReg takes time to respond to the discovery probes, the webapp user will get a sluggish UI experience.
To avoid this we need to make sure GReg is easily accessible from the Application Server. It would be a good idea to maintain a fast network connection between AS and GReg. Also if GReg is getting a lot of discovery messages, it would be a good idea to run several instances of GReg with some load balancing mechanism. Also at application level we can improve performance by caching discovery probe results for a certain period of time. WSO2 ESB already does this when wiring to back end services using WS-Discovery.
Summary and Conclusion
That brings us to the end of this introductory tutorial on WS-Discovery. We started by looking at why endpoint discovery is important for SOA and enterprise application development. We studied some of the issues present in applications that do not have dynamic endpoint discovery capabilities. After that we went through the basics of WS-Discovery protocol and explored its operational modes, message types and message flows.Then we switched over to exploring the WS-Discovery features available in the WSO2 Carbon platform. We created a simple inventory management system using WSO2 Application Server and WSO2 Data Services Server. We practically experienced the problems that can manifest within a system which does not have endpoint discovery capabilities. Then we fixed all those issues by introducing WS-Discovery into our solution. We used WSO2 Governance Registry as a discovery proxy and configured all the services and the web application to use the discovery protocol to publish and discover service endpoints.
Finally we looked at some of the discovery capabilities available in WSO2 ESB. We learned how to use the WS-Discovery control panel of the ESB to connect to a remote discovery proxy and browse discovered services and endpoints. We also learned how easy it is to create proxy services using a discovered service endpoint. We also looked at how to install the WS-Discovery mediation extensions kit on the ESB and how to use that plug-in to construct smart proxy services with 100% dynamic endpoint discovery capabilities.
As of now WSO2 Carbon only supports the managed mode of WS-Discovery. But please note that we are currently working on implementing the ad hoc mode of WS-Discovery as well. That will enable Carbon based servers to publish and discover services using a UDP multicast based communication protocol.
Compared to most WS-* standards, WS-Discovery is a very simple protocol. Anybody can learn the protocol end-to-end by reading the protocol specification once. But as we witnessed in our examples, it is a very powerful standard and if applied properly, can solve a plethora of endpoint management problems in the enterprise. What’s really impressive is that this protocol can enable different SOA stacks to work together seamlessly. For an example of this please refer the article titled “Integrating Mule with WSO2 Registry Using WS-Discovery” by Jos Dirksen.
If you are wondering who are the other vendors that support WS-Discovery, please be aware that Microsoft already has included WS-Discovery support in their .NET 4.0platform.
I hope you found the content in this article useful. Please do try out the samples as described to get the full experience of WSO2 Carbon and WS-Discovery. Please feel free to use our discovery libraries in your own applications and send in your valuable feedback to our mailing lists.
Missing file
Thanks for the valuable article and examples. Bringing together various components of WSO2 is particulary useful.
In trying to work through the example I ran into a couple of missing files:
inventory.sql
TraderPaulsPublicIMS-1.0.0.car
Can you please provide these files?
Will this example work in a StratosLive environment? Some of the configuration steps seem to be a little different, especially the setup for the WS-Discovery Mediation Extensions in the ESB.
Thanks,
Cliff
Running the Sample in StratosLive
You should be able to run most of this sample in StratosLive. In fact I'm currently working on porting this sample to StratosLive environment. Certain parameters like the discovery proxy URLs etc need to be changed for that to work. Also in StratosLive the user doesn't have access to configuration files like axis2.xml. Therefore users should use the provided UI components to get WS-Discovery configured in the cloud. For an example StratosLive AppServer has a UI component called "Service Discovery" which will allow the user to specify a discovery proxy URL and enable service information publishing. Other than a few minor changes like that, the sample should work in the cloud without any issues.
I'd also like to point out that the standard shopping cart sample available on StratosLive is also using WS-Discovery underneath to discover certain service endpoints.
WS-Discovery mediation extensions are currently not available in StratosLive. We are planning to add this feature to the cloud deployment in the future.
Thanks,
Hiranya
Missing Files Uploaded
You are a life saver :) Thanks for pointing out the missing files. I have updated the solution1.zip with the inventory.sql file. You will also find a file named inventory_with_data.sql in the same zip file which is basically the same SQL script with some sample data (this contains some images so might not be able to open it in a text editor). TraderPaulsPublicIMS-1.0.0.car file has also been uploaded to the SVN and the reference in the article is now pointing to this artifact.
Here are the direct links to the files for your convenience:
solution1.zip - https://svn.wso2.org/repos/wso2/people/hiranya/wsd-tutorial/solution1.zip
TraderPaulsPublicIMS-1.0.0.cat - https://svn.wso2.org/repos/wso2/people/hiranya/wsd-tutorial/TraderPaulsPublicIMS-1.0.0.car
Thanks,
Hiranya
Hi Hiranya, Thanks for your
Thanks for your reply regarding porting this example to StratosLive, I look forward to it.
I was able to download TraderPaulsPublicIMS-1.0.0.car as well as the new solution1.zip. However, I don't see the *.sql files in the new solution1.zip.
Thanks,
Cliff
Direct Links to SQL Files
I just downloaded the solution1.zip and double checked. The *.sql files are available in the archive. If you want to verify the zip file you have downloaded, the md5sum of the solution1.zip should be 8cee64bf0f3e4d5e989f1588da2b510b.
Anyway here are some direct links to the sql files:
https://svn.wso2.org/repos/wso2/people/hiranya/wsd-tutorial/inventory.sql
https://svn.wso2.org/repos/wso2/people/hiranya/wsd-tutorial/inventory_with_data.sql
Hope this helps.
Thanks,
Hiranya