Acquired and called directly
Automatic Failover
No Persistence
Direct call - Request/Response
Acquired and called through the MessageHelper
Automatic Failover
Message Persistence
KSB Exception Messaging
Callback Mechanisms
In the examples below, notice that the client code is unaware of the protocol with which the underlying service is deployed. Given a connector for a given protocol and a compatible service definition, you could move a service to different protocols as access needs change without affecting dependent client code.
The easiest way to call a service is to grab it and invoke it directly. This uses a direct request/response pattern and what you see is what you get. You will wait for the processing the call takes on the other side plus the cost of the remote connection time. Any exceptions thrown will come across the wire in a protocol-acceptable way.
This code acquires a SOAP-based service and calls it:
QName serviceName = new QName("testNameSpace", "soap-repeatTopic"); SOAPService soapService = (SOAPService) GlobalResourceLoader.getService(serviceName); soapService.doTheThing("hello");
The SOAPService interface needs to be in the client classpath and bindable to the WSDL. The easiest way to achieve this in Java is to create a bean that is exported as a SOAP service. This is the server-side service declaration in a Spring file:
<bean class=" org.kuali.rice.ksb.messaging.SOAPServiceDefinition"> <property name="service"> <ref bean="soapService" /> </property> <property name="localServiceName" value="soap-repeatTopic" /> <property name="serviceNameSpaceURI" value="testNameSpace" /> <property name="priority" value="3" /> <property name="retryAttempts" value="1" /> <property name="busSecurity" value="false" /> </bean>
This declaration exposes the bean soapService on the bus as a SOAP available service. The Web Service Definition Language is available at the serviceServletUrl + serviceNameSpaceURI + localServiceName + ?wsdl.
This next code snippet acquires and calls a Java base service:
EchoService echoService = (EchoService)GlobalResourceLoader.getService(new QName("TestCl1", "echoService")); String echoValue = "echoValue"; String result = echoService.echo(echoValue);
Again, the interface is all that is required to make the call. This is the server-side service declaration that deploys a bean using Spring’s HttpInvoker as the underlying transport:
<bean class="org.kuali.rice.ksb.messaging.JavaServiceDefinition "> <property name="service" ref="echoService" /> <property name="serviceInterface" value=" org.kuali.rice.ksb.messaging.remotedservices. EchoService " /> <property name="localServiceName" value="soap-echoService" /> <property name="busSecurity" value="false" /> </bean>
Below is a description of each property on the ServiceDefinition (JavaServiceDefinition and SOAPServiceDefinition):
Table 6.2. Properties of the ServiceDefinition
property | required | default | description |
---|---|---|---|
busSecurity | no | yes (JavaServiceDefinition), no (SOAPServiceDefinition) | For Java-based services, message is digitally signed before calling the service and verified at the node hosting the service. For SOAP services, WSS4J is used to digitally sign the SOAP request/response in accordance with the WS Security specification. More info on Bus Security here. |
localServiceName | yes | none | The local name of the QName that makes up the complete service name. |
messageExceptionHandler | no | DefaultMessageExceptionHandler | Name of the MessageExceptionHandler that is called when a service call fails. This is called after the retryAttempts or millisToLive policy of the service or Node has been met. |
millisToLive | no | none | Used instead of retryAttempts. Only considered in case of error when invoking service. Defines how long the message should continue to be tried before being put into KSB Exception Messaging. |
priority | no | 5 | Only applies when asynchronous messaging is enabled. The lower the priority is, the sooner the message will be executed. For example, if 100 priority 10 messages are waiting for invocation and a priority 5 message is sent, the priority 5 message will be executed first. |
queue | no | true | If true, the service will behave like a queue in that there is only one real service call when a message is sent. If false, the service will behave like a topic. All beans bound to the service name will be sent a message when a message is sent to the service. Use queues for operations you only want to happen once (for example, to route a document). Use topics for notifications across a cluster (for example, to invalidate cache entry). |
retryAttempts | no | 7 | Determines the number of times a service can be invoked before being put into KSB Exception Messaging (the error state) |
service | yes | none | The bean to be exposed for invocation on the bus |
serviceEndPoint | no | serviceServletUrl + serviceName | This can be explicitly set to create an alternate service end point, different from the one the bus automatically creates. |
serviceName | yes | serviceNameSpaceURI + localServiceName | If localServiceName and serviceNameSpaceURI are omitted, the QName of the service. This can be used instead of the localServiceName and serviceNameSpaceURI convenience methods. |
serviceNameSpaceURI | no | messageEntity property or message.entity config param is used | The namespaceURI of the QName that makes up the complete service name. If set to "" (blank string) the property is NOT included in the construction of the QName representing the service and the service name will just be the localServiceName with no namespace. |
To make a call to a service through messaging, acquire the service by its name using the MessageHelper:
QName serviceName = new QName("testAppsSharedQueue", "sharedQueue"); KEWSampleJavaService testJavaAsyncService = (KEWSampleJavaService) KSBServiceLocator.getMessageHelper().getServiceAsynchronously(serviceName);
At this point, the testJavaAsyncService can be called like a normal JavaBean:
testJavaAsyncService.invoke(new ClientAppServiceSharedPayloadObj("message content", false));
Because this is a queue, a single message is sent to one of the beans bound to the service name new QName("testAppsSharedQueue", "sharedQueue"). That 'message' is the call 'invoke' and it takes a ClientAppServiceSharedPayloadObj. Typically, messaging is done asynchronously. Messages are sent when the currently running JTA transaction is committed - that is, the messaging layer automatically synchronizes with the current transaction. So, using JTA, even though the above is coded in line with code, invocation is normally delayed until the transaction surrounding the logic at runtime is committed.
When not using JTA, the message is sent asynchronously (by a different thread of execution), but it's sent ASAP.
To review, the requirements to use a service that is exposed to the bus on a different machine are:
The service name
The interface to which to cast the returned service proxy object
The ExceptionMessageHandler required by the service in case invocation fails
Typically, service providers give clients a JAR with this content or organizations maintain a JAR with this content.
To complete the example: Below is the Spring configuration used to expose this service to the bus. This is taken from the file TestClient1SpringBeans.xml:
<!-- bean declaration --> <bean id="sharedQueue" class=" org.kuali.rice.ksb.testclient1.ClientApp1SharedQueue" /> <!-- RiceConfigurer Snippet --> <bean class=" org.kuali.rice.ksb.messaging.JavaServiceDefinition"> <property name="service" ref="sharedQueue" /> <property name="localServiceName" value="sharedQueue" /> <property name="serviceNameSpaceURI" value="testAppsSharedQueue" /> </bean> <... more .../>
This is located in the Spring file of the application exposing the service (in other words, the location in which the actual invocation will occur). The client does not need a Spring configuration to invoke the service.
There are two messaging call paradigms, called Topics and Queues. When any number of services is declared a Topic, then those services are invoked at least once or multiple times. If any number of services is declared a Queue, then one and only one service name will be invoked.
You can use Callback objects to get responses from service calls made using messaging. Acquiring a service for use with a Callback:
QName serviceName = new QName("TestCl1", "testXmlAsyncService"); SimpleCallback callback = new SimpleCallback(); KEWXMLService testXmlAsyncService = (KEWXMLService) KSBServiceLocator.getMessageHelper().getServiceAsynchronously(serviceName, callback); testXmlAsyncService.invoke("message content");
When the service is invoked asynchronously, the AsynchronousCallback object's (the SimpleCallback class above) callback method is called.
When message persistence is turned on, this object is serialized with any method call made through the messaging API. Take into consideration that this object (and the result of a method call) may survive machine restart and therefore it’s recommended that you NOT depend on certain transient in-memory resources.