This project is the Subscriber part of a message-based communication system built in Java using the Java Message Service (JMS) API. It communicates through a central message broker with a corresponding Publisher client.
Message Broker / Server: Payara Server
Java Message Service (JMS) is a Java API that enables distributed communication which is loosely coupled, reliable, and asynchronous. This project implements the Subscriber side of the Publish/Subscribe (Pub/Sub) messaging model using a JMS Topic.
JMS supports two primary messaging models: Point-to-Point (Queue) and Publish/Subscribe (Topic). Understanding the difference is crucial for designing an effective messaging architecture.
The PTP model is built around the concept of a Queue. Messages are sent to a specific queue, and receivers listen to that queue to consume the messages.
- One-to-One Communication: Each message is delivered to exactly one receiver. Even if multiple receivers are listening on the same queue, the message broker ensures that only one of them will process a given message. This model is often used for load balancing, where multiple instances of a service can pull tasks from a shared queue.
- Durable by Default: Messages sent to a queue are stored by the broker until a consumer successfully receives and acknowledges it. If no consumers are active when the message is sent, it will wait in the queue until one becomes available.
- Coupling: The sender and receiver are loosely coupled. The sender only needs to know the name of the queue, not anything about the receiver.
- Order Processing System: When a customer places an order, the order details are sent as a message to a
pending-ordersqueue. One of several order processing worker services picks up the message, processes the order, and updates the database. - Task Scheduling: A web application sends an email confirmation task to a
mail-queue. A separate email service consumes the message and sends the email, preventing the user from having to wait. - Financial Transactions: Ensuring that each payment transaction is processed exactly once.
// --- Queue Sender (Producer) ---
QueueConnectionFactory factory = (QueueConnectionFactory) ic.lookup("myQueueFactory");
QueueConnection connection = factory.createQueueConnection();
QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) ic.lookup("myQueue");
QueueSender sender = session.createSender(queue);
TextMessage message = session.createTextMessage("This is a message for the queue.");
sender.send(message);
connection.close();
// --- Queue Receiver (Consumer) ---
QueueConnectionFactory factory = (QueueConnectionFactory) ic.lookup("myQueueFactory");
QueueConnection connection = factory.createQueueConnection();
connection.start();
QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) ic.lookup("myQueue");
QueueReceiver receiver = session.createReceiver(queue);
Message receivedMessage = receiver.receive(); // Blocks until a message is available
System.out.println(((TextMessage) receivedMessage).getText());
connection.close();The Pub/Sub model is built around the concept of a Topic. A publisher sends messages to a topic, and the message is broadcast to all active subscribers who have registered interest in that topic.
- One-to-Many Communication: Each message is delivered to all interested subscribers. It's a broadcast mechanism.
- Non-Durable Subscribers (by default): By default, subscribers must be active and connected to receive messages. If a subscriber is offline when a message is published, it will miss that message.
- Durable Subscribers (optional): JMS allows the creation of "durable subscribers." A durable subscriber can disconnect and, upon reconnecting, will receive all the messages that were published while it was offline. This project uses a non-durable subscriber.
- Decoupling: This model offers very high decoupling. The publisher has no knowledge of the number or type of subscribers.
- Live News Feeds: A news agency publishes breaking news alerts to a
news-alertstopic. Multiple subscribers (a mobile app, a website, an email notifier) all receive the alert simultaneously. - Stock Market Tickers: Financial data providers publish real-time stock price updates to a topic. Any number of trading applications or dashboards can subscribe to receive these updates.
- Live Sports Scores: A sports service broadcasts score updates to a topic, and all connected fan applications receive the updates instantly.
- System Monitoring: A central monitoring service publishes system health alerts. Different teams (database, network, application support) can subscribe to these alerts to take appropriate action.
// --- Topic Publisher (Producer) ---
TopicConnectionFactory factory = (TopicConnectionFactory) ic.lookup("myTopicFactory");
TopicConnection connection = factory.createTopicConnection();
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = (Topic) ic.lookup("myTopic");
TopicPublisher publisher = session.createPublisher(topic);
TextMessage message = session.createTextMessage("This is a broadcast message for the topic.");
publisher.publish(message);
connection.close();
// --- Topic Subscriber (Consumer) ---
TopicConnectionFactory factory = (TopicConnectionFactory) ic.lookup("myTopicFactory");
TopicConnection connection = factory.createTopicConnection();
connection.start();
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = (Topic) ic.lookup("myTopic");
TopicSubscriber subscriber = session.createSubscriber(topic);
Message receivedMessage = subscriber.receive();
System.out.println(((TextMessage) receivedMessage).getText());
connection.close();| Feature | Queue (Point-to-Point) | Topic (Publish/Subscribe) |
|---|---|---|
| Delivery Model | One-to-one | One-to-many (broadcast) |
| Message Consumers | Only one consumer gets a specific message | All subscribers get a copy of the message |
| Message Persistence | Messages persist until consumed and acknowledged | Messages are lost for offline non-durable subscribers |
| Main Use Case | Task distribution, load balancing, guaranteed processing | Broadcasting events, notifications, real-time data streams |
| Coupling | Loosely coupled | Very loosely coupled (decoupled) |
Implementation: JMS-FirstClient acts as the message subscriber using a Topic.
- It uses JNDI (
InitialContext) to look up theTopicConnectionFactory(myTopicConnectionFactory) and theTopic(myTopic). - A
TopicConnectionand aTopicSession(non-transacted,AUTO_ACKNOWLEDGE) are created. - It creates a
TopicSubscriberand waits to receive a message. - Once a message arrives, it extracts the string body from the message and prints it to the console.
The corresponding producer/publisher client, JMS-SecondClient, is in a separate repository. You will need to run it to send messages to this subscriber.
Find the producer client here: https://github.com/Sahan-Kaushalya/JMS-SecondClient.git
- Java Development Kit (JDK 17)
- Payara Server (Must be running for the clients to connect and communicate. Ensure resources like
myTopicConnectionFactoryandmyTopicare configured on the server).
If the application does not run normally or throws exceptions on startup, please follow these steps:
Ensure that your installed JDK version is compatible with your version of Payara Server. Mismatched versions can cause unexpected runtime failures. This project is configured for Java 17.
If you are using a modern JDK (Java 16 or newer), you might encounter IllegalAccessError or InaccessibleObjectException due to strict module encapsulation. You need to explicitly grant the application access to internal Java APIs.
Add the following arguments to the VM Options in your IDE's Run Configuration:
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.management/javax.management.openmbean=ALL-UNNAMED --add-opens java.management/javax.management=ALL-UNNAMED
- Start the Payara Server.
- Run this
JMS-FirstClient(Consumer) so it is ready and waiting to receive messages. - Clone and run the
JMS-SecondClient(Producer) from its repository. Start typing messages in its console. - You should see those messages appearing instantly in the console of this
JMS-FirstClient.