how to read a jms queue from a non hosted java application? - jms

I'm trying to read messages from a jms queue created in "Sun App Server" from a non-hosted application (console app) but I get the following error:
NoInitialContextException
Cannot instantiate class: javax.jms.TopicConnectionFactory
with this code:
Properties env = new Properties( );
env.put(Context.INITIAL_CONTEXT_FACTORY, "javax.jms.TopicConnectionFactory");
InitialContext jndi = new InitialContext(env);
and I have referenced the j2ee.jar library that contains the class but certainly, the class is an interface.
Can I access the queue from a non-hosted application??

Aitor;
When you say "Sun App Server", I'm not sure what that means, but I will assume it is Glassfish.
There are 2 separate steps to acquiring remote JMS resources.
You need to create a remote JNDI connection which requires a valid InitialContextFactory class name.
Once you have a the connection, you can look up the TopicConnectionFactory.
For item #1, this link demonstrates how to make a remote JNDI connection.
For item #2, once you have a JNDI context, you will also need to know the JNDI name of the TopicConnectionFactory which will look something like:
TopicConnectionFactory tcf = (TopicConnectionFactory) jndi.lookup("jms/TopicConnectionFactory");
One aspect you need to keep in mind is that the j2ee.jar library contains the generic Java EE interfaces for the JMS classes, but you will also need a library in your classpath that contain the JMS implementation concrete classes. This also goes for the JNDI connection. This tutorial provides a concise list as:
Applicationserver JNDI Lookup
/lib/appserv-rt.jar
/lib/appserv-admin.jar
/lib/javaee.jar /lib/j2ee.jar
Client Lib
/imq/lib/jms.jar
/imq/lib/imq.jar
/imq/lib/imqutil.jar
/lib/install/applications/jmsra/jmsra.jar

Related

Adding ActiveMQ as a JMS Provider, Topic Connection Factory and Topic definitions

I am trying to add ActiveMQ as a JMS Provider in Websphere Application Server.
I have followed the instructions described here ActiveMQ 5.11 with WebSphere Application Server 8.5 and adapted to the topic.
Unfortunately I am not sure what I need to add in External JNDI name for both Topic Connection Factory and Topic definitions.
As per IBM documentation:
"External JNDI Name The JNDI name that is used to bind the queue into
the application server name space.
As a convention, use the fully qualified JNDI name; for example, in
the form jms/Name, where Name is the logical name of the resource.
This name is used to link the platform binding information. The
binding associates the resources defined by the deployment descriptor
of the module to the actual (physical) resources bound into JNDI by
the platform."
From my understanding this should be the name that I am using in my app to access the resource defined in WAS.
I also have added the resources in my deployment descriptor as resources.
Any help would be highly appreciated.
Regards
Given that you are accessing the resources via resource references (defined in your deployment descriptor), the configured JNDI name should match the lookup name that is defined in your resource reference.
For example, if your resource reference looks like this,
<resource-ref>
<res-ref-name>java:comp/env/jms/topicConnectionFactoryRef</res-ref-name>
<res-type>javax.jms.TopicConnectionFactory</res-type>
<lookup-name>jms/myTopicConnectionFactory</lookup-name>
</resource-ref>
or if your resource-ref lacks lookup-name and you instead have a ibm-web-bnd.xml file with a binding-name,
<resource-ref name="java:comp/env/jms/topicConnectionFactoryRef"
binding-name="jms/myTopicConnectionFactory">
</resource-ref>
then specify jms/myTopicConnectionFactory as the JNDI name.
Application code will then be able to do:
TopicConnectionFactory tcf = InitialContext.doLookup("java:comp/env/jms/topicConnectionFactoryRef");
Application code could also perform a direct lookup of the JNDI name as follows (although using the resource reference is preferred because it is more spec compliant and standard across app servers),
TopicConnectionFactory tcf = InitialContext.doLookup("jms/myTopicConnectionFactory");
The same applies to javax.jms.Topic.
If your resource environment reference in your deployment descriptor looks like this,
<resource-env-ref>
<resource-env-ref-name>java:comp/env/jms/topicRef</resource-env-ref-name>
<resource-env-ref-type>javax.jms.Topic</resource-env-ref-type>
<lookup-name>jms/myTopic</lookup-name>
</resource-env-ref>
or if your resource-ref lacks lookup-name and you instead have a ibm-web-bnd.xml file with a binding-name,
<resource-ref name="java:comp/env/jms/topicRef" binding-name="jms/myTopic">
</resource-ref>
then specify jms/myTopic as the JNDI name of the Topic.
Application code will then be able to do:
Topic topic = InitialContext.doLookup("java:comp/env/jms/topicRef");
Some optimizations/special cases:
If you have neither lookup-name nor binding-name, then WebSphere Application Server computes a default binding via the resource reference name. If this is the case for your resource reference, then you will have a deployment descriptor such as the following without any bindings file,
<resource-ref>
<res-ref-name>jms/myTopicConnectionFactory</res-ref-name>
<res-type>javax.jms.TopicConnectionFactory</res-type>
</resource-ref>
In the above case, specify jms/myTopicConnectionFactory as the JNDI name.
The application will be able to look it up as,
TopicConnectionFactory tcf = InitialContext.doLookup("java:comp/env/jms/myTopicConnectionFactory");

OpenLiberty NameNotFoundException: java:/comp/env

I have a web app running in Tomcat correctly that I want to run on the new OpenLiberty server, the app is starting correctly inside OpenLiberty but at the moment of the database connection initiation is throwing the following exception:
[Default Executor-thread-15] 2018-03-15 15:02:30 ERROR TomcatConnectionManager:41 - Loading jdbc/mysql/myaap failure
javax.naming.NameNotFoundException: java:/comp/env
at com.ibm.ws.jndi.url.contexts.javacolon.internal.JavaURLName.<init>(JavaURLName.java:83)
at com.ibm.ws.jndi.url.contexts.javacolon.internal.JavaURLNameParser.parse(JavaURLNameParser.java:39)
at com.ibm.ws.jndi.url.contexts.javacolon.internal.JavaURLNameParser.parse(JavaURLNameParser.java:60)
at com.ibm.ws.jndi.url.contexts.javacolon.internal.JavaURLContext$NameUtil.<init>(JavaURLContext.java:474)
at com.ibm.ws.jndi.url.contexts.javacolon.internal.JavaURLContext.lookup(JavaURLContext.java:321)
at com.ibm.ws.jndi.url.contexts.javacolon.internal.JavaURLContext.lookup(JavaURLContext.java:370)
at org.apache.aries.jndi.DelegateContext.lookup(DelegateContext.java:161)
The above exception is thrown during the lookup phase:
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env");
Is there any way to make it work on OpenLiberty doing less changes possible?
On OpenLiberty the equivalent lookup would look like this:
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:comp/env");
The key is that you need to use java:comp/... instead of java:/comp/...
The reason why Tomcat is different than Liberty is because Tomcat is just a servlet container and Liberty conforms to the full Java EE specification.
According to section EE.5.2.2 of the Java EE 7 spec:
The application component’s naming environment is composed of four logical
namespaces, representing naming environments with different scopes. The four
namespaces are:
java:comp – Names in this namespace are per-component (for example, per enterprise
bean). Except for components in a web module, each component gets
its own java:comp namespace, not shared with any other component. Components
in a web module do not have their own private component namespace.
See note below.
java:module – Names in this namespace are shared by all components in a
module (for example, all enterprise beans in a single EJB module, or all components
in a web module).
java:app – Names in this namespace are shared by all components in all modules
in a single application, where “single application” means a single deployment
unit, such as a single ear file, a single module deployed standalone, etc.
For example, a war file and an EJB jar file in the same ear file would both have
access to resources in the java:app namespace.
java:global – Names in this namespace are shared by all applications deployed
in an application server instance. Note that an application server instance
may represent a single server, a cluster of servers, an administrative
domain containing many servers, or even more. The scope of an application
server instance is product-dependent, but it must be possible to deploy multiple
applications to a single application server instance.
Had a similar problem going between WebSphere and Tomcat. I'm developing and testing on a Tomcat server and using utilities I can't change that handle the DB connection to our DB2. On WebSphere it uses a constant set to "jdbc/COMPDB2" to retrieve the DataSource when I configure Tomcat and my Web.xml file it resolves to "java:comp/env/jdbc/SFCCDB2"
My work around for on local work space it to add a listener to copy the resource to the level in the InitialContext. I'm not very experienced with the server side of things but this is working so far using TomEE 7.0.81.
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/SFCCDB2");
javax.naming.Context envCtx = (javax.naming.Context) ctx.lookup("java:comp/env");
try{
/*
Added this because after redeploying code to the server it would error
connecting to the DB with an SQLException Datasource is closed
*/
DataSource dataSource = (DataSource) ctx.lookup("jdbc/COMPDB2");
ctx.destroySubcontext("jdbc");
} catch (NamingException e){
//Doesn't exist; safe to just add
}
ctx.createSubcontext("jdbc");
ctx.bind("jdbc/COMPDB2", ds);
ctx.close();

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.

Difference between DefaultJMSConnectionFactory and JmsXA

Out-of-the-box, Wildfly 10 configures a pooled connection factory as part of the JMS subsystem with two entries.
<pooled-connection-factory name="activemq-ra"
transaction="xa"
connectors="in-vm"
entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory"/>
One might inject a connection factory like so:
#Resource(mappedName = "java:jboss/DefaultJMSConnectionFactory")
private ConnectionFactory connectionFactory;
What is the difference between this and choosing the other entry; java:/JmsXA?
There is no difference it's an additionnal JNDI entry to suit JMS 2 spec default ConnectionFactory name : java:comp/DefaultJMSConnectionFactory
You should resolve it using this name.
Since JMS 2.0, a default JMS connection factory is accessible to EE
application under the JNDI name java:comp/DefaultJMSConnectionFactory.
WildFly messaging subsystem defines a pooled-connection-factory that
is used to provide this default connection factory. Any parameter
change on this pooled-connection-factory will be take into account by
any EE application looking the default JMS provider under the JNDI
name java:comp/DefaultJMSConnectionFactory.
See https://docs.jboss.org/author/display/WFLY9/Messaging+configuration
The other one is just a legacy identifier :
The JCA layer intercepts the calls to createConnection() and createSession() and provides a caching layer (amongst other things). So when you call createConnection() or createSession(), then, in most cases it's not really calling the actual JMS implementation to actually create a new JMS connection or JMS session, it's just returning one from it's own internal cache - in other words the JCA layer pools JMS connections and JMS sessions.
In JBoss application server, the "special" JMS connection factory which provides the JCA caching is usually available at java:/JmsXA in jndi.
See https://developer.jboss.org/wiki/ShouldICacheJMSConnectionsAndJMSSessions

What activation config properties do I really need for MDB to listen to remote queue in JBoss 5 using default jms-ra.rar

I'm trying to understand what config properties do I really need to make an MDB deployed on JBoss 5 process messages from a queue set up on a remote JMS provider. I'm planning to configure the activation spec using the ejb-jar.xml, and I wan't to configure only what I really need, not more. My understanding is that the allowed config for the spec is configured in the ra.xml in use. If I look for the ra.xml from jms-ra.rar I can see the below inbound-resourceadapter element
<inbound-resourceadapter>
<messageadapter>
<messagelistener>
<messagelistener-type>javax.jms.MessageListener</messagelistener-type>
<activationspec>
<activationspec-class>org.jboss.resource.adapter.jms.inflow.JmsActivationSpec</activationspec-class>
<required-config-property>
<config-property-name>destination</config-property-name>
</required-config-property>
</activationspec>
</messagelistener>
</messageadapter>
</inbound-resourceadapter>
only the destination property is mandatory. In my case this destination is remote, therefore I did configure a JMSProviderLoader with the necessary JNDI properties and also a connection factory that references this JMSProviderLoader, both in a new [myprovidername]-ds.xml file I added to deploy.
I cannot understand how if I only add a destination will the MDB know it is from that specific remote JMS provider, I do see that the outbound-resourceadapter element from ra.xml specifies the below:
<config-property>
<description>The jndi name of the provider of connection factories</description>
<config-property-name>JmsProviderAdapterJNDI</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>java:DefaultJMSProvider</config-property-value>
</config-property>
But judging from the word outgoing, this would not apply to inbound flows.
In summary, to enable an MDB to listen, do I only need destination? If so, how does it know what JMS provider this is from given that I could have multiple providers with same destination name?
Thanks
The magic comes with the Activation Spec that is configured in the inbound-resourceadapter:
<activationspec-class>org.jboss.resource.adapter.jms.inflow.JmsActivationSpec</activationspec-class>
Looking into the source shows, that java:/DefaultJMSProvider is hard set as default for the providerAdapterJNDI, so this one is used automagically, same as explicitly configured for the outbound part.
So you only need a destination as mandatory property because all else has a viable default in JmsActivationSpec.
As long as you name your own JMSProviderLoader to be the "Default" one in your *-ds.xml, everything works fine:
<attribute name="ProviderName">DefaultJMSProvider</attribute>
Otherwise you would have to set the according attribute explicitly in your activation spec for the MDB.

Resources