Setting Trusted Packages (ActiveMQ) - java

I am trying to send receive ActiveMQ messages. But I see the messages come back with this message in them.
JMSException in onMessage(): javax.jms.JMSException: Failed to build body from content. Serializable class not available to broker. Reason: java.lang.ClassNotFoundException: Forbidden class com.logicalprovisioning.common.gtc.shared.GTCMessage! This class is not trusted to be serialized as ObjectMessage payload. Please take a look at http://activemq.apache.org/objectmessage.html for more information on how to configure trusted classes.
So I read the link in the message. And I tried to follow the instructions. Although I must say its not very well written on where to put the configuration.
So what I did was:
1. I edited the ACTIVEMQ_OPTS line in activemq.bat file in the bin folder to
if "%ACTIVEMQ_OPTS%" == "" set ACTIVEMQ_OPTS=-Xms1G -Xmx1G -Djava.util.logging.config.file=logging.properties -Djava.security.auth.login.config="%ACTIVEMQ_CONF%\login.config" -Dorg.apache.activemq.SERIALIZABLE_PACKAGES=com.logicalprovisioning.common.gtc.shared.GTCMessage
It didn't work.
I added the above line in the activemq.bat of the win64 folder as well. It didn't work.
I modified my subscriber object creation to add the Trusted Packages. Like:
String providerEndpoints = "tcp://" + host + ":" + port + "?wireFormat.maxInactivityDuration=7200000";
// Set the trusted packages/classes to move back and forth on the ActiveMQ JMS service.
ArrayList<String> trustedClasses = new ArrayList<String>();
trustedClasses.add("com.logicalprovisioning.common.gtc.shared.GTCMessage");
// Obtain the factory
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL(providerEndpoints);
// Add the trusted packages/classes to the ActiveMQ consumer.
activeMQConnectionFactory.setTrustedPackages(trustedClasses);
//Create the connection
setConnection(activeMQConnectionFactory.createQueueConnection());
getConnection().setClientID(this.getName());
// Make a session
setSession(getConnection().createQueueSession(false, Session.AUTO_ACKNOWLEDGE));
getSession().createQueue(jmsDestination);
// Create the destination
Destination destination = getSession().createQueue(jmsDestination);
String selector = "JMSCorrelationID = '" + getActionRequest().getOriginId() + "_" + getActionRequest().getRequestId() + "'" ;
setConsumer(getSession().createConsumer(destination, selector));
getConsumer().setMessageListener(new DefaultMessageListener(this));
// Start ...
gtcMessages = new GTCMessageQueue<GTCMessage>(); // We'll need a message store now
getConnection().start();
And I added something similar in my producer as well for good measure:
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
String queueConnectionFactoryNameLookup = PalInit.getProperty("jms.queue.connection.factory");
// Set the trusted packages/classes to move back and forth on the ActiveMQ JMS service.
ArrayList<String> trustedClasses = new ArrayList<String>();
trustedClasses.add("com.logicalprovisioning.common.gtc.shared.GTCMessage");
ActiveMQConnectionFactory activeMQConnectionFactory = (ActiveMQConnectionFactory) environmentContext.lookup(queueConnectionFactoryNameLookup);
activeMQConnectionFactory.setTrustedPackages(trustedClasses);
// Create connection
QueueConnection queueConnection = activeMQConnectionFactory.createQueueConnection();
queueConnection.start();
// Create session and producer
setSession(queueConnection.createSession(false, Session.AUTO_ACKNOWLEDGE));
String queueName = PalInit.getProperty("jms.destination");
Queue jmsQueue = getSession().createQueue(queueName);
setProducer(getSession().createProducer(jmsQueue));
setQueueConnection(queueConnection);
// Set Message "Time to Live" to the request timeout plus 10 minutes
getProducer().setTimeToLive(getTimeout() + (10 * 60 * 1000L));
But nothing seems to work. I have the ActiveMQ-All jar in the Tomcat's lib folder and so too the jar where the GTCMessage class resides. Can anyone please tell me what am I doing wrong? Is it a problem of a missing class or my bad configuration? Any help would be appreciated. Thanks!
The application is running on Tomcat 9, JAVA 1.8 and Active MQ 5.15.11.

I think your problem is that you're setting the name of the specific class rather than the package of the class. The code looks at the package name, not the class name. Try this:
// Set the trusted packages to move back and forth on the ActiveMQ JMS service.
ArrayList<String> trustedPackages = new ArrayList<String>();
trustedPackages.add("com.logicalprovisioning.common.gtc.shared");
ActiveMQConnectionFactory activeMQConnectionFactory = (ActiveMQConnectionFactory) environmentContext.lookup(queueConnectionFactoryNameLookup);
activeMQConnectionFactory.setTrustedPackages(trustedPackages);
I don't think you'll need to set this on the broker itself.
Aside from that I would strongly discourage you from using JMS ObjectMessage. They depend on Java serialization to marshal and unmarshal their object payload. This process is generally considered unsafe, because a malicious payload can exploit the host system. Lots of CVEs have been created for this which is why most JMS providers force users to explicitly whitelist packages that can be exchanged using ObjectMessage messages.
There are also a number of other issues with using JMS ObjectMessage not related to security that you should read about. This article has a good suggestion for how to replace ObjectMessage - define a data representation for the payload (JSON, protobuf, XML) and use TextMessage or BytesMessage to carry it.

Related

How to retrieve properties set by a JMS producer through Websphere MQ API?

We are trying to retrieve the JMS Headers we populate in the message using the Websphere MQ APIs.
Right now i am using mq-all-client jars to establish the connection to the queueManagers.
getOptions.options = CMQC.MQGMO_NO_WAIT + CMQC.MQGMO_FAIL_IF_QUIESCING + CMQC.MQGMO_CONVERT;
I tried retrieving the JMS Property by :
MQMessage message = new MQMessage();
queue.get(message, getOptions);
logger.info(message.getStringProperty("My_PROPERTY"));
I am getting a null .
Is there a way where you establish a connection through MQ-allclients jar and still retrieve the JMS property set on the message ?
I can retrieve the properties through a JMS Consumer but i want to get it through the MQ APIs.
logger.info(message.getStringProperty("My_PROPERTY"));
Well, that 'particular' named property does not exist. Don't forget, property keywords are case sensitive.
Why don't you dump all of the named properties of the message and see exactly which ones are in the message?
String propName;
Enumeration<String> props = msg.getPropertyNames("%");
if (props != null)
{
while (props.hasMoreElements())
{
propName = props.nextElement();
System.out.println("---> propName="+propName+" : " + "value="+msg.getObjectProperty(propName));
}
}

Qpid receiver on Azure EventHub

I have already working application based on Azure EventHub. Now I need write java receiver that connects to the existing infrastructure.
Existing configuration:
Event Hub > SomeName > Consumer Group > SomeGroupName
In the administrative console I cannot see any QUEUE or TOPIC definitions. Analyzing working c# code I can see that hub-name + group-name is enough to connect.
I have reconstructed url that allows me to connect over java (and connection works so far).
amqps://SomeName.servicebus.windows.net
So my questions:
1) When instead of queue /topic I specify group-name then I get exception The messaging entity 'sb://SomeName.servicebus.windows.net/SomeGroupName' could not be found. What is the model used there instead of queue/topic?
2) How to work with such infrastructure from Apache-qpid?
Are you using the Event Hub created in the old portal or one created using the new portal?
EventHub is not a Message Bus, so there are no Queues or Topics, that is correct.
The consumer group is not a part of the address. The address is build using the namespace and the name of the eventhub in that namespace.
So the address becomes:
sb://SomeNameSpaceName.servicebus.windows.net/SomeEventHubName
Can you post the c# code you've analyzed? Since you have an already working application maybe we can workout the differences that prevents it from working now.
The greatest hint for resolve the question gave me following link: http://theitjourney.blogspot.com/2015/12/sendreceive-messages-using-amqp-in-java.html
So No queue neither topic in this model. You need to connect to specific provider and specify correct EventHub as following:
application.properties:
connectionfactory.SBCF=amqps://<PolicyName>:<PolicyKey>#<DomainName>.servicebus.windows.net
queue.EventHub=<EventHubName>/ConsumerGroups/$Default/Partitions/0
Where:
After that following code allowed me to create MessageConsumer:
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.qpid.amqp_1_0.jms.jndi.PropertiesFileInitialContextFactory");
env.put(Context.PROVIDER_URL,
getClass().getResource("/application.properties").toString());
Context context = null;
context = new InitialContext(env);
// Look up ConnectionFactory
ConnectionFactory cf = (ConnectionFactory) context.lookup("SBCF");
Destination queue = (Destination) context.lookup("EventHub");
// Create Connection
Connection connection = cf.createConnection();
// Create receiver-side Session, MessageConsumer
Session receiveSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer receiver = receiveSession.createConsumer(queue);

Get a Topic in JMS using Java SE

I am using Java SE. I create a topic when the app first starts like so:
connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
conn = connectionFactory.createTopicConnection();
session = conn.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
conn.start();
session.createTopic(name);
I am confused on how to retrieve this Topic in my classes.
Say for example I have a class, and it connects to the JMS Service just like above using:
connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
conn = connectionFactory.createTopicConnection();
session = conn.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
conn.start();
How can I then get a reference to the Topic I created a app startup to send messages?
I would imagine something along the lines of:
session.getTopic(name);
Would exist, but I cant find anything on it.
I have read how to do this using Java EE using JNDI lookup, but this service is not available to me since I am not running in a container.
You don't 'retrieve' a topic. A Topic instance is just a piece of information. You construct an instance of it in your client if you want to subscribe to a topic (or queue), like is demonstrated in the ActiveMQ hello world example:
http://activemq.apache.org/hello-world.html
ex:
// the name should of course be the same as it exists on the producer side
Destination destination = session.createTopic("TEST.FOO");
// Create a MessageConsumer from the Session to the Topic or Queue
MessageConsumer consumer = session.createConsumer(destination);
This is all governed by the standardized and very mature JMS API, so you should refer to the JEE documentation. Any book on JMS will work for you as well.
Further reading: https://docs.oracle.com/javaee/6/tutorial/doc/bncdr.html
API docs: http://docs.oracle.com/javaee/6/api/javax/jms/package-summary.html

org.apache.activemq.transport.InactivityIOException: Cannot send, channel has already failed

I am using apache's activemq for queueing. We have started to see the following exception very often when writing things to the queue:
Caused by: org.apache.activemq.transport.InactivityIOException: Cannot send, channel has already failed:
at org.apache.activemq.transport.AbstractInactivityMonitor.doOnewaySend(AbstractInactivityMonitor.java:282)
at org.apache.activemq.transport.AbstractInactivityMonitor.oneway(AbstractInactivityMonitor.java:271)
at org.apache.activemq.transport.TransportFilter.oneway(TransportFilter.java:85)
at org.apache.activemq.transport.WireFormatNegotiator.oneway(WireFormatNegotiator.java:104)
at org.apache.activemq.transport.MutexTransport.oneway(MutexTransport.java:68)
at org.apache.activemq.transport.ResponseCorrelator.asyncRequest(ResponseCorrelator.java:81)
at org.apache.activemq.transport.ResponseCorrelator.request(ResponseCorrelator.java:86)
at org.apache.activemq.ActiveMQConnection.syncSendPacket(ActiveMQConnection.java:1366)
I can't figure out what could be causing this-- or even, frankly, where to start debugging what is causing this.
Here is the queue set up code:
camelContext = new DefaultCamelContext();
camelContext.setErrorHandlerBuilder(new LoggingErrorHandlerBuilder());
camelContext.getShutdownStrategy().setTimeout(SHUTDOWN_TIMEOUT_SECONDS);
routePolicy = new RoutePolicy();
routePolicy.setCamelContext(camelContext);
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(queueUri);
// use a pooled connection factory between the module and the queue
pooledConnectionFactory = new PooledConnectionFactory(connectionFactory);
// how many connections should there be in the session pool?
pooledConnectionFactory.setMaxConnections(this.maxConnections);
pooledConnectionFactory.setMaximumActiveSessionPerConnection(this.maxActiveSessionPerConnection);
pooledConnectionFactory.setCreateConnectionOnStartup(true);
pooledConnectionFactory.setBlockIfSessionPoolIsFull(false);
JmsConfiguration jmsConfiguration = new JmsConfiguration(pooledConnectionFactory);
jmsConfiguration.setDeliveryPersistent(false); // do not store a copy of the messages on the queue
ActiveMQComponent activeMQComponent = ActiveMQComponent.activeMQComponent(queueUri);
activeMQComponent.setConfiguration(jmsConfiguration);
camelContext.addComponent("activemq", activeMQComponent);
Component activemq = camelContext.getComponent("activemq");
// register endpoints for queues and topics
Endpoint queueEndpoint = activemq.createEndpoint("activemq:queue:polaris.*");
Endpoint topicEndpoint = activemq.createEndpoint("activemq:topic:polaris.*");
producerTemplate = camelContext.createProducerTemplate();
camelContext.start();
queueEndpoint.start();
topicEndpoint.start();
Like I said, the error doesn't suggest any directions for debugging, and it doesn't happen in 100% of cases where I can be sure my configuration is not set up correctly.
Recently I ran into the same problem. I found this https://issues.apache.org/jira/browse/AMQ-6600
Apache ActiveMQ client throws InactivityIOException when one of the jars is missing in classpath. In my case it was hawtbuf-1.11.jar. When I added this jar to classpath it started to work without errors.
management port: 61616 (default)
service port : 8161(default)
change your broker url port to 61616 and run
refer this
Check, if there is a non-Jms client pinging your JMS broker. This may be an external monitoring tool, a load balancing tool such as keepalived, or another one.

How to use Java JMS with MQseries

I am trying to develop a JMS standalone application to read and write to a Queue on MQSeries.
My boss asked me to use pure java JMS (not ibm.mq lib) to do that.
Here is the information that need to make the jms connection:
mq.hostname=10.10.10.10
mq.channel=API.CLIENTCHL
mq.queueManager=MQPETAPI
mq.port=1422
Do you know how to do that Or do you have any link that teach me to do that.
The issue here is the requirement that "My boss asked me to use pure java JMS (not ibm.mq lib) to do that." JMS is a specification and each implementation must comply with the API and the semantics, but is free to do whatever they want at a low level. It is always necessary to use the implementation classes provided by the transport vendor. Therefore if you use WebSphere MQ as the transport, you will need to use the IBM MQ JMS classes to write a JMS application.
That said, if you stick with pure JMS API calls you would be able to plug in any transport vendor's classes. This is what is usually intended when you are given requirements such as that mentioned in the original post.
There's an article describing exactly what you are looking to do called Running a standalone Java application on WebSphere MQ V6.0 It uses only the JMS API and it uses JNDI in a local file system (a .bindings file). By swapping out the IBM JMS classes for another vendor and using their JNDI tools you would be able to plug in any JMS transport without changing your code using this approach.
If you want to do the same thing without JNDI, look at the sample programs provided with the MQ client install where you obtained your Java classes. In a UNIX/Linux system these are in /opt/mqm/samp and on Windows they are in install_dir/tools/jms/samples. The SimpleRequestor.java sample has the following code for initializing your connection factory without JNDI:
try {
// Create a connection factory
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf = ff.createConnectionFactory();
// Set the properties
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, "localhost");
cf.setIntProperty(WMQConstants.WMQ_PORT, 1414);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, "SYSTEM.DEF.SVRCONN");
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, "QM1");
Because this approach does not use JNDI, you are required to write code that is not transportable across transport vendors. It is IBM WebSphere MQ specific.
If you grabbed the MQ jars from somewhere and do not have the full install (and thus do not have the samples) you can download it as SupportPac MQC7. The download is free. In general you should use the latest client, even with a back-level queue manager. Obviously you do not get V7 functionality from a V6 QMgr but the JMS implementation in the V7 client is much improved, even for V6 functionality. If for some reason you really must use the V6 client, you can download it as SupportPacMQC6. Whichever client version you use, make sure to use the corresponding Infocenter.
V6 Infocenter
V7 Infocenter
Finally, the landing page with an index for all the SupportPacs is here.
A complete (synchronous) standalone JMS application with TextMessage.
It is IBM WebSphere MQ specific.
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import com.ibm.mq.jms.JMSC;
import com.ibm.mq.jms.MQQueueConnectionFactory;
public class JMSApplicationStandAlone {
public static void main(String[] args) {
try {
/*MQ Configuration*/
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setHostName("localhost");
mqQueueConnectionFactory.setChannel("MQ.CHANNEL");//communications link
mqQueueConnectionFactory.setPort(1416);
mqQueueConnectionFactory.setQueueManager("QUEUE.MGR");//service provider
mqQueueConnectionFactory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
/*Create Connection */
QueueConnection queueConnection = mqQueueConnectionFactory.createQueueConnection();
queueConnection.start();
/*Create session */
QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
/*Create response queue */
Queue queue = queueSession.createQueue("QUEUE.RESPONSE");
/*Create text message */
TextMessage textMessage = queueSession.createTextMessage("put some message here");
textMessage.setJMSReplyTo(queue);
textMessage.setJMSType("mcd://xmlns");//message type
textMessage.setJMSExpiration(2*1000);//message expiration
textMessage.setJMSDeliveryMode(DeliveryMode.PERSISTENT); //message delivery mode either persistent or non-persistemnt
/*Create sender queue */
QueueSender queueSender = queueSession.createSender(queueSession.createQueue("QUEUE.REQEST"));
queueSender.setTimeToLive(2*1000);
queueSender.send(textMessage);
/*After sending a message we get message id */
System.out.println("after sending a message we get message id "+ textMessage.getJMSMessageID());
String jmsCorrelationID = " JMSCorrelationID = '" + textMessage.getJMSMessageID() + "'";
/*Within the session we have to create queue reciver */
QueueReceiver queueReceiver = queueSession.createReceiver(queue,jmsCorrelationID);
/*Receive the message from*/
Message message = queueReceiver.receive(60*1000);
String responseMsg = ((TextMessage) message).getText();
queueSender.close();
queueReceiver.close();
queueSession.close();
queueConnection.close();
} catch (JMSException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Note: Replace the configuration values
If you don't mind writing WMQ-specific code then you can do
MQConnectionFactory cf = new MQConnectionFactory();
cf.setHostName(HOSTNAME);
cf.setPort(PORT);
cf.setChannel(CHANNEL);
cf.setQueueManager(QMNAME);
cf.setTransportType(WMQConstants.WMQ_CM_CLIENT);
then usual JMS resources
Connection c = cf.createConnection();
Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue q = s.createQueue("myQueue"); // replace with real queue name
MessageProducer p = s.createProducer(q);
and finally create and send a message
Message m = s.createTextMessage("Hello, World!);
p.send(m);
(i've typed that in off the top of my head so can't rule out a typo, but it's fundamentally correct). If you're really supposed to be using 'pure JMS' - i.e. with no provider-specific objects - then you need to bind a MQConnectionFactory object in JNDI (take a look at the JMSAdmin tool, it's in the docs) then look it up from your application, i.e.
InitialContext ic = new InitialContext(); // or as appropraite
ConnectionFactory cf = (ConnectionFactory)ic.lookup("myMQfactory"); // replace with JNDI name
Typically with JMS you would define the QueueConnectionFactory in your container via whatever configuration mechanism it makes available then add it to the container's JNDI registry. Each container would have it's own methods for doing that (i.e. Tomcat versus WebSphere).
If you want to forgo JNDI, you could create an instance of com.ibm.mq.jms.MQQueueConnectionFactory directly and set the hostname, port, queueManager, and channel properties on it. You can then use that object as you would an instance of javax.jms.QueueConnectionFactory since it implements it.
I can't teach you JMS in a single post, but I can point you to some of the resources that I used to learn it myself:
The O'Reilly Java Message Service book
IBM Developerworks JMS Tutorial (more MQSeries/Websphere MQ specific)
The Spring framework can help you use JMS more effectively, especially if you're developing a standalone app outside of a J2EE app server: Spring Java Message Service documentation
This is fairly common. Here are some examples.

Categories