(For more resources on topic_name, see here.)
The examples in this article are based on a standard ESB application template that can be found under the Chapter3 directory within the sample downloads..We will modify this template application as we proceed through this chapter.
Follow these steps:
<jbossesb parameterReloadSecs="5"
xsi_schemaLocation="http://anonsvn.labs.jboss.com/labs/
jbossesb/trunk/product/etc/schemas/xml/
jbossesb-1.3.0.xsd
http://anonsvn.jboss.org/repos/labs/
labs/jbossesb/trunk/product/etc/
schemas/xml/jbossesb-1.3.0.xsd">
<providers>
<jms-provider connection-factory="ConnectionFactory"
name="JBossMQ">
<jms-bus busid="chapter3GwChannel">
<jms-message-filter dest-name="queue/chapter3_Request_gw"
dest-type="QUEUE"/>
</jms-bus>
<jms-bus busid="chapter3EsbChannel">
<jms-message-filter dest-name="queue/chapter3_Request_esb"
dest-type="QUEUE"/>
</jms-bus>
</jms-provider>
</providers>
<services>
<service category="Chapter3Sample"
description="A template for Chapter3"
name="Chapter3Service">
<listeners>
<jms-listener busidref="chapter3GwChannel"
is-gateway="true"
name="Chapter3GwListener"/>
<jms-listener busidref="chapter3EsbChannel"
name="Chapter3Listener"/>
</listeners>
<actions mep="OneWay">
<action class="org.jboss.soa.esb.actions.SystemPrintln"
name="PrintBefore">
<property name="message"/>
<property name="printfull" value="true"/>
</action>
</actions>
</service>
</services>
</jbossesb>
A service is an implementation of a piece of business logic which exposes a well defined service contract to consumers. The service will provide an abstract service contract which describes the functionality exposed by the service and will exhibit the following characteristics:
Services which adhere to these criteria will be capable of evolving and scaling without affecting any consumers of that service. The consumer no longer cares which implementation of the service is being invoked, nor where it is located, provided that the exposed service contract remains compatible.
The structure of the message, and how it can be manipulated, plays an important part in any ESB application as a result of the message driven nature of the communication between service providers and consumers. The message is the envelope which contains all of the information relevant to a specific invocation of a service.
All messages within JBoss ESB are implementations of the org.jboss.soa.esb.message. Message interface, the major aspects of which are:
Let us execute the Chapter3 sample application that was opened up at the beginning of this chapter. Follow these steps:
The entire ESB message contents will be printed in the console as follows:
INFO [STDOUT] Message structure:
INFO [STDOUT] [ message: [ JBOSS_XML ]
header: [ To: JMSEpr [ PortReference < <wsa:Address
jms:localhost:1099#queue/chapter3_Request_esb/>,
<wsa:ReferenceProperties jbossesb:java.naming.factory.
initial : org.jnp.interfaces.NamingContextFactory/>,
<wsa:ReferenceProperties jbossesb:java.naming.provider.url :
localhost:1099/>, <wsa:ReferenceProperties jbossesb:java.naming.
factory.url.pkgs : org.jnp.interfaces/>, <wsa:ReferenceProperties
jbossesb:destination-type : queue/>, <wsa:ReferenceProperties
jbossesb:destination-name : queue/chapter3_Request_esb/>,
<wsa:ReferenceProperties jbossesb:specification-version :
1.1/>, <wsa:ReferenceProperties jbossesb:connection-factory :
ConnectionFactory/>, <wsa:ReferenceProperties jbossesb:persistent :
true/>, <wsa:ReferenceProperties jbossesb:acknowledge-mode : AUTO_
ACKNOWLEDGE/>, <wsa:ReferenceProperties jbossesb:transacted : false/>,
<wsa:ReferenceProperties jbossesb:type : urn:jboss/esb/epr/type/
jms/> > ] MessageID: e694a6a5-6a30-45bf-8f6d-f48363219ccf RelatesTo:
jms:correlationID#e694a6a5-6a30-45bf-8f6d-f48363219ccf ]
context: {}
body: [ objects: {org.jboss.soa.esb.message.defaultEntry=Chapter 3
says Hello!} ]
fault: [ ]
attachments: [ Named:{}, Unnamed:[] ]
properties: [ {org.jboss.soa.esb.message.transport.type=Deferred
serialized value: 12d16a5, org.jboss.soa.esb.message.byte.size=2757,
javax.jms.message.redelivered=false, org.jboss.soa.esb.gateway.
original.queue.name=Deferred serialized value: 129bebb, org.jboss.soa.
esb.message.source=Deferred serialized value: 1a8e795} ] ]
You have just created a Chapter3.esb file and deployed it to the ESB Runtime on the JBoss Application Server 5.1. You executed a gateway client that posted a string to the Bus. The server converted this message to an ESB message and the complete structure was printed out. Take a moment to examine the output and understand the various parts of the ESB message.
Step 1 through step 4 describe how to start the server and deploy our application from within JBoss Developer Studio. For the rest of this chapter, and throughout this book, you will be repeating these steps and will just be asked to deploy the application.
JBoss ESB provides two different implementations of the message interface, one which marshalls data into an XML format and a second which uses Java serialization to create a binary representation of the message. Both of these implementations will only handle Java serializable objects by default, however it is possible to extend the XML implementation to support additional object types.
Message implementations are created indirectly through the org.jboss.soa.esb. message.format.MessageFactory class.
In general any use of serializable objects can lead to a brittle application, one that is more tightly coupled between the message producer and consumer. The message implementations within JBoss ESB mitigate this by supporting a ‘Just In Time’ approach when accessing the data. Care must still be taken with what data is placed within the message, however serialization/marshalling of these objects will only occur as and when required.
Extending the ESB to provide alternative message implementations, and extending the current XML implementation to support additional types, is outside the scope of this book.
This is the section of the message which contains the main payload information for the message, adhering to the contract exposed by the service. The payload should only consist of the data required by the service contract and should not rely on any service implementation details as this will prevent the evolution or replacement of the service implementation at a future date.
The types of data contained within the body are restricted only by the requirements imposed by the message implementation, in other words the implementation must be able to serialize or marshall the contents as part of service invocation.
The body consists of
public Object get() ;
public void add(final Object value) ;
public Object get(final String name) ;
public void add(final String name, final Object value) ;
Let us create another action class that simply prints the message body. We will add this action to the sample application that was opened up at the beginning of this chapter.
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.message.Message;
protected ConfigTree _config;
public MyAction(ConfigTree config) {
_config = config;
}
public Message displayMessage(Message message) throws Exception {
System.out.println(
"&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
System.out.println("Body: " + message.getBody().get());
System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
return message;
}
12:19:32,562 INFO [STDOUT] &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
12:19:32,562 INFO [STDOUT] Body: Chapter 3 says Hello!
12:19:32,562 INFO [STDOUT] &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
You have just created your own action class that used the Message API to get the main payload of the message and printed it to the console.
Now add another miscellaneous SystemPrintln action after our BodyPrinter. Name it PrintAfter and make sure printfull is set to true. Modify the MyAction class and add additional named content using the getBody().add(name, object) method and see what gets printed on the console.
Here is the actions section of the config file
<actions mep="OneWay">
<action class="org.jboss.soa.esb.actions.SystemPrintln"
name="PrintBefore">
<property name="message"/>
<property name="printfull" value="true"/>
</action>
<action class="org.jboss.soa.esb.samples.chapter3.MyAction"
name="BodyPrinter" process="displayMessage"/>
<action class="org.jboss.soa.esb.actions.SystemPrintln"
name="PrintAfter">
<property name="message"/>
<property name="printfull" value="true"/>
</action>
</actions>
The following is the listing of the MyAction class’s modified displayMessage method
public Message displayMessage(Message message) throws Exception {
System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
System.out.println("Body: " + message.getBody().get());
message.getBody().add("Something", "Unknown");
System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
return message;
}
The message header contains the information relating to the identity, routing, and the correlation of messages. This information is based on, and shares much in common with, the concepts defined in the W3C WS-Addressing specification.
It is important to point out that many of these aspects are normally initialized automatically by other parts of the codebase; a solid understanding of these concepts will allow the developer to create composite services using more advanced topologies.
Every time a message is sent within the ESB it contains information which describes who sent the message, which service it should be routed to, and where any replies/faults should be sent once processing is complete. The creation of this information is the responsibility of the invoker and, once delivered, any changes made to this information, from within the target service, will be ignored by that service.
The information in the header takes the form of Endpoint References (EPRs) containing a representation of the service address, often transport specific, and extensions which can contain relevant contextual information for that endpoint. This information should be treated as opaque by all parties except the party which was responsible for creating it.
There are four EPRs included in the header, they are as follows:
When thinking about the routing information it is important to view these details from the perspective of the service consumer, as the EPRs represent the wishes of the consumer and must be adhered to. If the service implementation involves more advanced topologies, like chaining and continuations, which we will discuss later in the chapter, then care must be taken to preserve these EPRs when messages are propagated to subsequent services.
There are two parts of the header which are related to the identity of the message and its correlation with a preceding message. These are as follows:
The action header is an optional, service-specific URN that may be used to further refine the processing of the message by a service provider or service consumer. The URN should refer to an application-specific namespace.
There are no restrictions on how this header is to be used by the application including, if considered appropriate, ignoring its contents.
Let us modify MyAction to display some of the header information that we need:
public Message displayMessage(Message message) throws Exception {
System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
System.out.println("From: " +
message.getHeader().getCall().getFrom());
System.out.println("To: " +
message.getHeader().getCall().getTo());
System.out.println("MessageID: " +
message.getHeader().getCall().getMessageID());
System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
return message;
}
INFO [EsbDeployment] Stopping 'Chapter3.esb'
INFO [EsbDeployment] Destroying 'Chapter3.esb'
WARN [ServiceMessageCounterLifecycleResource] Calling cleanup on
existing service message counters for identity ID-7
INFO [QueueService] Queue[/queue/chapter3_Request_gw] stopped
INFO [QueueService] Queue[/queue/chapter3_Request_esb] stopped
INFO [QueueService] Queue[/queue/chapter3_Request_esb] started,
fullSize=200000, pageSize=2000, downCacheSize=2000
INFO [QueueService] Queue[/queue/chapter3_Request_gw] started,
fullSize=200000, pageSize=2000, downCacheSize=2000
INFO [EsbDeployment] Starting ESB Deployment 'Chapter3.esb'
INFO [STDOUT] &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
INFO [STDOUT] From: null
INFO [STDOUT] To: JMSEpr [ PortReference < <wsa:Address
jms:localhost:1099#queue/chapter3_Request_esb/>,
<wsa:ReferenceProperties jbossesb:java.naming.factory.
initial : org.jnp.interfaces.NamingContextFactory/>,
<wsa:ReferenceProperties jbossesb:java.naming.provider.url :
localhost:1099/>, <wsa:ReferenceProperties jbossesb:java.naming.
factory.url.pkgs : org.jnp.interfaces/>, <wsa:ReferenceProperties
jbossesb:destination-type : queue/>, <wsa:ReferenceProperties
jbossesb:destination-name : queue/chapter3_Request_esb/>,
<wsa:ReferenceProperties jbossesb:specification-version :
1.1/>, <wsa:ReferenceProperties jbossesb:connection-factory :
ConnectionFactory/>, <wsa:ReferenceProperties jbossesb:persistent
: true/>, <wsa:ReferenceProperties jbossesb:acknowledge-mode :
AUTO_ACKNOWLEDGE/>, <wsa:ReferenceProperties jbossesb:transacted :
false/>, <wsa:ReferenceProperties jbossesb:type : urn:jboss/esb/
epr/type/jms/> > ]
INFO [STDOUT] MessageID: 46e57744-d0ac-4f01-ad78-b1f15a3335d1
INFO [STDOUT] &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
We examined some of the header contents through the API. We printed the From, To, and the MessageID from within our MyAction class.
Now modify the MyAction class to print the Action, ReplyTo, RelatesTo, and FaultTo contents of the header to the console.
Here is the listing of the modified MyAction class’s method:
public Message displayMessage(Message message) throws Exception {
System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
System.out.println("From: " +
message.getHeader().getCall().getFrom());
System.out.println("To: " +
message.getHeader().getCall().getTo());
System.out.println("MessageID: " +
message.getHeader().getCall().getMessageID());
System.out.println("Action: " +
message.getHeader().getCall().getAction());
System.out.println("FaultTo: " +
message.getHeader().getCall().getFaultTo());
System.out.println("RelatesTo: " +
message.getHeader().getCall().getRelatesTo());
System.out.println("ReplyTo: " +
message.getHeader().getCall().getReplyTo());
System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
return message;
}
The message context is used to transport the active contextual information when the message is sent to the target service. This may include information such as the current security context, transactional information, or even context specific to the application. This contextual information is not considered to be part of the service contract and is assumed to change between successive message deliveries.
Where the message context really becomes important is when a service pipeline is invoked through an InVM transport, as this can allow the message to be passed by reference. When the transport passes the message to the target service it will create a copy of the message header and message context, allowing each to be updated in subsequent actions without affecting the invoked service.
Modify the MyAction class to print the context of the ESB message; obtain the context through the getContext() method. You will notice that the context is empty for our sample application as we currently have no security or transactional context attached to the message.
The message format within JBoss ESB allows the consumer and producer to use any payload that suits the purpose of the service contract. No constraints are placed on this payload other than the fact that it must be possible to marshall the payload contents so that the messages can be transported between the consumer and producer.
While this ability is useful for creating composite services, it can be a disadvantage when you need to design services that have an abstract contract, hide the details of the implementation, are loosely coupled, and can easily be reused. In order to encourage the loose coupling of services it is often advantageous to choose a payload that does not dictate implementation, for example XML.
JBoss ESB provides support for enforcing the structure of XML payloads for request and response messages, through the XML schema language as defined through the W3C. An XML Schema Document (XSD) is an abstract, structural definition which can be used to formally describe an XML message and guarantee that a specific payload matches that definition through a process called validation.
Enabling validation on a service is simply a matter of providing the schema associated with the request and/or response messages and specifying the validate attribute, as follows:
<actions inXsd="/request.xsd" outXsd="/response.xsd" validate="true">
...
</actions>
This will force the service pipeline to validate the request and response messages against the XSD files, if they are specified, with the request validation occurring before the first service action is executed and the response validation occurring immediately before the response message is sent to the consumer.
If validation of the request or response message does fail then a MessageValidationException fault will be raised and sent to the consumer using the normal fault processing as defined in the MEPs and responses section. This exception can also be seen by enabling DEBUG logging through the mechanism supported by the server.
Add a request.xsd or a response.xsd or both to your actions in the sample application provided. Enable validation and test the output.
JBoss ESB handles the majority of its configuration through a hierarchical structure similar to the W3C DOM, namely, org.jboss.soa.esb.helpers.ConfigTree. Each node within the structure contains a name, a reference to the parent node, a set of named attributes, and references to all child nodes.
This structure is used, directly and indirectly, within the implementation of the service pipeline and action processors, and will be required if you are intending to create your own action processors. The only exception to this is when using an annotated action class when the configuring of the action will be handled by the framework instead of programmatically.
The ConfigTree instance passed to an action processor is a hierarchical representation of the properties as defined within the action definition of the jboss-esb.xml file. Each property defined within an action may be interpreted as a name/value pair or as hierarchical content to be parsed by the action. For example the following:
<action ....>
<!-- name/value property -->
<property name="propertyName" value="propertyValue"/>
<!-- Hierarchical property -->
<property name="propertyName">
<hierarchicalProperty attr="value">
<inner name="myName" random="randomValue"/>
</hierarchicalProperty>
</property>
</action>
This will result in the following ConfigTree structure being passed to the action:
Traversing the hierarchy is simply a matter of using the following methods to obtain access to the parent or child nodes:
public ConfigTree getParent() ;
public ConfigTree[] getAllChildren() ;
public ConfigTree[] getChildren(String name) ;
public ConfigTree getFirstChild(String name) ;
Attributes are usually accessed by querying the current ConfigTree instance for the value associated with the required name, using the following methods:
public String getAttribute(String name) ;
public String getAttribute(String name, String defaultValue) ;
public long getLongAttribute(String name, long defaultValue) ;
public float getFloatAttribute(String name, float defaultValue) ;
public boolean getBooleanAttribute(String name, boolean defaultValue)
;
public String getRequiredAttribute(String name) throws
ConfigurationException ;
It is also possible to obtain the number of attributes, names of all the attributes, or the set of key/value pairs using the following methods:
public int attributeCount() ;
public Set<String> getAttributeNames() ;
public List<KeyValuePair> attributesAsList() ;
Let us add some configuration properties to our MyAction. We will make the & and the number of times it needs to be printed as configurable properties. Follow these steps:
public String SYMBOL = "&";
public int COUNT = 48;
_config = config;
String symbol = _config.getAttribute("symbol");
if (symbol != null) {
SYMBOL = symbol;
}
String count = _config.getAttribute("count");
if (count != null) {
COUNT = Integer.parseInt(count);
}
private void printLine() {
StringBuffer line = new StringBuffer(COUNT);
for (int i = 0; i < COUNT; i++) {
line.append(SYMBOL);
}
System.out.println(line);
}
printLine();
System.out.println("Body: " + message.getBody().get());
printLine();
return message;
INFO [STDOUT] **************************************************
INFO [STDOUT] Body: Chapter 3 says Hello!
INFO [STDOUT] **************************************************
You just added two properties to the MyAction class. You also retrieved these properties from the ConfigTree and used them.
Experiment with the other API methods. Write hierarchicalProperty and see how that can be retrieved.
I remember deciding to pursue my first IT certification, the CompTIA A+. I had signed…
Key takeaways The transformer architecture has proved to be revolutionary in outperforming the classical RNN…
Once we learn how to deploy an Ubuntu server, how to manage users, and how…
Key-takeaways: Clean code isn’t just a nice thing to have or a luxury in software projects; it's a necessity. If we…
While developing a web application, or setting dynamic pages and meta tags we need to deal with…
Software architecture is one of the most discussed topics in the software industry today, and…