These are my notes I took before taking the
Enterprise Integration with Spring Certification exam.
I passed the exam. At the second attempt, after actually taking those notes. The fist attempt was after 1-2 weeks of preparation and it was unnecessarily rushed (I knew it’s too early, if you think it’s too early it’s probably too early), The most difficult thing in preparation for this exam was keeping all those different new things separate in my head and not mixing them up. As soon as I knew where I am in the imaginary table of contents, everything was clear. What helped in organising the knowledge was reading
this book.
There is some overlap of this exam with the first Spring Core exam. There is also some stuff that logically would belong to Spring Integration but is actually not part of the exam - the amount of material is simply too big to squeeze everything. That is why it is essential to read the official study guide and study according to it. Or to my notes below (which follow the guide) :P
Table of contents
- Remoting
- Web Services
- RESTful services with Spring-MVC
- JMS with Spring
- Transactions
- Batch processing with Spring Batch
- Spring Integration
Remoting
- The concepts involved with Spring Remoting on both server- and client-side
- Exporters on server side, bind a service to registry, or expose an endpoint; no code change to the service
- ProxyFactoryBeans on client side, which handle the communication and convert exceptions; you inject the instance of service which in in fact a dynamic proxy (polymorphism)
- The benefits of Spring Remoting over traditional remoting technologies
- Hide ‘plumbing’ code
- Support multiple protocols in a consistent way (normally it’s violation of concerns, business logic mixed with remoting infrastructure)
- Configure and expose services declaratively (configuration-based approach)
- The remoting protocols supported by Spring
- RMI - uses RMI registry, Java serialisation
-
HttpInvoker - HTTP based (POST), Java serialisation
-
Hessian / Burlap - HTTP based, binary/txt XML serialisation
- Stateless EJB (not mentioned in the training)
-
How Spring Remoting-based RMI is less invasive than plain RMI
-
Spring HTTP Invoker: how client and server interact with each other
Server side | Client side | |
RMI |
<bean class="RmiServiceExporter">
<p:service-name value="myServiceNameInRegistry"/>
<p:service-interface value="...TheService"/>
<p:service ref="myService"/>
<p:registry-port="1099"/>
</bean>
|
<bean id, class="RmiProxyFactoryBean">
<p:service-interface value="myService"/>
<p:service-url value="rmi://foo:1099/myServiceName
InRegistry"/> </bean>
|
Http
Invoker |
<bean name="/transfer" class="HttpInvokerServiceExporter">
<p:service-interface value="...TheService"/>
<p:service ref="myService"/>
</bean>
+ DispatcherServlet or HttpRequestHandlerServlet with name ”transfer”
|
<bean id, class="HttpInvokerProxyFactoryBean">
<p:service-interface value="myService"/>
<p:service-url value="rmi://foo:8080/services/transfer"/>
</bean>
|
Hessian / Burlap
|
same as above, replace “HttpInvoker” with “Hessian” / “Burlap”
|
Web Services
- How do Web Services compare to Remoting and Messaging
- information exchange protocol and format is HTTP and XML (or JSON) => no firewalls in between
- two ways of using the HTTP to transfer XML
- SOAP/POX and WSDL/XSD - contract-first, they use only POST and/or GET method for all the operations
- REST
- “loose coupling – we define document-oriented contract between service consumers and providers
- interoperability – XML payload (is understood by all major platforms like Java. NET, C++, Ruby, PHP, Perl,...)”
- The approach to building web services that Spring-WS supports
- is contract-first approach (start with XSD / WSDL)
- POX / SOAP + WSDL
- WebServiceClient - support for creating e.g. SOAP message
-
@PayloadRoot defines the resource path, @ResponsePayload and @RequestPayload are for XML mapping
-
The Object-to-XML frameworks supported by Spring-OXM (or Spring 3.0)
-
JAXB1/2, Castor, XMLBeans, JiBX
-
XStream (not mentioned in training)
-
also XPath argument binding
-
The strategies supported to map requests to endpoints (?) (link)
-
@PayloadRoot - on method level, requires namespace+localPart values, which build the URL qualifier; needs the PayloadRootAnnotationMethodEndpointMapping registered;
-
SOAP Action Header - based on the To and Action SOAP headers, @SoapAction: on method level “Whenever a message comes in which has this SOAPAction header, the method will be invoked.”
-
WS-Addressing, or AnnotationActionEndpointMapping, also AddressingEndpointInterceptor - annotate the handling methods with the @Action("http://samples/RequestOrder") annotation
-
XPath - @Namespace(prefix = "s", uri="http://samples") annotation on class/method level, and @XPathParam("/s:orderRequest/@id") int orderId in the method - the attribute will be given the value which is the evaluation of the XPath expression
-
Message Payload - e.g. @RequestPayload Element inside the method
-
Of these strategies, how does @PayloadRoot work exactly?
-
it’s written above, but: “The PayloadRootAnnotationMethodEndpointMapping uses the @PayloadRoot annotation, with the localPart and namespace elements, to mark methods with a particular qualified name. Whenever a message comes in which has this qualified name for the payload root element, the method will be invoked.“
-
The functionality offered by the WebServiceTemplate
-
does the SOAP stupid stuff, and also POX
-
works with marshallers / unmarshallers (set the “marshaller” and “unmarshaller” propperty)
-
convenience methods
-
callbacks
-
error handling (annotate method with @SoapFault(faultCode=FaultCode.CLIENT), SoapFaultMessageResolver is default)
-
the template by default uses Java's HttpUrlConnectionMessageSender, if you wanna apache client, override "messageSender" with HttpComponentsMessageSender
-
it’s also possible to use JmsMessageSender
-
The underlying WS-Security implementations supported by Spring-WS (link)
-
are implemented as interceptors
-
XwsSecurityInterceptor (requires Sun JVM and SAAJ) - requires security policy XML configuration file
-
Wss4jSecurityInterceptor (also supports non-Sun JVMs and Axiom)
-
support for Spring Security and JAAS keystores (JAAS goes with wss)
-
also client-side interceptors, which are injected in the WebServiceTemplate
-
supports: authentication, digital signatures, encryption and decryption
-
exception handling: WsSecuritySecurementException (just logs), WsSecurityValidationException (translated to SOAP Fault)
-
How key stores are supported by Spring-WS for use with WS-Security (?)
-
in Java, the keystores are of type java.security.KeyStore and they store:
-
private keys
-
symmetric keys
-
trusted certificates
-
Spring provides KeyStoreFactoryBean and KeyStoreCallbackHandler
SOAP / POX (Spring WS)
|
||
web.xml
|
<servlet>
<servlet-name>si-ws-gateway</servlet-name>
<servlet-class>
MessageDispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>si-ws-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>si-ws-gateway</servlet-name>
<url-pattern>/quoteservice</url-pattern>
</servlet-mapping>
* also add contextConfigLocation as context-param and ContextLoaderListener for app context
|
|
web infrastr. config
|
<bean class="....UriEndpointMapping">
<p:name="defaultEndpoint" ref="ws-inbound-gateway"/>
</bean>
|
|
app config
|
<context:component-scan base-package=”transfers.ws”/>
<ws:annotation-driven/>
|
|
Endpoint implementation
|
@Endpoint
public class TransferServiceEndpoint {
...
public @ResponsePayload TransferResponse newTransfer(@RequestPayload TransferRequest request){
...
}
}
|
<int-ws:inbound-gateway id="ws-inbound-gateway"
request-channel="ws-requests"
extract-payload="false"
[marshaller/unmarshaller=”jaxb2”]/>
<oxm:jaxb2-marshaller
id=”marshaller”
contextPath=”reward.ws.types:someotherpackage”/>
, or just simply
<ws:annotation-driven/> <!-- registers all infrastructure beans needed for annotation-based endpoints, like JAXB2 (un)marshalling-->
<SOAP-ENV::Header>
...
<wsa:To S:mustUnderstand="true">http://example/com/fabrikam</wsa:To>
<wsa:Action>http://example.com/fabrikam/mail/Delete</wsa:Action>
</SOAP-ENV:Header>
@Action("http://samples/RequestOrder")
public Order getOrder(OrderRequest orderRequest) {
return orderService.getOrder(orderRequest.getId());
}
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
@Namespace(prefix = "s", uri="http://samples")
public Order getOrder(@XPathParam("/s:orderRequest/@id") int orderId) {
Order order = orderService.getOrder(orderId);
// create Source from order and return it
}
<bean class=”...WebServiceTemplate”>
<property name=”defaultUri” value=”http://mybank.com/transfer”/>
<property name=”marshaller” ref=”marshallerAndUnmarshaller”/>
<property name=”unmarshaller” ref=”marshallerAndUnmarshaller”/>
<property name=”faultMessageResolver” ref=”myCustomFaultMessageResolver”/>
</bean>
<bean id=”marshallerAndUnmarshaller” class=”...CastorMarshaller”>
<property name=”mappingLocation” value=”classpath:castor-mapping.xml”/>
</bean>
template.marshallSendAndReceive(new TransferRequest(“S123”));
template.sendSourceAndReceiveToResult(source, result);
full definition, e.g.:
doSendAndReceive(MessageContext messageContext, WebServiceConnection connection, WebServiceMessageCallback requestCallback, WebServiceMessageExtractor<T> responseExtractor)
<ws:interceptors>
<bean class=”blabla.SoapEnvelopeLoggingInterceptor”
</ws:interceptors>
or
<ws:interceptors>
<ws:payloadRoot localPart=”MyRequest” namespaceUri=”htpp://blabla.com/namespace”>
<bean class=”blabla.SoapEnvelopeLoggingInterceptor”
</ws:payloadRoot>
</ws:interceptors>
client-side:
<bean class=”org...WebServiceTemplate”>
<property name=”interceptors”>
<bean class=”blablabalInterceptor”
</property>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="password" value="password"/>
<property name="location" value="classpath:org/springframework/ws/soap/security/xwss/test-keystore.jks"/>
</bean>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="wsSecurityInterceptor"
class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="keyStoreHandler"/>
<ref bean="...
</list>
</property>
</bean>
RESTful services with Spring-MVC
- The main REST principles
-
(makes HTTP not only transport protocol, but also application protocol)
-
H Hypermedia (links)
-
U Uniform Interface (nouns for resources, verbs for operations: GET, POST, PUT, DELETE, HEAD, OPTIONS)
-
S Stateless Conversation => scalable
-
I Identifiable Resources
-
R Resource Representations (multiple representations for resource, which is abstract; Accept header in req, Content-Type in res)
-
REST support in Spring-MVC
-
name - e.g. "/subscribe", so that it allows it to be used with DispatcherServlet
-
view-name - is the Spring MVC view name
-
you can also use inbound-message-adapter if you don't need two way communication, it uses MessageTemplate
-
you can also add e.g. @ResponseStatus(value=HttpStatus.CONFLICT) on your Exception class, to have the exception mapped
-
alternatively to the above, in your controller you can add an empty void method annotated with @ExceptionHandler(value=YourException.class) and @ResponseStatus(value=HttpStatus.NOT_FOUND)
- you can use outbound-channel-adapter if you don’t need two way communication, it uses RestTemplate;
- in the case above it’s better to override the error handler, as the default one treats only 4** and 5** responses as errors
-
Spring-MVC is an alternative to JAX-RS, not an implementation
-
got it ;)
-
The @RequestMapping annotation, including URI template support
-
The @RequestBody and @ResponseBody annotations
-
The functionality offered by the RestTemplate
-
for client side
-
has default HttpMessageConverters (same like on server), supports URI templates
-
e.g. Jaxb2RootElementHttpMessageConverter, register it with <mvc:annotation-driven/> !
-
it can also use external configuration, e.g. Apache Commons HTTP Client (set the “requestFactory” property to CommonsCliemtHttpRequestFactory
-
HttpEntity represents request or response (payload + headers)
Method
|
Safe (no side effects)
|
Indepotent
|
Comments
|
GET
|
y
|
y
|
Is cacheable (ETag) or Last-Modified, 304
|
HEAD
|
y
|
y
|
|
POST
|
n
|
n
|
Location header in response
|
PUT
|
n
|
y
|
Create OR update
|
DELETE
|
n
|
y
|
SOAP / POX (Spring WS)
|
||
web.xml
|
<servlet>
<servlet-name>http-ws-gateway</servlet-name>
<servlet-class>HttpRequestHandlerServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param- name>
<param-value>http-ws-gateway.xml</param-value>
</servlet>
<servlet-mapping>
<servlet-name>http-ws-gateway</servlet-name>
<url-pattern>/httpquote</url-pattern>
</servlet-mapping>
|
|
web infrastr. config
|
<int-http:inbound-gateway
id="http-inbound-gateway"
request-channel="http-request"
reply-channel="http-response"
extract-reply-payload="false"
view-name="about"
reply-key, reply-timeout,message-converters,
supported-methods, convert-exceptions,
request-payload-type, error-code, errors-key,
header-mapper, name/>
|
|
app config
|
N/A
|
|
Endpoint implementation
|
@Controller
@RequestMapping(“/rewards”)
public class Blabla{
@RequestMapping(value=”/{number}”, method=GET)
public String Reward blabla(@RequestBody someObject / Model model, @PathVariable(“number”) String number){
// ...
// return view name
}
@RequestMapping(method=POST)
@ResponseStatus(HttpStatus.CREATED)
public @ResponseBody Reward blabla(@RequestBody someObject, HttpServletResponse res){
// ...
res.addHeader(“Location”, getMeUrl(order.getId()));
// return the object, will be mapped because of @ResponseBody, by converter based on Accept header
}
}
|
<int-http:outbound-gateway
url="http://blblah"
request-channel="requests"
http-method="GET"
expected-response-type="java.lang.String">
<int-http:uri-variable
name="location"
expression="payload"/>
</int:http:outbound-gateway>
<T> T getForObject(URIurl, Class<T>responseType) throws RestClientException; ← returns the object from GET
<T> T getForObject(Stringurl, Class<T>responseType, Object... uriVariables)throwsRestClientException;
<T> T getForObject(Stringurl, Class<T>responseType, Map<String, ?>uriVariables)throwsRestClientException;
<T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType)
throws RestClientException; ← returns the whole response from GET (with headers)
void put(String url, Object request, Object...uriVariables) throws RestClientException;
void delete(String url, Map<String, ?>uriVariables) throws RestClientException;
<T> T postForObject(String url,Object request,Class<T> responseType, Object… uriVariables) throws RestClientException;
<T> ResponseEntity <T> postForEntity(String url, Object request, Class<T> resType, Object...uriVariables)
throws RestClientException;
* URI url=response.getHeaders().getLocation(); ← to get location of the new resource!
<T> T execute()
<T> ResponseEntity <T> exchange(String url, HttpMethod method, HttpEntity<?> reqEntity, Class<T> resType, Object... uriVariables)
throws RestClientException;
JMS with Spring
-
Where can Spring-JMS applications obtain their JMS resources from
-
either create manually (standalone), or obtain from JNDI:
-
The functionality offered by Spring's JMS message listener container, including the use of a MessageListenerAdapter through the 'method' attribute in the <jms:listener/> element
- <div "> MessageListener an interface for asynchronous reception of messages; one method: void onMessage(Message) </div>
-
implement MessageListener, or SessionAwareMessageListener (extends MessageListener)
-
requires a listener container - in the past EJB container, now
-
SimpleMessageListenerContainer - fixed number of sessions
-
DefaultMessageListenerContainer - adds transactional capability
-
The functionality offered by the JmsTemplate
-
converts exceptions to unchecked
-
convenience methods
-
needs reference to ConnectionFactory, optionally set defaultDestination property
-
use CachingConnectionFactory wrapper around the ActiveMQConnectionFactory, as JmsTemplate aggresively opens and closes and reopens JMS resources
-
uses
-
MessageConverter (default SimpleMessageConverter - handles String, Serializable, Map, byte[]) - from/to Message
-
DestinationResolver (default DynamicDestinationResolver, JndiDestinationResolver)- from String to Destination
Destination
|
<bean id=”orderQueue” class=”org.apache.activemq...ActiveMQQueue”>
<constructor-arg value=”queue.orders”/> </bean> |
<jee:jndi-lookup id=”orderQueue” jndi-name=”jms/OrderQueue”/>
|
|
ConnectionFactory
|
<bean id=”cf” class=”org.apache.activemq.ActiveMQConnectionFactory”>
<property name=”brokerURL” value=”tc[://localhost:61616”/> </bean> |
<jee:jndi lookup id=”cf” jndi-name=”jms/ConnectionFactory”/>
|
|
Connection
|
connectionFactory.createConnection();
|
Session
|
created from the Connection
|
JMS Message
|
created from Session
session.createProducer(destination);
|
MessageProducer
|
|
MessageConsumer
|
<jms:listener-container connection-factory=”cf”>
<jms:listener destination=”queue.orders”
ref=”myListener
(method=”order”)
(response-destination=”queue.confirmation”)/>
</jms:listener-container>
void convertAndSend([String/Destination d,] Object m)
void convertAndSend(Object m, MessagePostProcessor mpp) // to do stuff to the message after it has been converted
void send(MessageCreator mc) // is used inside convertAndSend()
Object execute(ProducerCallback<T> action)
Object execute(SessionCallback<T> action)
Message receive([String/Destination d,])
Object receiveAndConvert(destination)
Transactions
-
Local JMS Transactions with Spring
-
acknowledge mode - not transactions connection.createSession(transacted=false, acknowledgeMode)
-
AUTO_ACKNOWLEDGE (default) => calling .receive() or onMessage() = removing message from the queue
-
CLIENT_ACKNOWLEDGE => client must call message.acknowledge(), and this one call will remove all messages since last time! (which are bound to the same session). And client can also call session.recover() to have redelivery of those messages (can be duplicates)
-
DUPS_OK_ACKNOWLEDGE => like auto but lazily, once every few times
-
transacted session - connection.createSession(transacted=true, null)
-
starts local JMS when no managed JMS or JTA transaction is in progress; will be synchronised with existing local transaction
-
transaction starts, when message is received
-
if a message fails, it will be put back on the queue
-
How to enable local JMS transactions with Spring's message listener container
-
If and if so, how is a local JMS transaction made available to the JmsTemplate
-
jmsTemplate will automatically use same session (ConnectionFactoryUtils.doGetTransactionalSession()), so the above settings are ignored in case the session was created within an active transaction already
-
How does Spring attempt to synchronize a local JMS transaction and a local database transaction (?)
-
Commit database before JMS (can end up with duplicates), and at the end
-
Put commits close together
-
only as last resort use XA
-
The functionality offered by the JmsTransactionManager (?)
-
JmsTransactionManager or DataSourceTransactionManager are both implementations of PlatformTransactionManager
-
“Performs local resource transactions, binding a JMS Connection/Session pair from the specified ConnectionFactory to the thread
-
The JmsTemplate auto-detects an attached thread and participates automatically with Session
-
The JmsTransationManager allows a CachingConnectionFactory that uses a single connection for all JMS access (performance gains). All Sessions belong to the same connection”
-
What guarantees does JTA provide that local transactions do not provide
-
once-and-once-only delivery
-
ACID with multiple resources
-
How to switch from local to global JTA transactions
-
you also may have to reconfigure some resources like Hibernate
-
Where can you obtain a JTA transaction manager from
-
within J2EE server:
-
standalone definition:
-
this class is provided by Spring, but it only integrates with external JTA TX manager (e.g. Atomikos was used in course)
-
the externally provided transactionManager and userTransaction resources have to be of type “XA aware”
<jms:listener-container acknowledge=”auto|client|dups_ok|transacted”/>
or
<jms:listener-container transaction-manager=”tm”/>
connection.createSession(transacted=true, acknowledgeMode);
session.commit();
session.rollback();
<bean class=”..JmsTemplate>
(<property name=”sessionAcknowledgeMode” value=”...”/>)
<property name=”sessionTransacted” value=”true”/>
</bean>
<jms:listener-container transaction-manager=”transactionManager” ../>
<tx:jta-transaction-manager/>
<jee:jndi-lookup id=”dataSource” jndi-name=”java:comp/env/jdbc/myDS”/>
<jee:jndi-lookup id=”connectionFactory” jndi-name=”java:comp/env/jdbc/myConnFac”/>
<bean id=”transactionManager” class=”org….JtaTransactionManager”>
<property name=”transactionManager” ref=”jtaTxMgr”/>
<property name=”userTransaction” ref=”userTx”/>
</bean>
Batch processing with Spring Batch
-
Main concepts (Job, Step, Job Instance, Job Execution, Step Execution, etc.)
-
easy, see also here
-
job execution has status, but job instance (job+parameters) also has a status, the “overall status”
-
The interfaces typically used to implement a chunk-oriented Step
-
Step for one chunk goes like this:
-
ItemReader, ItemReader<Dining>, public Dining read(){}
-
ItemProcessor (optional), ItemProcessor<XMLDining, Dining>, public Dining process(XMLDining bla){}
-
ItemWriter, ItemWriter<Dining>, public write(List<? extends Dining> dinings){}
-
How and where state can be stored
-
in JobRepository
-
it can use database or in memory map
-
persists jobs’ metadata and intermediate state of execution (=job instance and job execution)
-
JobLauncher creates JobExecution entity in the JobRepository, next it executes the job, and returns the result
-
JobLauncher is already wrapped in CommandLineJobRunner, if you wanna use it
-
jobLauncher.run(job, parameters)
-
What are job parameters and how are they used
-
they identify the job instance, same instance cannot be run twice thats why add there some counter
-
in case same job instance is attempted to be re-launched you will get JobInstanceAlreadyCompletedException
-
you create it programmatically using builder:
-
What is a FieldSetMapper and what is it used for
-
FieldSetMapper<T> → T mapFieldSet(FieldSet fs)
-
used by e.g. LineMapper (e.g. DefaultLineMapper), which is used by FlatFileItemReader
<job id="resendUnprocessedDinings">
<step id="processConfirmationsStep" next="sendUnprocessedDiningsStep">
<tasklet>
<chunk reader="confirmationReader"
writer="confirmationUpdater"
commit-interval="${chunk.size}"
reader-transactional-queue="true"/>
</tasklet>
</step>
</job>
<bean id="itemReader" class="....FlatFileItemReader" scope="step">
<property name="resource" value="file://#{jobParameters['filena']}"/>
<property name="lineMapper">
<bean class="....DefaultLineMapper">
<property name="lineTokenizer">
<bean class="....DelimitedLineTokenizer">
<property name="names" value="source,dest,amount,date"/>
</bean>
</property>
<property name="fieldSetMapper" ref=”myMapper”/>
</bean>
</property>
</bean>
<bean id="itemWriter" class="....FlatFileItemWriter" scope="step">
<property name=”fieldSetCreator” ref=”customCreator”/>
...
</bean>
<batch:job-repository id=”jobRepository”/>
, or
<batch:job-repository
data-source="dataSource"
id="jobRepository"
transaction-manager="transactionManager"
table-prefix="BATCH_"/>
<bean id="jobLauncher" class="....SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository"/>
</bean>
JobParametersBuilder jpb = new JobParametersBuilder();
jpb.addString('filena', 'payment.xml');
JobExecution execution = jobLauncher.run(job, jpb.toJobParameters());
Date date = fs.readDate(0,"dd/MM/yyyy");
Long number = fs.readLong(1);
String value = fs.readString("city");
String[] values = fs.getValues();
public class MyMapper implements FieldSetMapper<Payment>{
@Override
public Payment mapFieldSet(FieldSet fieldSet)
throws BindException {
... = fieldSet.readString("source");
... = fieldSet.readBigDecimal("amount");
... = fieldSet.readDate("date");
}
}
Spring Integration
-
Main concepts (Messages, Channels, Endpoint types)
-
Pay special attention to the various Endpoint types and how they're used!
-
refer even more carefully the above
-
How to programmatically create new Messages
-
MessageBuilder of course
-
each message is created with unique ID
-
Also MessagingTemplate is worth mentioning, which is a programmatic endpoint for sending the messages created by MessageBuilder (e.g. for testing)
-
Using chains and bridges
-
see a.
-
Synchronous vs. asynchronous message passing: the different Channel types and how each of them should be used
-
see a.
-
The corresponding effects on things like transactions and security
-
see a.
-
The need for active polling and how to configure that
Comments
Comments: