we are using rabbit MQ and Spring Integration in our project. Every Message has a deliver mode, header, properties, and payload part.
We want to add properties i.e) priority with value 2(any integer) , payload with "test message 3" and publish the message to the queue named OES. please see screen shot.
How to add the messageproperties i.e) priority =2(or any value) in the below outbound-channel-adapter(Spring Integration). I know we can add "headers" by adding into "mapped-request-headers" but i would like to add the properties. There are no properties defined for the MessageProperties in "outbound-channel-adapter". Is there a way to overcome this issue.
We have no issues with payload, it is going already. we want to add only the MessageProperties with priority=2(any value). how to add that in the outbound-channel-adapter(no need of hardcoding, should be generic)?
<!-- the mapped-request-headers should be symmetric with
the list on the consumer side defined in consumerbeans.consumerHeaderMapper() -->
<int-amqp:outbound-channel-adapter id="publishingAmqpAdapter"
channel="producer-processed-event-channel"
amqp-template="amqpPublishingTemplate"
exchange-name="events_forwarding_exchange"
routing-key-expression="headers['routing-path']"
mapped-request-headers="X-CallerIdentity,routing-path,content-type,route_to*,event-type,compression-state,STANDARD_REQUEST_HEADERS"
/>
Other configuration:
<!-- chain routes and transforms the ApplicationEvent into a json string -->
<int:chain id="routingAndTransforming"
input-channel="producer-inbound-event-channel"
output-channel="producer-routed-event-channel">
<int:transformer ref="outboundMessageTracker"/>
<int:transformer ref="messagePropertiesTransformer"/>
<int:transformer ref="eventRouter"/>
<int:transformer ref="eventToJsonTransformer"/>
</int:chain>
<int:transformer id="messagePayloadCompressor"
input-channel="compress-message-payload"
output-channel="producer-processed-event-channel"
ref="payloadCompressor"/>
#Configuration("amqpProducerBeans")
#ImportResource(value = "classpath:com/apple/store/platform/events/si/event-producer-flow.xml")
public class AmqpProducerBeans {
#Bean(name = { "amqpPublishingTemplate" })
public AmqpTemplate amqpTemplate() {
logger.debug("creating amqp publishing template");
RabbitTemplate rabbitTemplate = new RabbitTemplate(producerConnectionFactory());
SimpleMessageConverter converter = new SimpleMessageConverter();
// following needed for retry logic
converter.setCreateMessageIds(true);
rabbitTemplate.setMessageConverter(converter);
return rabbitTemplate;
}
/*Other code commented */
}
Other Code:
import org.springframework.integration.Message;
import org.springframework.integration.annotation.Transformer;
import org.springframework.integration.message.GenericMessage;
public class PayloadCompressor {
#Transformer
public Message<byte[]> compress(Message<String> message){
/* some code commented */
Map<String, Object> headers = new HashMap<String, Object>();
headers.putAll(message.getHeaders());
headers.remove("compression-state");
headers.put("compression-state", CompressionState.COMPRESSED);
Message<byte[]> compressedMessage = new GenericMessage<byte[]>(compressedPayload, headers);
return compressedMessage;
}
If we are not using spring integration, then we can use channel.basicPublish below way and send the MessageProperties.
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/");
factory.setHost("10.102.175.30");
factory.setUsername("rahul");
factory.setPassword("rahul");
factory.setPort(5672);
Connection connection = factory.newConnection();
System.out.println("got connection "+connection);
Channel channel = connection.createChannel();
MessageProperties msgproperties= new MessageProperties() ;
MessageProperties.BASIC.setPriority(3);
// set Messageproperties with priority
String exchangeName = "HeaderExchange";
String routingKey = "testkey";
//routingkey
byte[] messageBodyBytes = "Message having priority value 3".getBytes();
channel.basicPublish(exchangeName,
routingKey,
true,
msgproperties.BASIC,
messageBodyBytes);
Please let me know if you need more details.
Properties are already mapped automatically - see the header mapper.
Simply use a <header-enricher/> to set the appropriate header and it will be mapped to the correct property. In the case of priority, the constant is here for the amqp-specific header constants, see here.
Related
I'm looking for a way to copy some headers from the request message to the response message when I use RabbitMq in RPC mode.
so far I have tried with setBeforeSendReplyPostProcessors but I can only access the response and add headers to it. but I don't have access to the request to get the values I need.
I have also tried with the advice chain, but the returnObject is null after proceeding so I can't modify it (I admit I don't understand why it is null... I thought I could get the object to modify it):
#Bean
public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer simpleRabbitListenerContainerFactoryConfigurer, ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory = new SimpleRabbitListenerContainerFactory();
simpleRabbitListenerContainerFactory.setAdviceChain(new MethodInterceptor() {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object returnObject = invocation.proceed();
//returnObject is null here
return returnObject;
}
});
simpleRabbitListenerContainerFactoryConfigurer.configure(simpleRabbitListenerContainerFactory, connectionFactory);
return simpleRabbitListenerContainerFactory;
}
a working way is to change my method annotated with #RabbitListener so it returns a Message and there I can access both the requesting message (via arguments of the annotated method) and the response.
But I would like to do it automatically, since I need this feature at different places.
Basicaly I want to copy one header from the request message to the response.
this code do the job, but I want to do it through an aspect, or an interceptor.
#RabbitListener(queues = "myQueue"
, containerFactory = "simpleRabbitListenerContainerFactory")
public Message<MyResponseObject> execute(MyRequestObject myRequestObject, #Header("HEADER_TO_COPY") String headerToCopy) {
MyResponseObject myResponseObject = compute(myRequestObject);
return MessageBuilder.withPayload(myResponseObject)
.setHeader("HEADER_RESPONSE", headerToCopy)
.build();
}
The Message<?> return type support was added for this reason, but we could add an extension point to allow this, please open a GitHub issue.
Contributions are welcome.
Anyone knows if it is possible to send a collection of messages to a queue using Rabbit template?
Obviously I can send them one at a time, but I want to do it in a single bulk operation (to gain performance).
Thanks!
You can create a bean of BatchingRabbitTemplate and use it. Here is a working example bean:
#Bean
public BatchingRabbitTemplate batchingRabbitTemplate(ConnectionFactory connectionFactory) {
BatchingStrategy strategy = new SimpleBatchingStrategy(500, 25_000, 3_000);
TaskScheduler scheduler = new ConcurrentTaskScheduler();
BatchingRabbitTemplate template = new BatchingRabbitTemplate(strategy, scheduler);
template.setConnectionFactory(connectionFactory);
// ... other settings
return template;
}
Now you can inject BatchingRabbitTemplate in another bean and use it:
#Bean
public ApplicationRunner runner(BatchingRabbitTemplate template) {
MessageProperties props = //...
return args -> template.send(new Message("Test").getBytes(), props);
}
See Reference Manual about batching support:
Starting with version 1.4.2, the BatchingRabbitTemplate has been introduced. This is a subclass of RabbitTemplate with an overridden send method that batches messages according to the BatchingStrategy; only when a batch is complete is the message sent to RabbitMQ.
code like below i send a message and set it messageid and CorrelationIdString
rabbitTemplate.send(RabbitMQConfig.EXCHANGE_NAME, "aaa.orange.bbb",new Message(messageBody, MessagePropertiesBuilder.newInstance().setCorrelationIdString(uuid3).
setMessageId(uuid3).setContentType("text/x-json").build()),
new CorrelationData(uuid3)
);
and the receiver code
public void processMessage (Message msg) throws Exception {
// Thread.sleep(100000);
System.out.println("Receiver1 got message" + msg);
and the log
Receiver1 got message(Body:'hello,world1 2' MessageProperties [headers={spring_listener_return_correlation=93fbcc71-b0eb-4d33-a187-d4b27122a663}, timestamp=null, messageId=5f779051-12c5-43f1-a589-6d14430d3a52, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=text/x-json, contentEncoding=null, contentLength=0, deliveryMode=null, receivedDeliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=first_exchange, receivedRoutingKey=aaa.orange.bbb, receivedDelay=null, deliveryTag=1, messageCount=0, consumerTag=amq.ctag-JbtjvUYYqlWOIsgKkOe-8A, consumerQueue=queue_a])
my question is why CorrelationIdString is null is that a problem or not
We migrated away from a byte[] for correlationId to String in 1.6 and 1.7 but we had to stick with byte[] by default for backwards compatibility; the migration is complete in 2.0 (currently 2.0.2) and correlationIdString is not longer a property.
I suggest moving to 2.0.
Alternatively, if you must use an older version, see CorrelationIdPolicy here to switch from byte[] to String or BOTH.
For me I was forced to override createContainerInstance method to change the policy on the listener side.
final DefaultMessagePropertiesConverter messagePropertiesConverter = new DefaultMessagePropertiesConverter();
messagePropertiesConverter.setCorrelationIdPolicy(DefaultMessagePropertiesConverter.CorrelationIdPolicy.STRING);
return new SimpleRabbitListenerContainerFactory() {
#Override
protected SimpleMessageListenerContainer createContainerInstance() {
final SimpleMessageListenerContainer result = new SimpleMessageListenerContainer();
result.setMessagePropertiesConverter(messagePropertiesConverter);
return result;
}
};
I am attempting to send a message to a VM endpoint using the FunctionalTestCase below:
#Test
public void CreateAddToCalendarOptionsApiTest() throws Exception{
MuleClient client = new MuleClient(muleContext);
String payload = "foo";
Map<String, Object> properties = null;
MuleMessage result = client.send("vm://processActivity", payload, properties);
assertEquals("foo Received", result.getPayloadAsString());
}
However, in the flow to be tested there are multiple VM connectors defined, so I am getting a TransportFactoryException stating:
org.mule.transport.service.TransportFactoryException: There are at least
2 connectors matching protocol "vm", so the connector to use must be
specified on the endpoint using the 'connector' property/attribute.
Connectors in your configuration that support "vm" are: inMemoryVMQueue,
recordDeletedActivityDLQStore, recordPublishedActivityDLQStore,
recordUpdatedActivityDLQStore, deleteQueuedActivityDLQStore,
(java.lang.IllegalStateException)
How do I specify that my client.send("vm://processActivity", payload, properties) uses a specific VMConnector?
Use a query parameter:
client.send("vm://processActivity?connector=inMemoryVMQueue", payload, properties);
... replacing inMemoryVMQueue by whatever connector you want to use.
It becomes functionally equivalent to an endpoint property.
"vm://test?connector=VM" define like the below in your query path
public void test() throws MuleException{
MuleMessage message = new DefaultMuleMessage("ts",muleContext);
MuleMessage reply = muleContext.getClient().send("vm://test?connector=VM", message);
}
I want to create a flow or model dynamically without using mule-config.xml for tcp with remote machines.
It should be something like this:
MuleContext context = new DefaultMuleContextFactory().createMuleContext();
MuleRegistry registry = context.getRegistry();
EndpointBuilder testEndpointBuilder = new EndpointURIEndpointBuilder("vm://testFlow.in",
context);
testEndpointBuilder.setExchangePattern(MessageExchangePattern.REQUEST_RESPONSE);
registry.registerEndpointBuilder("testFlow.in", testEndpointBuilder);
InboundEndpoint vmInboundEndpoint = testEndpointBuilder.buildInboundEndpoint();
registry.registerEndpoint(vmInboundEndpoint);
StringAppendTransformer stringAppendTransformer = new StringAppendTransformer(" world");
stringAppendTransformer.setMuleContext(context);
Flow testFlow = new Flow("testFlow", context);
testFlow.setMessageSource(vmInboundEndpoint);
testFlow.setMessageProcessors(Arrays.asList((MessageProcessor) stringAppendTransformer));
registry.registerFlowConstruct(testFlow);
context.start();
MuleClient muleClient = new MuleClient(context);
MuleMessage response = muleClient.send("vm://testFlow.in", "hello", null);
Validate.isTrue(response.getPayloadAsString().equals("hello world"));
muleClient.dispose();
context.stop();
Not sure if I understand your problem, but if you need a tcp outbound endpoint in your flow, you just create it similarly like the inbound vm endpoint in the example, but you then add it to a certain point in the flow in a list with all the processors with setMessageProcessors, like in the example where stringAppendTransformer is wrapped inside a list and added to the flow.
The code to create your tcp outbound would be something like this:
String address = "tcp://localhost:1234";
EndpointURIEndpointBuilder builder = new
EndpointURIEndpointBuilder(new URIBuilder(address), context);
builder.setExchangePattern(MessageExchangePattern.REQUEST_RESPONSE);
registry.registerEndpointBuilder("testFlow.out", builder);
OutboundEndpoint tcpOutboundEndpoint = builder.buildOutboundEndpoint();
registry.registerEndpoint(tcpOutboundEndpoint);
UPDATE regarding your new comment:
using a Java component:
//object factory for your Java class
PrototypeObjectFactory objectFactory = new PrototypeObjectFactory(MyClass.class);
objectFactory.initialise();
//the actual component
DefaultJavaComponent component = new DefaultJavaComponent(objectFactory);
//entry point resolver to determine the called method
EntryPointResolver resolver = new ExplicitMethodEntryPointResolver();
((ExplicitMethodEntryPointResolver)resolver).addMethod("myMethod");
component.setEntryPointResolvers(Arrays.asList(resolver));
Then add the component in the list like you add all the other processors