Unit-Of-Order in WLI JMSControl - jms

One of our clientes is intented to use the Unit-Of-Order Weblogic Server Feature (UOO).
Everything is OK using UOO in pure java code for sending JMS Messages with custom UOO Names, as well as propagating the UOO Name in Aqualogic Service Bus from the Proxy Service to a Business Service (both using JMS as the transfer protocol).
However, using UOO in Weblogic Integration along with WLI JMSControl, does no work properly.
Consider this code:
#com.bea.control.JMSControl.Properties(value = {
#com.bea.control.JMSControl.PropertyValue(name = "JMS_BEA_UnitOfOrder", value = "MyUOONameFromWLI"),
#com.bea.control.JMSControl.PropertyValue(name = "MyCustomProperty", value = "MyCustomValue") })
public void sendTextMessage(String payload);
It sends the property MyCustomProperty to the JMS consumer, but the property JMS_BEA_UnitOfOrder - related to UOO Name - is ignored. The default User-generated UOO name is used instead.
So, how to customize my UOO Name using Weblogic JMSControl?

I've found that it has no support!

Related

How to set ContentType in qpid JMS

Hello this question is related to this and I'd like to know if there is any way to set ContentType header in qpid in JMS context. We are using JmsConnectionFactory and spring's JMS library.
I was trying to find some way using extension but can't find the way or if that's even possible.
final org.apache.qpid.jms.JmsConnectionFactory jmsConnectionFactory = new org.apache.qpid.jms.JmsConnectionFactory();
jmsConnectionFactory.setExtension("headers", (connection, uri) -> {
if (connection instanceof JmsConnection) {
}
});
There is no exposed means of setting the content-type on the messages sent from the Qpid JMS client. The client itself uses this field as part of the JMS mapping to AMQP to distinguish certain message types that it sends and to determine at receive time what certain messages should be presented as.
It is technically possible to use reflection to reach in and so the value but the APIs you have to use from the JmsMessageFacade class are not public and could change with any release so choosing to do so comes with significant risk.

How to send messages with selectors in websocket topic in Spring Boot

CLARIFICATION:
Thanks to #JustinBertram comment I realized that this question does not make sense.
STOMP protocol does not support selectors by itself, you have to use brokers such as ActiveMQ that implement it. STOMP supports headers that can be used by brokers for filtering messages by selectors.
In my case, I'm not using any broker, just frontend with Angular + Stomp + SocksJS and backend with Spring Boot, so I can't use selectors.
The documentation of STOMP protocol does not make this clear to me and I got confused. See these references:
the specification:
Stomp brokers may support the selector header which allows you to
specify an SQL 92 selector on the message headers which acts as a
filter for content based routing.
this article:
The subscribe() method takes an optional headers argument to specify
additional headers when subscribing to a destination:
var headers = {ack: 'client', 'selector': "location = 'Europe'"};
client.subscribe("/queue/test", message_callback, headers);
The client specifies that it will handle the message acknowledgement
and is interested to receive only messages matching the selector
location = 'Europe'.
I'm implementing a backend in Spring Boot. For two-way communications with the frontend I'm using stomp over websockets.
I have followed this Spring Boot + Angular example
It works, but one of my requirements is that the backend has to send messages with selectors so that the frontend subscribes to a topic and only receives the filtered data, to avoid performance issues with real time data.
i.e. { 'selector': "location = 'Europe'" }
For that purpose, I'm trying to make the backend send the messages with selectors, but I can't make it work.
I have followed this article to implement the frontend with selectors and it works correctly, the problem is only the backend.
I tried with #SendTo annotation but it seems it doesn't have any params for that as per the article:
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000);
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
Also I tried with the MessagingTemplate, but I don't know how to set the selector properties in the header:
http://assets.spring.io/wp/WebSocketBlogPost.html
MessageSendingOperations<String> messagingTemplate;
messagingTemplate.convertAndSend(destination, quote);
I really appreciate any help, I have read many articles and docs but I don't find anything talking in particular about this with a solution.
Well, it's possible to use the JMS' selectors with Spring (Boot) Websocket and a STOMP client. I found a native way.
The key thing is that the selector is applied to the org.springframework.messaging.Message instance, and it uses Spring's Spel language to apply the condition (it's not the JMS SQL-like).
So using the default SimpMessagingTemplate, in the backend you can send header variables like this:
this.messagingTemplate.convertAndSend(
"/topic/something", //your destination
payload, //any kind of payload (body)
Map.of("id", 1) //header with key/value
);
In the frontend, to enter a selector that will be evaluated by the org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry.filterSubscriptions, you must declare your Stomp/WebSockets headers as:
{"selector": "headers['nativeHeaders']['id'][0] == '999'"}
Yeah, it's horrible but it works.
As the default Message is GenericMessage, the headers are processed in a new key called "nativeHeaders".
The ['key'],[0] and == are Spring's Spel sintaxes.
Go ahead and filter your messages on the backend, not in the frontend, please!
The latest version of the STOMP specification doesn't include any specific statement about selectors and their syntax because it's really up to the broker implementation as to what is supported here. The specification now just states:
STOMP servers MAY support additional server specific headers to customize the delivery semantics of the subscription. Consult your server's documentation for details.
Brokers like ActiveMQ 5.x and ActiveMQ Artemis support the selector STOMP header and the syntax & behavior of the selector is based on JMS selectors.
Selectors in JMS are for selecting messages on consumption and are configured by the consuming client. You can't set the selector when sending the message.
JMS selectors select messages based on the headers or properties of the message, although some implementations go beyond this and allow selecting based on the content of the message itself. Therefore, if you want to have a selector location = 'Europe' on a consumer then you should set a header on the message when it is sent with the name location and the value of Europe.
The convertAndSend method is overloaded and provides a couple of ways to set a header:
Pass a map of key/value pairs to the convertAndSend method.
Implement a MessagePostProcessor and pass that to the convertAndSend method. Inside your post-processor you can invoke the javax.jms.Message#setStringProperty() method.

JMSTemplate with multiple brokers. Destination resolving exception

I have problem which I am trying solve all day, without success...
I have an application which trying to send/receive messages to/from external system A and external system B. A and B it is WLS based external systems.
While my application is coming up - i am reading all configurations and building my applicational JMSProducer and injecting JMSTemlate with predefined destination name in it.
Here is my code:
private JMSProducer initProducer(Conf conf) {
DestinationResolver destinationResolver = getDestinationResolver(conf);
ConnectionFactory connectionFactory = getConnectionFactory();
String destinationName = conf.getDestinationName();
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(connectionFactory);
jmsTemplate.setDestinationResolver(destinationResolver);
jmsTemplate.setDefaultDestinationName(destinationName);
return new JMSProducer(jmsTemplate);
}
public DestinationResolver getDestinationResolver(Conf conf) {
JndiDestinationResolver destinationResolver = new JndiDestinationResolver();
destinationResolver.setCache(false);
destinationResolver.setJndiTemplate(getJNDITemplate(conf));
return destinationResolver;
}
private JndiTemplate getJNDITemplate(Conf conf) {
JndiTemplate jndiTemplate = new JndiTemplate();
Properties properties = new Properties();
String connectionFactoryClassName = externalSystemConf.getConnectionParam().getConnectionFactory();
properties.setProperty("java.naming.factory.initial", connectionFactoryClassName);
properties.setProperty("java.naming.provider.url", getProviderURL(conf.getConnectionParam()));
jndiTemplate.setEnvironment(properties);
return jndiTemplate;
}
Now scenario what happens.
My app is up and running, external system A with 2 queues and external system B with 1 queue also is up and running.
I am retrieving relevant, already initialized JMSProducer in which I have already injected JMSTemplate with destinationName.
Sending messages to queues of external system A
Again retrieving next instance of JMSProducer relevant for system B
Sending messages to queue of external system B
At this stage everything is good, all messages delivered to relevant queues in external systems.
Now I am getting again JMSProducer which relevant for external system A, and trying to send messages to one of the queues. And in this stage I have a problem, DestinationResolutionException is thrown:
Destination [topic2.queueName] not found in JNDI
javax.naming.NameNotFoundException: While trying to lookup 'topic2.queueName' didn't find subcontext 'topic2'. Resolved ""
How it is possible, I have just sent messages to external system A with the same destination and it worked fine. Why it throwing exception when I am sending message to A after I tried to sent it to B?
By the way, If I will try to change cache flag to true when defining destination resolver, it is solving this problem. However in this case I starting to have problem when my external system is going to be restarted. After restart it also have some exception related to destination resolving.
Solved.
Problem was that in both external systems, in WLS - domain name, jms server name, and jms module name were the same. And weblogic client stores state, map with this names.
This is from WLS documentation
Interoperating WebLogic Server domains have the following
restrictions:
Domain names must be unique.
WebLogic server names must be unique, even if they are in two
different domains.
JMS server names must be unique, even if they are in two different
domains.
Interoperating domains may have special Security considerations.
https://docs.oracle.com/cd/E28280_01/web.1111/e13738/best_practice.htm#JMSAD635
After changing all above mentioned names - problem was solved.

How can I get messageId after sending to RabbitMq?

I want some messageId kind of thing when I am sending to Rabbit Mq Queue as I will get when sending to IBM MQ using jms. I am using spring MQ amqp starter dependency with Spring Boot. Configuration is done only in application.yml (property file). I am using Rabbit template for sending.
rabbitMqTemplate.convertAndSend(EMPTY_STRING,queueName, message, messagePostProcessor);
I have tried messagePostProcessor. Any help is appreciated. I had a look into below content. But didnt understand how to implement. Does it require special configuration (connectionfactory/ container)?
https://www.rabbitmq.com/confirms.html
Unlike JMS, the rabbit client doesn't assign message ids.
However, you can configure the RabbitTemplate's MessageConverter to create an id, which you can then retrieve with a post processor.
See AbstractMessageConverter...
/**
* Flag to indicate that new messages should have unique identifiers added to their properties before sending.
* Default false.
* #param createMessageIds the flag value to set
*/
public void setCreateMessageIds(boolean createMessageIds) {
this.createMessageIds = createMessageIds;
}
For message confirmations, see the reference manual. But that is unrelated to the message id property.

Solace NIFI JMSConnectionFactoryProvider

I am trying to connect to Solace Queues on a VPN different then default using Appache NIFI ConsumeJMS Processor. When I try to enable the JMSConnectionFactoryProvider I get the following error:
JMSConnectionFactoryProvider Failed to invoke #OnEnabled method due to
java.lang.IllegalStateException: java.lang.IllegalStateException:
Failed to load and/or instantiate class
'com.solacesystems.jms.SolConnectionFactory'
The NIFI JMSConnectionFactoryProvider provides a generic service to create vendor specific javax.jms.ConnectionFactory implementations. ConnectionFactory can be served once this service is configured successfully.
Why is NIFI Unable to find the class within the Solace JMS API Jar files?
----- Update --------
Solace JMS API 10.1.0 now contains an zero argument default constructor, and integration with NiFi is now possible.
Here is an example configuration:
Controller:
The MQ ConnectionFactory Implementation is set to com.solacesystems.jms.SolConnectionFactoryImpl.
ConsumeJMS Processor:
Username field can also take the form of "myUsername#myMessageVPN".
----- Original -------
The problem here is that Apache NiFi is not using a portable method of creating a ConnectionFactory. It is trying to create a ConnectionFactory by calling an zero argument default constructor, but there's no guarantee that one exists.
// From https://github.com/apache/nifi/blob/master/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-cf-service/src/main/java/org/apache/nifi/jms/cf/JMSConnectionFactoryProvider.java
private void createConnectionFactoryInstance(ConfigurationContext context) {
String connectionFactoryImplName = context.getProperty(CONNECTION_FACTORY_IMPL).evaluateAttributeExpressions().getValue();
this.connectionFactory = Utils.newDefaultInstance(connectionFactoryImplName);
}
Note that there's an entry over at NiFi's JIRA https://issues.apache.org/jira/browse/NIFI-2701 to "Add JNDI Factory support for JMS ConnectionFactory service". (The initial description of that entry is a bit confusing, but the comments are clearer.)
At this point, Solace only supports the creation of the ConnectionFactory through the standard JNDI lookup - javax.naming.InitialContext.lookup() and through Solace's proprietary method - SolJmsUtility.createConnectionFactory().
Solace will investigate whether it is feasible to implement a zero argument default constructor for the ConnectionFactory.
A JNDI connection provider is developed and published at http://dev.solace.com/integration-guides/nifi/. For people that are interested in this provider, it is worthwhile finding out.

Resources