Wildfly 10.1.0 Singleton doesn't work - java

I've two Wildfly server configured in a domain, and I need to make a singleton that runs with HA. I need it to run only in one server, and if that server fails it should be started in the slave server.
I'm using the defult configuration and I only created the “/META-INF/singleton-deployment.xml” in my WAR.
When I deploy the WAR it starts in both servers! Not only in one. What is missing? Do I need to edit something in domain.xml?
My singleton only writes a text to the log file and console, just for testing:
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.ejb.Schedule;
import javax.ejb.Singleton;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
#ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
#Singleton
public class TesteAPP {
final Logger logger = LogManager.getLogger(TesteAPP.class);
#Schedule(second = "*/1", minute = "*", hour = "*", persistent = false)
public void executaTarefa() {
try {
logger.log(Level.INFO, "Tarefa executada com sucesso! Nome da máquina: {} Endereço da máquina: {}",
InetAddress.getLocalHost().getHostName(),
InetAddress.getLocalHost().getHostAddress());
System.out.println(String.format("Tarefa executada com sucesso! Nome da máquina: %s Endereço da máquina: %s",
InetAddress.getLocalHost().getHostName(),
InetAddress.getLocalHost().getHostAddress()));
} catch (UnknownHostException e) {
logger.log(Level.ALL, "Tarefa executada com sucesso!");
System.out.println(String.format("Tarefa executada com sucesso!"));
}
}
}

Was answered in https://stackoverflow.com/a/27956003/653069
As per EJB 3.1 spec, the #Singleton is only per-JVM and not per-cluster.
In cases where the container is distributed over many virtual machines, each application will have one bean instance of the Singleton for each JVM.
Nevertheless, you can use JBoss specific way. Take a look into the cluster-ha-singleton quickstart - EAP 7.0 one, which should work on WildFly 10.x too.

Related

Redhat Fuse-Karaf refusing to inject jms ConnectionFactory

Fuse karaf fuse-karaf-7.11.1.fuse-7_11_1-00013-redhat-00003.
I am creating a simple bridge from servlet to ActiveMQ 5.9 using amqp protocol. I managed to configure a ConectionFactory and tested OK with the jms:send command. I wrote a JMS service which is responding to the POST from the servlet side, but is failing to create the Connection factory.
admin#root()> jms:connectionFactories
JMS Connection Factory
jms/artemis
The Jms service code is:
package com.mycompany.jms;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TemporaryQueue;
import javax.jms.TextMessage;
import javax.naming.*;
import org.apache.aries.blueprint.annotation.service.Reference;
import org.apache.aries.blueprint.annotation.service.Service;
import org.apache.aries.blueprint.annotation.service.ServiceProperty;
import com.mycompany.JmsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Service(classes = JmsService.class, properties = {
// Only necessary for Remote Services
#ServiceProperty(name = "service.exported.interfaces", values = "*") })
#Singleton
public class JmsService4Reals implements JmsService {
private static final Logger LOG = LoggerFactory.getLogger(JmsService4Reals.class);
#Reference
ConnectionFactory connectionFactory;
#Override
public String sendMessage(String opeCod, String message) {
LOG.info(String.format("received: opeCod=%s, msg=%s", opeCod, message));
try {
if(connectionFactory== null)
return "Reference: no connectionFactory found";
Connection connection = null;
try {
connection = connectionFactory.createConnection();
LOG.info("JMS Connection created=%s", connection);
connection.start();
} catch (JMSException e) {
if (connection != null)
connection.stop();
return prepareErrorResponse(e.getMessage());
}
} catch (JMSException e) {
return prepareErrorResponse(e.getMessage());
}
}
private String prepareErrorResponse(String msg) {
return msg;
}
}
Please help, i'm stuck with no progress
The servlet always reponds with "Reference: no connectionFactory found"
I also tried with the JNDI lookup method with the same result.
try {
Context context = new InitialContext();
LOG.info("context=%s", context);
connectionFactory = (ConnectionFactory) context.lookup("jms/artemis");
LOG.info("connectionFactory=%s", connectionFactory);
}catch(Exception e) {
e.printStackTrace();
return "no connectionFactory found on JNDI";
}
I expect the jms/artemyour textis to be injected on my ConnectionFactory, but never occurs.
The actual exception you get when calling context.lookup() is:
javax.naming.NoInitialContextException: \
Need to specify class name in environment or system property, \
or as an applet parameter, or in an application resource file: \
java.naming.factory.initial
That's how JNDI works and you need special preparation to use it in OSGi (and Fuse Karaf is OSGi runtime based on Apache Karaf).
You have to install jndi feature first. Then your exception will be:
javax.naming.NotContextException: jms/artemis
However it's almost everything you need. jndi feature gives you several commands, like this one:
karaf#root()> jndi:names
JNDI Name │ Class Name
─────────────────────────┼─────────────────────────────────────────────────────────────────
osgi:service/jms/artemis │ org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory
osgi:service/jndi │ org.apache.karaf.jndi.internal.JndiServiceImpl
If you now use osgi:service/jms/artemis instead of just jms/artemis, you get proper connection factory. I got this in logs:
2023-01-02 09:45:53,412 INFO {XNIO-2 task-1} [grgr.test.Activator7$1.doGet()] \
(Activator7.java:65) : connectionFactory=ActiveMQConnectionFactory [serverLocator=ServerLocatorImpl \
[initialConnectors=[TransportConfiguration(name=null, factory=org-apache-activemq-artemis-core-remoting-impl-netty-NettyConnectorFactory) \
?port=61616&host=localhost], discoveryGroupConfiguration=null], \
clientID=null, consumerWindowSize = 1048576, \
dupsOKBatchSize=1048576, transactionBatchSize=1048576, readOnly=falseEnableSharedClientID=false]
You can find more examples of persistence usage in Fuse Karaf here: https://github.com/jboss-fuse/karaf-quickstarts
A developer documentation is here: https://github.com/jboss-fuse/karaf-quickstarts/tree/7.x.redhat-7-11-x/persistence/manual/src/main/asciidoc

Error while trying to send message to WMQ through java

I am trying to connect to a QM and send message to one of the queues, using below code:
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.JMSException;
import com.ibm.msg.client.commonservices.*;
import com.ibm.mq.commonservices.internal.trace.*;
import com.ibm.msg.client.jms.JmsConnectionFactory;
import com.ibm.msg.client.jms.JmsFactoryFactory;
import com.ibm.msg.client.wmq.WMQConstants;
public class MQConnect {
public static void main(String[] args) {
Connection connection = null;
Session session = null;
Destination destination = null;
Destination tempDestination = null;
MessageProducer producer = null;
MessageConsumer consumer = null;
try {
System.out.println(WMQConstants.WMQ_PROVIDER);
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf = ff.createConnectionFactory();
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, "hostname");
cf.setIntProperty(WMQConstants.WMQ_PORT, port);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, "channel");
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, "QMName");
connection = cf.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("test_java_q");
producer = session.createProducer(destination);
long uniqueNumber = System.currentTimeMillis() % 1000;
TextMessage message = session.createTextMessage(" " + uniqueNumber);
connection.start();
producer.send(message);
}catch (JMSException jmsex) {
System.out.println(jmsex.getErrorCode());
System.out.println(jmsex.getLinkedException().getCause());
System.out.println(jmsex.getMessage());
}
}
}
The error I'm getting is :
Exception in thread "main" java.lang.NoClassDefFoundError:
com/ibm/msg/client/commonservices/trace/Trace
at com.ibm.msg.client.jms.JmsFactoryFactory.<clinit>(JmsFactoryFactory.java:54)
at MQConnect.main(MQConnect.java:33)
Caused by: java.lang.ClassNotFoundException: com.ibm.msg.client.commonservices.trace.Trace
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more
I have the required jar files but I believe something is still missing from project references.
I have checked the jars and class specified in error is present in one of the jar files.
These are the jar files I have referenced:
com.ibm.mq.headers.jar
com.ibm.mq.jar
com.ibm.mq.pcf.jar
com.ibm.mq.tools.ras.jar
jms.jar
com.ibm.mqjms.jar
com.ibm.mq.jmqi.jar
com.ibm.mq.commonservices.jar
There are also several other jars which are not com.ibm.*
Any suggestions?
You really shouldn't be picking and choosing MQ JAR files. For IBM MQ v8.0 or higher, you should be using the com.ibm.mq.allclient.jar and jms.jar files. That's it, just 2 files. IBM has posted the com.ibm.mq.allclient.jar file on Maven (and use the most recent one). You can find com.ibm.mq.allclient.jar here and jms.jar from here.
If you are using IBM MQ v7.5 or lower (which all releases are out of support) then the correct JAR files are:
connector.jar
com.ibm.mq.jar
com.ibm.mq.commonservices.jar
com.ibm.mq.headers.jar
com.ibm.mq.jmqi.jar
com.ibm.mq.pcf.jar
com.ibm.mqjms.jar
jms.jar
fscontext.jar
jndi.jar
jta.jar
ldap.jar
Also, do NOT mix and match JAR files between different releases of IBM MQ. It will cause you all kinds of headaches.
Finally, what is this line:
destination = session.createQueue("test_java_q");
It should be:
destination = context.createQueue("queue:///MY.TEST.QUEUE");
where 'MY.TEST.QUEUE' actually exists in the queue manager. All MQ objects are case sensitive and MQ Best Practises says to use uppercase object names. i.e. avoid lowercase names.
Updated Feb. 14, 2022.
You need to add some code to your program so we can see what is going on.
(1) Add the following MQLevel class (taken from my open source project Universal File Mover) to your project:
public class MQLevel extends MQJavaLevel
{
/**
* The constructor
*/
public MQLevel()
{
super();
}
/**
* Get the IBM name for the MQ JAR class.
* #return
*/
public String getName()
{
return queryValue(0);
}
/**
* Get the version of the MQ JAR class.
* #return
*/
public String getVersion()
{
return queryValue(1);
}
}
(2) Next add the following lines of code in your main method or at the very top of your code:
System.out.println(System.getProperty("java.class.path").replace(';','\n'));
try
{
MQLevel mql = new MQLevel();
System.out.println("MQ JAR Version = " + mql.getName()+" V"+mql.getVersion());
}
catch(Exception e)
{
e.printStackTrace();
}
Note: If you are on Linux/Unix rather than Windows then change the ';' (semi-colon) in the replace method to a ':' (colon).
i.e.
System.out.println(System.getProperty("java.class.path").replace(':','\n'));
Run your program with the updates, then update the original posting both with the new output.

MessageDrivenBean isnt handle messages [Wildfly]

Im trying to develop a "Message Driven Bean" to handle all the local ActiveMQ messages, but it's the first time that i try to do something like this.
The most part of the material that i found explain how to write a MDB using JBOSS server, in this case there's a xml file with some queue information, but in all wildfly tutorials there's no mention to any kind of configuration like that.
I have the following scenario:
A simple java project like message producer
An ActiveMQ instance running local
An EJB project deployed into Wildfly 10
My producer project is able to send messages to ActiveMQ queue, this part its working,but my EJB project just have a single class called TestMDBHandle with #MessageDriven annotation. Is this enough to receive my queue messages? Because the MDB isnt working, i imagine must be a kind of configuration or property in EJB to specify the host of the message-broker.
My message producer:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class MessageSender {
public static void main(String args[]) throws NamingException, JMSException {
MessageSender sender = new MessageSender();
sender.sender();
}
public void sender() throws NamingException, JMSException {
InitialContext jndi = null;
Session session = null;
Connection connection = null;
try {
jndi = new InitialContext();
ConnectionFactory factory = (ConnectionFactory)jndi.lookup("connectionFactory");
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = (Destination)jndi.lookup("MyQueue");
MessageProducer producer = session.createProducer(destination);
TextMessage mensagem = session.createTextMessage("Eu enviei uma mensagem!");
producer.send(mensagem);
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
connection.close();
jndi.close();
}
}
}
My jms properties located inside my producer project
java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url=tcp://localhost:61616
connectionFactoryNames = connectionFactory, queueConnectionFactory, topicConnectionFactory
queue.MyQueue=jms/myqueue
Finally, my ejb project have this single class, without any kind of property file or xml.
package br.com.jms.mdb;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
#MessageDriven(name = "meuHandler", activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/myqueue") })
public class Teste implements MessageListener {
#Resource
private MessageDrivenContext mdctx;
public Teste() {
}
#Override
public void onMessage(Message message) {
TextMessage objectMessage = null;
try {
objectMessage = (TextMessage)message;
System.out.println("Achei a mensagem : " + objectMessage.getText().toString());
}catch(JMSException e) {
e.printStackTrace();
}
}
}
Maybe you can provide a little more information such as the xml file with the queue information and the annotation properties of the MDB? Because it sounds you are heading in the right direction. The two main things:
You have to specify the exact queue that the MDB is listening to, for example through the properties of the #MessageDriven annotation (such as "name", "mappedName", "activationConfig"). And of course override the onMessage() method to process the messages.
You also have to make sure that this specific queue is available as a resource for your application. You have to provide jms configuration for this, which also defines the resource type (Queue or Topic). From your question I can't tell which of these steps you have (partly) completed.

loading Glassfish JMS jars dynamically at runtime throws a NoClassDefFoundError javax/jms/Destination

I'm desperate for any help on this issue as i'm stuck for a while now.
Part of my application using Glassfish JMS to transfer messages, the application works smoothly om my development machine if the jars are included, now i have the need to load the JMS jars dynamically on runtime, so i used the following code to load the jars in the classloader at runtime
public class ClassPathHack {
private static final Class<?>[] parameters = new Class[]{URL.class};
public static synchronized void addFile(String s) throws IOException, Exception {
File f = new File(s);
try {
java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
java.net.URL url = f.toURI().toURL();
for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
if (it.equals(url)){
return;
}
}
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true);
method.invoke(loader, new Object[]{url});
} catch (final java.lang.NoSuchMethodException |
java.lang.IllegalAccessException |
java.net.MalformedURLException |
java.lang.reflect.InvocationTargetException e){
throw new Exception(e);
}
}
}
and in the main method i've loaded all the JMS used jars before raising the thread that connect to JMS using the following code:
public static void loadGlassfishJars(){
try {
System.out.println("DEBUGGNG: Loading Glassfish jars");
ClassPathHack.addFile(glassfishPath + "\\glassfish\\lib\\appserv-rt.jar");
ClassPathHack.addFile(glassfishPath + "\\glassfish\\modules\\console-jms-plugin.jar");
ClassPathHack.addFile(glassfishPath + "\\glassfish\\lib\\gf-client.jar");
ClassPathHack.addFile(glassfishPath + "\\mq\\lib\\imq.jar");
ClassPathHack.addFile(glassfishPath + "\\mq\\lib\\imqbroker.jar");
ClassPathHack.addFile(glassfishPath + "\\glassfish\\lib\\install\\applications\\jmsra\\imqjmsra.jar");
ClassPathHack.addFile(glassfishPath + "\\glassfish\\lib\\javaee.jar");
ClassPathHack.addFile(glassfishPath + "\\glassfish\\modules\\jms-admin.jar");
ClassPathHack.addFile(glassfishPath + "\\glassfish\\modules\\jms-core.jar");
ClassPathHack.addFile(glassfishPath + "\\mq\\lib\\jms.jar");
ClassPathHack.addFile(glassfishPath + "\\glassfish\\modules\\jmxremote_optional-repackaged.jar");
} catch (Exception ex) {
Logger.getLogger(ZMQTranslator.class.getName()).log(Level.SEVERE, null, ex);
}
and the main method is something like the following:
public static void main(String[] args) {
//loading glassfish jms jars
loadGlassfishJars();
//JMS connector thread
System.out.println("DEBUGGNG:initiating ZMQConnectionReceiver");
ZMQConnectionReceiver zmqCon = new ZMQConnectionReceiver();
Thread t = new Thread(zmqCon);
t.start();
}
the exception is thrown while calling the constructor of ZMQConnectionReceiver class, which imports the following classes
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
//NOT RELATED IMPORTS
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.json.Json;
import model.DeviceConnection;
import org.zeromq.ZMQ;
import javax.json.stream.*;
and it has a simple constructor as follows:
public ZMQConnectionReceiver(){
lostNFound = new ConcurrentLinkedDeque();
}
The exception is thrown when calling the constructor
Exception in thread "main" java.lang.NoClassDefFoundError:
javax/jms/Destination
I've checked the existence of Destination Class in jms.jar and it's there.
Note. Glassfish JMS has a weird behavior, it requires that the application connecting to it should have the JARs installed with the same Glassfish version you are trying to connect to, any other jars will fail even if it was from the same Glassfish version but other installation.
How can I solve this issue?
I managed to work around this issue by downloading all the JMS dependencies JARs by adding the following dependency
<dependency>
<groupId>org.glassfish.main.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.1.2</version>
</dependency>
to the pom file of a maven application and building the application.
after that I included the downloaded JARs in my application and it worked fine as a standalone application on any machine.

Add manually mappedName in Message Driven Bean at runtime

I need to make simple Message Driven Bean that will listen on dynamically added queue locataions. I have tried few ways to implement this, but none of them worked. I have appplication that uses esb and java message queues, and I'm trying to read queue location from config file, during the runtime, and thus tell my message driven bean what is the queue on which to listen. I am not either sure that this is possible.
I also tried to implement message listener, but because I have to use ejb module, and ejb module does not support main method, it requires his own container (like message driven bean), I don't know what to use instead of main method to achive the same goal. I am not able to use session beans because I need to achieve asynchronous communication between client and service.
I also tried to use client application (although it is not one of the options), but maven project does not support debug and run functions for this type of application in netbeans.
Does anyone know any solution for this problem, or at least have some idea?
This may not be the best solution, but it is possible to receive and process JMS messages asynchronously with a Stateful Session Bean doing something like this:
package com.example.statefuljms;
import javax.annotation.Resource;
import javax.ejb.Local;
import javax.ejb.Stateful;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
#Stateful
#Local(MessageReceiverLocal.class)
public class MessageReceiver implements MessageReceiverLocal, MessageListener {
#Resource(mappedName = "ConnectionFactory")
private ConnectionFactory connectionFactory;
private QueueConnection connection;
#Override
public void start(String queueName) throws JMSException, NamingException {
Context initialContext = new InitialContext();
connection = (QueueConnection) connectionFactory.createConnection();
QueueSession session = (QueueSession) connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) initialContext.lookup(queueName);
QueueReceiver receiver = session.createReceiver(queue);
receiver.setMessageListener(this);
connection.start();
}
#Remove
#Override
public void stop() throws JMSException {
connection.stop();
connection.close();
}
#Override
public void onMessage(Message message) {
// handle message here
}
}
Use a Singleton to test:
package com.example.statefuljms;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.jms.JMSException;
import javax.naming.NamingException;
#Startup
#Singleton
public class Test {
#EJB
private MessageReceiverLocal messageReceiver;
#PostConstruct
public void run() {
messageReceiver.start("/queue/myQueue");
}
#PreDestroy
public void cleanup() {
messageReceiver.stop();
}
}

Categories

Resources