Multiple MBean Servers found - Spring JMX with Websphere 7 - spring

I am using Spring 3 and exposed an MBean through Spring, everything works fine but I see below warning message in the Log file.
WARN org.springframework.jmx.support.JmxUtils - Found more than one MBeanServer instance. Returning first from list.
Here is my configuration:
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="assembler" ref="assembler" />
<property name="namingStrategy" ref="namingStrategy" />
<property name="autodetect" value="true" />
<property name="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>
</bean>
<bean id="jmxAttributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />
<bean id="assembler"
class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="jmxAttributeSource" />
</bean>
<bean id="WASAdminService" class="com.ibm.websphere.management.AdminServiceFactory"
factory-method="getAdminService" />
<bean id="namingStrategy" class="com.xxxx.WebSphereNamingStrategy">
<constructor-arg ref="WASAdminService" />
</bean>
I put a debug point and found that it is finding 'com.ibm.ws.management.PlatformMBeanServer' and 'com.sun.jmx.mbeanserver.JmxMBeanServer' objects. Any idea why it is finding more than one? When I googled, I found that I can specify the 'agent id' to find the required MBean server, but again if we are deploying this code in multiple environments, agent id may not be same (cannot be static)...
Any inputs on this is appreciated...
Thanks, Kiran

My guess is that the two MBean servers correspond to:
the "Platform" MBean server - established by the JVM. This MBean server would have all of the builtin Mbeans, like: java.lang:type=Runtime
the WebSphere provided MBean server - which has MBeans like WebSphere:name=WebContainer,process=...
I'm new to this area, but am just about to investigate further. From my perspective, I'd like to specify the Platform Mbean server - so that it's easier to access from monitoring tools, like JConsole, etc

Related

Spring Configuration for JMS (Websphere MQ - SSL, Tomcat, JNDI, Non IBM JRE)

Background:
I have a relatively old application that uses Websphere MQ for messaging. It runs on WAS (Websphere Application Server) and uses MDBs (Message Driven Beans). I was successfully able to replace all MDBs using Spring Integration - JMS. My next step is try to see if I can port it out of WAS so that it can run on any other servlet container with a non-IBM JRE (I am trying: apache tomcat). Note that securing channels using SSL is a requirement. I prefer using JNDI.
End Goal:
To decouple my application from the application server (WAS) and other infrastructure like messaging (MQ). But taking this out of WAS onto tomcat is the first step. Next comes the task of updating my messaging infrastructure with something more scalable. This allows me to update individual components of the infrastructure that my app relies on, one thing at a time (app server, messaging layer, datastore) without disrupting my application too much, as I go.
Question:
Now, my challenge is to define JNDI resources on tomcat that can access Websphere MQ. I have made some progress on this using non-SSL channels that I defined in the context.xml file like so:
<Resource
name="jms/qcf_sandbox"
auth="Container"
type="com.ibm.mq.jms.MQQueueConnectionFactory"
factory="com.ibm.mq.jms.MQQueueConnectionFactoryFactory"
description="JMS Queue Connection Factory for sending messages"
HOST="localhost"
PORT="1414"
CHAN="CHANNEL_SANDBOX"
TRAN="1"
QMGR="QM_SANDBOX"/>
<Resource
name="jms/SandboxQ"
auth="Container"
type="com.ibm.mq.jms.MQQueue"
factory="com.ibm.mq.jms.MQQueueFactory"
description="JMS Queue"
QU="SANDBOX_Q"/>
My next step is to get this to work with SSL channels. I understand the part that involves setting up the keystores (kdb file and cert generation and exchanging), configuring the SSL channels on the QM etc. I have all that working already. How do I get tomcat to use my keystore, cipher suite etc? Pointers or a working example would be great!
Note: I am using Spring Integration 4.2, Websphere MQ v8, Tomcat v9, currently.
I must add that I did try everything without the JNDI first. So here's my spring jms non-ssl config without the JNDI, that works:
<bean id="mq-jms-cf-sandbox"
class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory">
<ref bean="mqQueueConnectionFactory" />
</property>
</bean>
<bean id="mqQueueConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="hostName" value="localhost" />
<property name="port" value="1414" />
<property name="queueManager" value="QM_SANDBOX" />
<property name="transportType" value="1" />
<property name="channel" value="CHANNEL_SANDBOX" />
</bean>
<bean id="jms-destination-sandbox" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="SANDBOX_Q" />
<property name="baseQueueManagerName">
<value>QM_SANDBOX</value>
</property>
<property name="baseQueueName">
<value>SANDBOX_Q</value>
</property>
</bean>
I think I finally figured out how to pull this off... here's a brief description of the steps. If you need more details let me know.
Pre-reqs:
Websphere MQ Server installed (at least v 8.0.0.2)
Configure the QM, SSL and non-SSL channels, create Qs and all that good stuff you need.
Needless to say, you need the Websphere MQ jars. Be mindful of any licensing restrictions.
Step 1: Get the direct connection working with no SSL, no JNDI. You will need to use these beans to configure your spring based JMS listeners and JMS Templates etc.
<bean id="mq-jms-cf-sandbox"
class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory">
<ref bean="mqQueueConnectionFactory" />
</property>
</bean>
<bean id="mqQueueConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="hostName" value="localhost" />
<property name="port" value="1414" />
<property name="queueManager" value="QM_SANDBOX" />
<property name="transportType" value="1" />
<property name="channel" value="NON_SSL_CHANNEL" />
</bean>
<bean id="jms-destination-sandbox" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="SANDBOX_Q" />
<property name="baseQueueManagerName">
<value>QM_SANDBOX</value>
</property>
<property name="baseQueueName">
<value>SANDBOX_Q</value>
</property>
</bean>
Step 2: Get the direct connection working with SSL, no JNDI. I found setting this up a little tricky.
2a. Since I was using a non-IBM JRE, I had to make sure the cipher specs & cipher suites needed to be configured according to the mappings specified here:
http://www-01.ibm.com/support/docview.wss?uid=swg1IV66840
This obviously means that we at least have to have our Websphere MQ upgraded to 8.0.0.2. In my case I used ECDHE_RSA_AES_256_GCM_SHA384 on the SSL channel and configured the jms beans within application to use TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, like so:
<bean id="mq-jms-cf-sandbox"
class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory">
<ref bean="mqQueueConnectionFactory" />
</property>
</bean>
<bean id="mqQueueConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="hostName" value="localhost" />
<property name="port" value="1414" />
<property name="queueManager" value="QM_SANDBOX" />
<property name="transportType" value="1" />
<property name="channel" value="SSL_CHANNEL" />
<property name="SSLCipherSuite" value="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"/>
</bean>
<bean id="jms-destination-sandbox" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="SANDBOX_Q" />
<property name="baseQueueManagerName">
<value>QM_SANDBOX</value>
</property>
<property name="baseQueueName">
<value>SANDBOX_Q</value>
</property>
</bean>
2b. Create certs, keystores (kdbs), exchange certs etc. There are many ways to do this. But be mindful that you will need to stash passwords, the key label for the queue manager must be ‘ibmwebspheremqqmgr’ – all in lower case, no spaces, (without quotes), the key label must be like ‘ibmwebspheremquserid’ – all in lower case, no spaces, (without quotes) where userid is the userid that runs tomcat. If you need more details on exactly how I did it using self signed certs, let me know.
2c. Now you have to get the JVM that tomcat runs, to read your keystores. There are many ways but here's how I did it:
Create a setenv.bat file in the tomcat bin folder, with the following contents (debugging SSL is optional)
set JAVA_OPTS="-Djavax.net.ssl.trustStore=C:\path-to-keystore\key.jks" "-Djavax.net.ssl.trustStorePassword=topsecret" "-Djavax.net.ssl.keyStore=C:\path-to-keystore\key.jks" "-Djavax.net.ssl.keyStorePassword=topsecret" "-Djavax.net.debug=ssl" "-Dcom.ibm.mq.cfg.useIBMCipherMappings=false"
2d. Start tomcat using the following command:
catalina.bat run > ..\logs\tomcat.log 2>&1
To stop, just press ctrl+c (on windows). Whichever way you do it, make sure that setenv.bat is used during start up. Or use JAVA_OPTS to set the keystore properties.
2e. Verify that the using the SSL channel works.
Step 3: Get a JNDI connection working with non-SSL, JNDI
There are many was to set up JNDI on tomcat. Here's how I did it: Within the web application create a file META-INF/Context.xml with the following contents:
<Resource
name="jms/qcf_sandbox"
auth="Container"
type="com.ibm.mq.jms.MQQueueConnectionFactory"
factory="com.ibm.mq.jms.MQQueueConnectionFactoryFactory"
description="JMS Queue Connection Factory for sending messages"
HOST="localhost"
PORT="1414"
CHAN="NON_SSL_CHANNEL"
TRAN="1"
QMGR="QM_SANDBOX"/>
<Resource
name="jms/SandboxQ"
auth="Container"
type="com.ibm.mq.jms.MQQueue"
factory="com.ibm.mq.jms.MQQueueFactory"
description="JMS Queue"
QU="SANDBOX_Q"/>
Now in your spring config, instead of the direct configurations, all you have to do is:
<jee:jndi-lookup id="mq-jms-cf-sandbox" jndi-name="java:/comp/env/jms/qcf_sandbox" resource-ref="false" />
<jee:jndi-lookup id="jms-destination-sandbox" jndi-name="java:/comp/env/jms/SandboxQ" resource-ref="false" />
Note that for brevity, I just didn't use resource references. In case you do, there a few additional steps which are straight forward.
Step 4: Now the final step is to use an SSL channel and JNDI. Assuming you have done step 2, this is easy. Modify the META-INF/Context.xml with the following contents:
<Resource
name="jms/qcf_sandbox"
auth="Container"
type="com.ibm.mq.jms.MQQueueConnectionFactory"
factory="com.ibm.mq.jms.MQQueueConnectionFactoryFactory"
description="JMS Queue Connection Factory for sending messages"
HOST="localhost"
PORT="1414"
CHAN="SSL_CHANNEL"
TRAN="1"
QMGR="QM_SANDBOX"
SCPHS="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"/>
<Resource
name="jms/SandboxQ"
auth="Container"
type="com.ibm.mq.jms.MQQueue"
factory="com.ibm.mq.jms.MQQueueFactory"
description="JMS Queue"
QU="SANDBOX_Q"/>
Note the line with SCPHS="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384". If you need to set other such parameters, see the "Short Form" column in this link:
https://www.ibm.com/support/knowledgecenter/SSFKSJ_8.0.0/com.ibm.mq.ref.dev.doc/q111800_.htm%23jm10910_?lang=en
Hopefully all this works for you. Good luck!
Once this configuration works, sending messages is pretty straight forward. But this is how you can listen for a message on a queue using Spring JMS
Reference: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html
Step 1: Use Spring's DefaultMessageListenerContainer and configure your beans in an xml file like so (spring-beans.xml):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener" />
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="mq-jms-cf-sandbox"/>
<property name="destination" ref="jms-destination-sandbox"/>
<property name="messageListener" ref="messageListener" />
</bean>
</beans>
Step 2: Add this to your web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/context/spring-beans.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Step 3: Write a Message Listener class like so:
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class ExampleListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
}
catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
else {
throw new IllegalArgumentException("Message must be of type TextMessage");
}
}
}
Alternatively, instead of step 3, if you are using spring integration, you can do something like so:
<int:channel id="jms-inbound"/>
<int-jms:message-driven-channel-adapter
id="jms-inbound-adapter" container="jmsContainer" channel="jms-inbound"
extract-payload="true" acknowledge="transacted"
message-converter="messagingMessageConverter" />
<beans:bean id="messagingMessageConverter" class="org.springframework.jms.support.converter.MessagingMessageConverter">
</beans:bean>

JNDI binding in JBOss for MQ

I need a little bit of help in configuring JBoss to work with MQ. I have created initial context in MQ using IBM MQ Explorer and have given a local directory for all bindings like file:/C:/jndi. I have created a connection factory for this initial context. Now JBoss documentation says to bind like this
<connection-definition class-name="com.ibm.mq.connector.outbound.ManagedConnectionFactoryImpl"
jndi-name="java:jboss/MQ.CONNECTIONFACTORY.NAME"
pool-name="MQ.CONNECTIONFACTORY.NAME">
I think I am missing some point here. How do I tell Jboss that my InitialContext bindings are in a directory. I have tried most of the combinations. May be I am not getting the concept right. Any pointers ?
When I try to access this MQ.CONNECTIONFACTORY.NAME from a test servlet I wrote I get javax.naming.NameNotFoundException . If I follow same steps in Java SE environment I am successfully able to establish a connection. I am new to application servers and the question might be naive
Regards
The description of the resources created via MQExplorer suggest that these have been put into JNDI backed by a File System context. This is perfectly fine, but what in theory needs to be done now is get JBOSS to read objects out that JNDI context rather than the usual JNDI provider provided by JBOSS. The settings that are in the connection definition extract are using the standard JBOSS JNDI context.
As an example of using JBOSS with the WebSphere MQ Resoruce Adapter have a look here http://pic.dhe.ibm.com/infocenter/wmqv7/v7r5/topic/com.ibm.mq.dev.doc/q031810_.htm
This links to an example set of definitions that store WMQ JMS administered objects in the JBOSS JNDI context.
This is an important question. I have used Spring for this, like this:
<util:properties id="remoteEnv">
<prop key="java.naming.provider.url">file:${my.config.path}/bindings</prop>
<prop key="java.naming.factory.url.pkgs">org.jboss.naming:org.jboss.naming.remote.client</prop>
<prop key="java.naming.factory.initial">com.sun.jndi.fscontext.RefFSContextFactory</prop>
<prop key="java.naming.security.principal">${mdb.user.name}</prop>
<prop key="java.naming.security.credentials">${mdb.user.pass}</prop>
</util:properties>
<bean id="remoteJNDITemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment" ref="remoteEnv" />
</bean>
<bean id="remoteJmsDestinationResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver">
<property name="jndiTemplate" ref="remoteJNDITemplate" />
<property name="cache" value="true" />
</bean>
<jee:jndi-lookup id="senderQueue" jndi-name="MY_QUEUE_NAME" environment-ref="remoteEnv" />
<bean id="xamqconnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="remoteJNDITemplate"/>
</property>
<property name="jndiName" value="MYCONNECTIONFACTORYJNDINAME"/>
<property name="lookupOnStartup" value="false" />
<property name="proxyInterface" value="javax.jms.XAQueueConnectionFactory" />
</bean>
<bean id="xaMQSenderJMSTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<ref bean="xamqconnectionFactory" />
</property>
<property name="pubSubDomain">
<value>false</value>
</property>
<property name="defaultDestination">
<ref bean="senderQueue" />
</property>
<property name="destinationResolver" ref="remoteJmsDestinationResolver" />
</bean>
however using the configuration above we bypass the resource adapter. That's no problem otherwise but it prevents transactions from joining the JBoss transaction, so JMS messages are send immediately, not with transaction commit. I haven't found a fix for that yet.
com.sun.jndi.fscontext.RefFSContextFactory, that is used to read .bindings file, can be found at this dependency:
<dependency>
<groupId>com.sun.messaging.mq</groupId>
<artifactId>fscontext</artifactId>
<version>4.6-b01</version>
</dependency>

Spring JTA transaction manager with Bitronix jndi lookup failures

I have a spring JTA transaction manager configured for Bitronix running on Tomcat 7. The transaction manager starts properly and the application works fine. I however get a lot of debug messages stating that
No JTA TransactionManager found at fallback JNDI location [java:comp/TransactionManager]
No JTA TransactionManager found at fallback JNDI location [java:pm/TransactionManager]
No JTA TransactionManager found at fallback JNDI location [java:appserver/TransactionManager]
etc..
I understand that this is the JtaTransactionManager scanning known jndi locations and this is not an error but a debug exception. The following is my transaction manager configuration
<bean id="BitronixTransactionManager" factory-method="getTransactionManager" class="bitronix.tm.TransactionManagerServices"
destroy-method="shutdown" />
and the jta transaction manager
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="autodetectTransactionManager" value="false"/>
<property name="transactionManager" ref="BitronixTransactionManager" />
<property name="userTransaction" ref="BitronixTransactionManager" />
I thought that setting the autodetectTransactionManager value to false would prevent this scanning and the resulting debug exceptions but it seems this setting has no effect.
I also tried with the following properties for the transactionManager to set the jndi location.
<property name="transactionManagerName" value="java:comp/TransactionManager"/>
<property name="userTransactionName" value="java:comp/UserTransaction"/>
I am using jta 1.1 and also get the same lookup message for the TransactionSynchronizationRegistry:
DEBUG [main] JtaTransactionManager.findTransactionSynchronizationRegistry(146) | No JTA TransactionSynchronizationRegistry found at default JNDI location [java:comp/TransactionSynchronizationRegistry]
I would really like to understand this and not merely filter this out with my Log4J
Probably the problem is that in Tomcat the default jndi locations are in in java:comp/env. So you should do
<property name="transactionManagerName" value="java:comp/env/TransactionManager"/>
<property name="transactionSynchronizationRegistryName" value="java:comp/env/TransactionSynchronizationRegistry"/>
<property name="userTransactionName" value="java:comp/env/UserTransaction"/>

Spring JNDI datasource not recognized after upgrade to ActiveMQ 5.6.0

I tested a ActiveMQ 5.5.0 (fuse version) app in AMQ 5.6.0 and noticed that our Spring JNDI configured Oracle datasources aren't being found.
The only thing I changed in my applications was the pom.xml versions of AMQ/Spring (to match the 5.6 versions). Otherwise, I'm using the identical application code and configuration (activemq.xml, jndi.xml, etc), but my Spring JDBC DAOs (v3.0.5) are failing to find them.
No errors in the logs otherwise, just this Spring Application Context initialization error...
javax.naming.NameNotFoundException; remaining name 'jdbc/myDataSource'
here is the relevant Spring jndi config (conf/jndi.xml, included in conf/activemq.xml)...
<bean id="jndi" class="org.apache.xbean.spring.jndi.SpringInitialContextFactory"
factory-method="makeInitialContext" scope="singleton">
<property name="entries" ref="jndiEntries" />
</bean>
<util:map id="jndiEntries">
<entry key="jdbc/myDataSource">
<bean id="myDBCPDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.OracleDriver" />
...
then my application references it like this...
<bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/myDataSource</value>
</property>
</bean>
<bean id="messageDAO" class="com.mycompany.MessageDAOImpl">
<property name="dataSource" ref="myDataSource" />
</bean>
That said, I tested without using JNDI (instead just hardcoded the datasource in my app) and everything works as expected. So that should rule out everything except the Spring JNDI registration/lookup of the datasource, etc.
So, what am I missing?
ActiveMQ has a dependency into xbean-spring, which you are using as a JNDI provider. It is likely that the transitive Xbean dependency has changed because of the upgrade to ActiveMQ 5.6.0.
I found the issue, I added a jndi.properties file under the /conf directory containing the following and it works fine now (didn't need this under AMQ 5.5...strange)...
java.naming.factory.initial = org.apache.xbean.spring.jndi.SpringInitialContextFactory

Start the H2 database in server mode via Spring

I'm trying to start the H2 database in server mode (I want it to run in a different process) via Spring.
Currently I'm using java Runnable.exec to start the h2 database (using the command: "java -cp h2.jar org.h2.tools.Server")
I know that there is a way to do it via Spring. I tried to add the following to the spring configuration, but it didn't work (it didn't start the H2 database):
<bean id="org.h2.tools.Server" class="org.h2.tools.Server"
factory-method="createTcpServer" init-method="start" destroy-method="stop">
<constructor-arg value="-tcp,-tcpAllowOthers,true,-tcpPort,8043" />
</bean>
<bean id="org.h2.tools.Server-WebServer" class="org.h2.tools.Server"
factory-method="createWebServer" init-method="start">
<constructor-arg value="-web,-webAllowOthers,true,-webPort,8082" />
</bean>
I would appreciate any help/ideas
Do you happen to have:
<beans default-lazy-init="true" ...
in your Spring configuration files?
Recently I had to do the same configuration to make unit test and check data, this works for me (Spring 3.1.4). Then you just have to connect with jdbc:h2:tcp://localhost:8043/mem:test and make sure to put a while(true){} at the end of your test.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.h2.Driver"/>
<!--property name="url" value="jdbc:h2:mem:;TRACE_LEVEL_FIlE=4"/-->
<property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean class="org.h2.tools.Server" factory-method="createTcpServer" init-method="start" destroy-method="stop">
<constructor-arg>
<array>
<value>-tcp</value>
<value>-tcpAllowOthers</value>
<value>-tcpPort</value>
<value>8043</value>
</array>
</constructor-arg>
</bean>
Are you sure the createTcpServer method in the Server class is really called? Have you tried setting up a breakpoint there?
H2 tutorial claims this how you can create and start the server programmatically:
import org.h2.tools.Server;
...
// start the TCP Server
Server server = Server.createTcpServer(args).start();
...
// stop the TCP Server
server.stop();
Your Spring definition seems to mimick the same initialization. But you can always try doing it manually - maybe it's some fault in Spring configuration.
EDIT:
I've tried your configuration and it works for me. What makes you think the server is not started? It doesn't print out anything on stdout, however the process listens at the 8043 port. So it seems pretty OK.

Resources