You have now seen and deployed all of the code for both the sender and receiver parts of the JAXM message echo example. If, however, you were to start your web browser and point it at http://localhost:8080/SOAPRPSender/request, which is the URL that causes the sender servlet to transmit a message, you would find that after about 30 seconds, the sender would give up waiting for a reply from the receiver and an error page would be displayed by the browser. Although all of the code is in place, the proper JAXM configuration has not been set up to allow the providers to exchange messages. In this section, we look at how to configure the JAXM reference implementation.
A message traveling from the sending servlet to the receiver has to make three hops:
From the sender to the local provider
From the local provider to the remote provider
From the remote provider to the receiving servlet
We saw earlier that a JAXM client logically connects to its local provider using the ProviderConnectionFactory createConnection( ) method, but we didn't see how the provider itself is located. This information is held in a file called client.xml, which must be located in the CLASSPATH of the JAXM client. Since both the sender and the receiver servlets in this example are deployed as web applications, their client.xml files should be placed in the WEB-INF/classes directory of their WAR files, as shown in the following listing of the files that make up the web archive for the SOAPRPSender servlet:
META-INF/MANIFEST.MF
WEB-INF/classes/ora/jwsnut/chapter4/soaprpsender/SOAPRPSenderServlet.class
WEB-INF/classes/client.xml
WEB-INF/web.xml
A full description of the client.xml file will be found in Chapter 8. The content of the client.xml file used by the SOAPRPSender servlet is shown in Example 4-6, in which the line numbers on the left have been added for ease of reference only.
1 <?xml version="1.0" encoding="ISO-8859-1"?> 2 3 <!DOCTYPE ClientConfig 4 PUBLIC "-//Sun Microsystems, Inc.//DTD JAXM Client//EN" 5 "http://java.sun.com/xml/dtds/jaxm_client_1_0.dtd"> 6 <ClientConfig> 7 <Endpoint> 8 urn:SOAPRPSender 9 </Endpoint> 10 <CallbackURL> 11 http://localhost:8080/SOAPRPSender/message 12 </CallbackURL> 13 14 <Provider> 15 <URI>http://java.sun.com/xml/jaxm/provider</URI> 16 <URL>http://localhost:8081/jaxm-provider/sender</URL> 17 </Provider> 18 </ClientConfig>
The lines shown in bold relate to the configuration of the sending servlet; the other lines are fixed content that are the same in all client.xml files. The Provider element at the end of the file is used when the client connects to the messaging provider. The two child elements are used as follows:
The URI value identifies the provider in use. For the JAXM reference implementation, you must use the value http://java.sun.com/xml/jaxm/provider. The JAXM code that implements the ProviderConnection interface and the provider itself communicate by adding private header entries to the messages sent by JAXM clients. This URI is used as the namespace for the XML elements in these header entries; it is also used to set their actor attribute. When the provider receives a message from a JAXM client, it removes and actions all headers for which the actor attribute has this fixed value.
The URL is where messages from the JAXM client to the provider are actually sent. For the reference implementation in the JWSDP, the provider is a Tomcat service called jaxm-provider, accessible at port 8081. The provider is not required to be on the same host as the JAXM client. If the provider is not co-located with the client, then the name of the provider's host should be used instead of localhost.
Figure 4-5 shows how the URL field of the Provider element is used to locate the JAXM client's local provider.
When a provider receives a message for delivery to a client, it needs to be able to match the destination address of the message to the client that provides service at that address. As we saw earlier, the destination address that is placed in a SOAP-RP header is a URI that identifies the target of the message � it need not be a URL. Therefore, the provider maintains a list of mappings from the Endpoint URI to the URL of the client that should receive messages destined for that URI. In the client.xml file, the Endpoint element declares the URI that corresponds to the client, and the CallbackURL element specifies the URL to which messages for that URI should be delivered. In terms of the example that we are using in this chapter, the sending servlet advertises its URI as urn:SOAPRPSender. Since the sending servlet expects to receive messages on the URL http://localhost:8080/SOAPRPSender/message, this is the URL to which the sending servlet's URI should be mapped.[4] Hence, the appropriate Endpoint and CallbackURL entries in the client.xml file for the sending servlet would be:
[4] This URL comes from the sending servlet's web.xml file, which was shown in Example 4-2.
<Endpoint> urn:SOAPRPSender </Endpoint> <CallbackURL> http://localhost:8080/SOAPRPSender/message </CallbackURL>
In the case of the receiving servlet (which as a JAXM client also requires its own client.xml file), these entries would look like this:
<Endpoint> urn:SOAPRPEcho </Endpoint> <CallbackURL> http://localhost:8080/SOAPRPEcho/message </CallbackURL>
The receiving servlet also needs a Provider element containing the URL of its local provider that, if you deploy both the sending and receiving servlets on the same host, is the same provider used by the sending servlet and therefore requires the same URL.
The client.xml file solves the problem of how to route messages between clients and a provider, but there remains the issue of how the providers route messages among themselves. In the case of the example used in this chapter, the provider needs to deliver messages addressed to the URIs urn:SOAPRPEcho and urn:SOAPRPSender, by passing them to whichever provider the clients owning these endpoints are connected.[5] In order to make this possible, providers are configured with URI-to-URL mappings that are similar to those created by the Endpoint and CallbackURL elements used in the client.xml file. Each provider must be configured with a mapping for each remote URI to which messages from its local clients might be addressed, specifying the URL of the provider to which messages carrying that URI as a destination address must be delivered (and not the URL of the receiving client).
[5] In general, when there are two JAXM clients on separate machines, there are two providers involved. However, if both the sender and receiver are deployed on the same machine, the likelihood is that they will use the same provider (although you could arrange to run two providers on the same machine). Even though this is the case, the configuration still has to be created in the same way as if there were two providers. The description here is consistent with that.
In the reference implementation, these mappings are stored in a file called provider.xml, which resides in the /WEB_INF directory of the jaxm-provider service, details of which you'll find in Chapter 8. Fortunately, you don't need to deal with this file directly � instead, you can view and change the mappings using the JAXM provider administration service, which can be accessed using a web browser.
The provider administration tool is a web application that provides a user interface that lets you configure the JAXM provider without having to manually edit its provider.xml file. Once you understand the content of this file (details of which are provided in Chapter 8), you'll find it very easy to use the administration tool, so we're not going to describe it in great detail. Here, we need to use it to add endpoint mappings for the URIs urn:SOAPRPEcho and urn:SOAPRPSender. Assuming that this service is running on the same host as your browser, the URL that you need to use to access it is http://localhost:8081/jaxm-provideradmin. When you attempt to connect to this service, you are prompted to supply a username and password. When you installed the JWSDP, you were prompted to supply a username and a password, and you should use the same username and password to access the configuration service. If you can't remember them, you can find them in the tomcat-users.xml file, which is held in the conf directory of the web server. Here is what this file typically looks like, with the important lines highlighted in bold:
<?xml version='1.0'?>
<tomcat-users>
<role rolename="admin"/>
<role rolename="manager"/>
<role rolename="provider"/>
<user username="JWSUserName" password="JWSPassword"
roles="admin,manager,provider"/>
</tomcat-users>
In this case, supply the username JWSUserName and the password JWSPassword. These values can also be found in the jwsnutExamples.properties file in your home directory, assuming you created it as described in Chapter 1.
Once you reach the configuration service's home page, expand the tree view that you'll see on the left, and select the entry for http below the SOAPRP profile. You should see a screen like that shown in Figure 4-6.
This screen contains, among other things, the endpoint mappings for messages being sent by the provider for the SOAP-RP profile using HTTP as the underlying communications protocol. The URL associated with the URI urn:SOAPRPEcho needs to be the one required to access the provider to which the receiving servlet is attached, whereas the URL for the URI urn:SOAPRPSender should be that of the provider for the sending servlet. A provider has three available URLs; for the case of the JWSDP reference implementation running in the Tomcat web server, these URLs are listed in Table 4-2, where it is assumed that the provider and the clients are all running on the same machine.
URL |
Description |
---|---|
http://localhost:8081/jaxm-provider/sender |
Address to which JAXM clients send outgoing messages. This URL is configured in the client's client.xml file and is not part of the provider configuration. |
http://localhost:8081/jaxm-provider/receiver/soaprp |
Address to which messages using the SOAP-RP profile should be sent. |
http://localhost:8081/jaxm-provider/receiver/ebxml |
Address to which messages using the ebXML profile should be sent. |
If the target provider is on a different machine, substitute the hostname of that machine for localhost in these URLs.
Since the messages in the example used in this chapter use the SOAP-RP profile, both of the JAXM client URIs should be mapped to the URL for the SOAP-RP receiving URL of the target provider, which will be http://localhost:8081/jaxm-provider/receiver/soaprp. To add these mappings, select "Create New Endpoint Mapping" from the combo box at the top right of the screen. You are presented with a form that allows you to enter a URI along with its corresponding URL, as shown in Figure 4-7, where the mapping for urn:SOAPRPEcho has been entered.
The mappings that you need to enter are shown in Table 4-3.
URI |
Target URL |
---|---|
urn:SOAPEcho |
http://localhost:8081/jaxm-provider/receiver/soaprp |
urn:SOAPSender |
http://localhost:8081/jaxm-provider/receiver/soaprp |
Once both mappings are set up, they should appear on the main screen as shown in Figure 4-8. Select "Save to Profile" to save these mappings.
At this point, the provider is properly configured to forward messages to either URI. Of course, if the clients are on separate machines and use different providers, it is then necessary to configure each provider separately:
The provider local to the sending servlet is configured with a mapping for the URI urn:SOAPRPEcho � that is, the URI to which it sends. The URL for this mapping refers to the other provider.
The provider local to the receiving servlet similarly requires a mapping for the URI urn:SOAPRPSender.
You can now finally run the example that we have been using throughout this chapter. To do so, simply enter the URL http://localhost:8080/SOAPRPSender/request into your browser. After a short delay, you should see the SOAP message that was sent by the sending servlet and returned by the receiver, an example of which is shown in Example 4-7. This message has been reformatted for the sake of readability.
<?xml version="1.0" encoding="UTF-8"?> <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"> <soap-env:Header> <m:path xmlns:m="http://schemas.xmlsoap.org/rp"> <m:from>urn:SOAPRPEcho</m:from> <m:to>urn:SOAPRPSender</m:to> <m:id>9a85b633-2c8f-4d2e-84a5-ff6b21c05f61</m:id> <m:relatesTo>3166c06a-e38c-466e-b43e-d55a37f3d3fc</m:relatesTo> <m:fwd/> <m:rev/> </m:path> </soap-env:Header> <soap-env:Body> <tns:Sent xmlns:tns="urn:SOAPRPSender">This is the content</tns:Sent> </soap-env:Body> <tns:Date xmlns:tns="urn:SOAPRPEcho">Thu Aug 08 15:58:53 BST 2002</tns:Date> </soap-env:Envelope>
The elements in the message header are defined by the SOAP-RP protocol, further information on which can be found later in this chapter. Note the to and from elements, which contain the URIs for the sending and receiving servlets, and the Date element, which follows the SOAP body and contains the date and time at which the message was processed by the receiver.
As a summary of how messaging providers use the JAXM configuration information, the following is a step-by-step account of the way in which a SOAP-RP message is sent from a JAXM client to its destination. The return path would obviously be identical, but with the addresses reversed.
The receiving servlet initializes. As it does so, it uses the ProviderConnectionFactory and the ProviderConnection interface to establish a connection to its local provider, as well as calls the ProviderConnection getMetaData( ) method. In order to contact the provider to obtain the metadata, the JAXM code in the client accesses the receiving servlet's client.xml file to locate the provider's URL from the Provider element � in this case, http://localhost:8081/jaxm-provider/sender. It also passes to the provider the information in the Endpoint and CallbackURL elements so that the provider knows that messages intended for the URI urn:SOAPRPEcho should be delivered to the URL http://localhost:8080/SOAPRPEcho/message.
The sending servlet uses the ProviderConnectionFactory and the ProviderConnection interface to establish a connection to its local provider. It also obtains a MessageFactory for the soaprp profile and constructs a message, setting the from address to urn:SOAPRPSender and the to address to urn:SOAPRPEcho.
The client uses the ProviderConnection send( ) method to transmit the message. The JAXM code in the client accesses the sending servlet's client.xml file and uses the URI and URL in the Provider element to find the URL of the provider � in this case, http://localhost:8081/jaxm-provider/sender. Also, if it has not already done so, it passes to the provider the information in the Endpoint and CallbackURL elements so that it can map the sending servlet's URI (urn:SOAPRPSender) to its message callback URL (http://localhost:8080/SOAPRPSender/message). The message is then delivered to the provider at the URL http://localhost:8081/jaxm-provider/sender.
When the provider receives the message, it stores it in its outgoing message queue. There is a separate set of message queues for each profile that the provider supports, which the reference implementation keeps in a directory hierarchy in temporary storage provided by its host container. If you are running the JWSDP in the Tomcat web server, you'll find the messages that the provider sends and receives held below the directory work\Services Engine\jwsdp-services\jaxm-provider, relative to the JWSDP installation directory.
When the message is to be transmitted from the outgoing message queue, the provider extracts its destination address. In order to do this, the provider needs to understand where it will find this address, which is profile-dependent. The provider can do this because the class com.sun.xml.messaging.soaprp.SOAPRPMessageImpl that represents a SOAP-RP message is derived from com.sun.xml.messaging.jaxm.util.ProfileMessage (which has abstract methods that extract to to and from addresses from the message). SOAPRPMessageImpl implements these methods so that they extract the correct parts of the SOAP-RP header. The message class for the ebXML profile similarly implements them to extract the Party object from the message (see Section 4.6 later in this chapter, for further information on this). The fact that the provider has to be able to get the destination address from within a SOAP message explains why nonprofiled messages that do not contain a destination address (i.e., SAAJ messages created using the default MessageFactory) cannot be sent using a provider.
The provider uses the destination address to check its URI-to-URL mapping, set up using the JAXM provider administration tool, to find the URL of the provider to which the message should be sent. In this case, the destination address is urn:SOAPRPEcho, which maps to the URL http://localhost:8081/jaxm-provider/receiver/soaprp. This happens to be a URL belonging to the same provider, of course, but this does not matter. The local provider delivers the message to its peer using an HTTP POST request to this URL. If delivery fails, the provider retries on the assumption that the remote provider is not yet started or there is a problem with network connectivity.
When the peer provider receives the incoming SOAP-RP message, it stores it in its received message queue. Subsequently, an attempt is made to deliver this message to the correct JAXM client. Delivery is performed by extracting the destination address from the message, in the same way as the sending provider did when transmitting the message, and using it to access the Endpoint mapping table built from the client.xml files of the clients connected to the provider (see Figure 4-5). Here, the destination address urn:SOAPRPEcho has been registered by the receiving servlet and mapped to its delivery URL http://localhost:8080/SOAPRPEcho/message (see Step 1). The provider delivers the message using an HTTP POST request to that URL. If delivery fails, or if there is no entry for the destination URI in the provider's mapping table, the provider will retry delivery later, on the assumption that the client has not yet been started but will register later.
The final point to mention in our discussion of JAXM configuration discusses the reason for including the load-on-startup element in the web.xml file of the receiving servlet in our example so that it is loaded when the web container initializes. As we said earlier in Section 4.4.1, a provider uses the Endpoint elements from the client.xml files of the JAXM clients that are connected to it to determine where to route the messages it receives. A provider cannot directly read these files � instead, they are read and a representation of their content is passed (in a private SOAP message header) when a client connects to the provider.[6] The sending servlet, which is not marked to be loaded at startup, initializes and connects to the provider when the request from the browser sent to the URL http://localhost:8080/SOAPRPSender/request is received by the web container; therefore, it is registered with the provider before a message addressed to it needs to be dispatched. However, since the receiving servlet's URL is only referenced when the provider tries to deliver a message to it based on an entry in the provider's client URI-to-URL mapping table, if the receiving servlet were not marked to be loaded at startup, it would not have initialized and connected to the provider, and therefore its URL would not be registered in this mapping table.
[6] Exactly when this happens is, of course, implementation-dependent. At the time of this writing, the reference implementation does this the first time the client requests ProviderMetaData, or when the ProviderConnection send() method is called for the first time. The fact that this is left so late also explains why a client that simply listens passively for messages, such as the receiving servlet in the example in this chapter, must call getMetaData( ) as shown in Example 4-4, even though it doesn't make use of the ProviderMetaData. The purpose of the call is simply to register the receiver's Endpoint with the provider so that it can receive messages.