Saturday, January 8, 2022

Event Handling in Spring

Spring's event handling is single-threaded so if an event is published,  until and unless all the receivers get the message, the processes are blocked and the flow will not continue. [Synchronous]

Four things necessary for an event:

  • Source : to publish the event in Spring - ApplicationEventPublisher object
  • Event : event must extends from ApplicationEvent // springframework 4.2 + - - any object 
  • Listener : IOC container
  • Handler : Handler method must annotated with @EventListener


Creating, publishing and handling custom events in Spring

Create an event class, `CustomEvent` by extending `ApplicationEvent`. This class must define a default constructor which should inherit constructor from ApplicationEvent class.

import org.springframework.context.ApplicationEvent;
public class CustomEvent extends ApplicationEvent{
    public CustomEvent(Object source) {
        super(source);
   }
   public String toString(){
        return "My Custom Event";
   }
}

Once your event class is defined, you can publish it from any class, let us say `EventClassPublisher` which implements `ApplicationEventPublisherAware`. You will also need to declare this class in XML configuration file as a bean so that the container can identify the bean as an event publisher because it implements the ApplicationEventPublisherAware interface.

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
public class CustomEventPublisher implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;
    public void setApplicationEventPublisher (ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }
    public void publish() {
        CustomEvent ce = new CustomEvent(this);
        publisher.publishEvent(ce);
    }
}

A published event can be handled in a class, let us say `EventClassHandler` which implements `ApplicationListener` interface and implements `onApplicationEvent` method for the custom event.

import org.springframework.context.ApplicationListener;
public class CustomEventHandler implements ApplicationListener<CustomEvent> {
    public void onApplicationEvent(CustomEvent event) {
        System.out.println(event.toString());
    }
}

A main file/controller file to trigger events:

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
       CustomEventPublisher cvp = (CustomEventPublisher) context.getBean("customEventPublisher");
       cvp.publish();
   }
}

Beans.xml for DI:

<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
             xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation = "http://www.springframework.org/schema/beans"
             http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id = "customEventHandler" class = "com.CustomEventHandler"/>
    <bean id = "customEventPublisher" class = "com.CustomEventPublisher"/>
</beans>

In springboot, same architecture is being followed under the hood with annotations.

Wednesday, October 13, 2021

JMS: Deep dive in MQ systems and Roadmap to Kafka

JMS: Java Messaging Service


JMS is an example of Messaging Systems based on asynnchronous design pattern which is used by microservices to interact with each other to send data and events. For introductory part, please refer here.
JMS allow applications to create and send, receive and read messages in a shared environment.

JMS: Programming Model


Basic components to develop client based producer and consumer system are:
  • ConnectionFactory: Use the Java Naming and Directory Interface (JNDI) to find a ConnectionFactory object, or instantiate a ConnectionFactory object directly and set its attributes.
    Based on delivery model, client has separate instance for connection factory to create a connection to a provider:
    • Point to point :  QueueConnectionFactory
    • Publish/subscribe: TopicConnectionFactory
         The following snippet of code demonstrates how to use JNDI to find a connection factory object:

Context ctx = new InitialContext();
ConnectionFactory cf1 = (ConnectionFactory) ctx.lookup("jms/QueueConnectionFactory");
ConnectionFactory cf2 = (ConnectionFactory) ctx.lookup("/jms/TopicConnectionFactory");
        
        Alternatively, you can directly instantiate a connection factory as follows:

ConnectionFactory connFactory = new com.sun.messaging.ConnectionFactory();
QueueConnectionFactory connFactory = new com.sun.messaging.QueueConnectionFactory();
TopicConnectionFactory connFactory = new com.sun.messaging.TopicConnectionFactory();

  • Connection: Use the ConnectionFactory object to create a Connection object. This can be done as follows:
Connection connection = connFactory.createConnection();

        Note that you must close all connections you have created using the Connection.close() method.

  • Session: Use the Connection object to create one or more Session objects, which provide transactional context with which to group a set of sends and receives into an atomic unit of work. A session can be created as follows:
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

        The createSession() method takes two arguments:
    • the first (false in this case) means that the session is not transacted,
    • the second means that the session will automatically acknowledge messages when they have been received successfully.

  • Destination: A destination object is used by the client to specify the source of messages it consumes and the target of messages it produces.
    In the point-to-point messaging - destinations are known as queues,
    In the publish/subscribe model of messaging - destinations are known as topics.




  • Use JNDI to find Destination object(s), or instantiate one directly and configure it by setting its attributes. The following snippet of code demonstrates how to perform a JNDI lookup of a queue named jms/SomeQueue:

    Destination dest = (Queue) ctx.lookup("jms/SomeQueue");

    Or, you can directly instantiate and configure a destination object:

    Queue q = new com.sun.messaging.Queue("world");

  • MessageProducer: Use a Session and a Destination object to create the needed MessageProducer object, which are used for sending messages to a destination.
    Note that you can create a MessageProducer object without specifying a Destination object, but in that case a Destination object must be specified for each message produced.

    MessageProducer producer = session.createProducer(SomeQueue OR SomeTopic);

    Once a producer has been created, it can be used to send messages as follows: producer.send(message);
  •   MessageConsumer: JMS messages can be consumed in two ways:
    • Synchronously: A client(receiver or subscriber) explicitly fetches a message from the destination using the receive method.
      • Use a Session object and a Destination object to create any needed MessageConsumer objects that are used for receiving messages.

        MessageConsumer consumer = session.createConsumer(SomeQueue or SomeTopic);
        connection.start();
        Message msg = consumer.receive(Long timeOut);

      • Once the consumer has been created, it can be used to receive messages. Message delivery, however, doesn't begin until you start the connection created earlier, which can be done by calling the start() method:
    • Asynchronously: A client can register a message listener(like event listener) with a consumer. 
      • Instantiate a MessageListener object and register it with a MessageConsumer object.
      • A MessageListener object acts as an asynchronous event handler for messages. The MessageListener interface contains one method, onMessage(), which you implement to receive and process the messages.

        MessageListener listener = new MyListener();
        consumer.setMessageListener(listener);

      • In order to avoid missing messages, the start() method should be called on the connection after the listener has been registered. When message delivery begins, the JMS provider automatically invokes the message listener's onMessage() whenever a message is delivered.

Implementation

  • Download and install Java System Message Queue. Once installed, the \bin directory contains a utility to install and uninstall the broker as a Window Service (imqsvcadmin). In addition, it contains the executable for the broker (imqbrokerd)
  • To test installation:
    • Run the broker. Go to the bin directory of your installation and run the following command: > imqbrokerd -tty
      The -tty option causes all logged messages to be displayed to the console in addition to the log file.
    • The broker should start and display a few messages before displaying: imqbroker@hostname:7676 ready
  • Test the broker by running the following command in a separate window:
    > imqcmd query bkr -u admin -p admin
    It should display all the details including host, messages count and cluster information.
Sample code(point to point):

import javax.jms.*;

public class TestJms {
   public static void main(String argv[]) throws Exception {
      // The producer and consumer need to get a connection factory and use it to set up a connection and a session
      QueueConnectionFactory connFactory = new com.sun.messaging.QueueConnectionFactory();
      QueueConnection conn = connFactory.createQueueConnection();
      // The session is not transacted, and it uses automatic message acknowledgement
      QueueSession session = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
      Queue q = new com.sun.messaging.Queue("Hello world !!!");
     
      QueueSender sender = session.createSender(q); // Sender
      TextMessage msg = session.createTextMessage(); // Text message
      msg.setText("Hello nik !");
      System.out.println("Sending the message: "+msg.getText());
      sender.send(msg);
      
      QueueReceiver receiver = session.createReceiver(q); // Receiver
      conn.start();
      Message m = receiver.receive();
      if(m instanceof TextMessage) {
         TextMessage txt = (TextMessage) m;
         System.out.println("Message Received: "+txt.getText());
      }
      session.close();
      conn.close();
   }
}

  • Compile TestJms.java:

    > javac -classpath /lib/jms.jar{;|:}/lib/img.jar TestJms.java

    Note: The choice of PATH SEPARATER CHARACTER, {;|:}, is platform dependent.
    • ':' on UNIX/Linux, and
    • ';' on Windows
  • Run: Assuming that the imqbrokerd is still running, run TestJms:

    > java -cp /lib/jms.jar{;|:};/lib/img.jar TestJms TestJms

  • Sample Output:
    > Sending the message: Hello nik !
       Message Received: Hello nik !

Reliable Messaging

JMS defines two delivery modes:
  • Persistent messages: Guaranteed to be successfully consumed once and only once. Messages are not lost.
  • Non-persistent messages: Guaranteed to be delivered at most once. Message loss is not a concern.
This, however, is all about performance trade-offs. The more reliable the delivery of messages, the more bandwidth and overhead required to achieve that reliability. Performance can be maximized by producing non-persistent messages, or you can maximize the reliability by producing persistent messages.

Message-Driven Beans

JMS is a mandatory API and service in J2EE platform. A good example is the message-driven bean, one of a family of EJBs specified in EJB 2.0/2.1. The other two EJBs are session beans and entity beans, which can only be called synchronously.

A JMS Message-Driven Bean (MDB) is a JMS message consumer that implements the JMS MessageListener interface. The onMessage() method is invoked when a message is received by the MDB container. Note that you do not invoke remote methods on MDBs (like with other enterprise beans) and as a result there are no home or remote interfaces associated with them. It also worth noting that with J2EE 1.4, MDBs are not limited to JMS; rather, a multiplicity of MDB interfaces can be declared and consumed by application components implementing those interfaces.

Limitations of JMS:

  • JMS is Java-based. In multi-tiered applications using microservices, where multiple languages and frameworks are used, this can become a hindrance.
  • In JMS, although APIs are specified, the message format is not. This is a limitation of JMS. They just have to use the same API.

An alternate solution to these problems is using Kafka.

JMS and Kafka are both wildly popular solutions for messaging. Whilst JMS has been around for longer it is still a very popular choice for certain use cases. Before you consider which one is better, it is best to do your homework and study the business requirements and your capabilities.

A friendly comparison between JMS and Kafka as follow:

ParameterJMSKafka
Order of MessagesThere is no guarantee that the messages will be received in order.The receiving of messages follows the order in which they are sent to the partition.
FilterThis is a JMS API message selector that allows the consumers to specify which messages they are interested in. This way, message filtering happens in JMS. Message selection can follow specific criteria. The filtering occurs at the producer.There is no concept of the filter at the broker level. Hence, messages picked up by the consumer do not specify any criteria. The filtering can happen only at the consumer level.
Persistence of MessagesIt provides either in- memory or disk-based storage of messages.It stores the messages for a specified period whether or not it has been picked up by the consumer.
Push vs. Pull of MessagesThe providers push the JMS message to queues and topics.The consumers pull the message from the broker.
Load BalancingLoad balancing can be designed by implementing some clustering mechanism. Thus, once the producer sends the messages, the load will be distributed across the clusters.Here load balancing happens automatically. Because once the Kafka nodes publish its metadata that indicates which servers are up and running in the cluster. Also, it tells the producer where the leader is. Thus, the client can send messages to the appropriate partition.

For more information, please visit JMS vs Kafka.

Tuesday, October 12, 2021

Enterprise applications - Need of messaging service and JMS

Why we need messaging system in enterprise applications ?

  • If applications have dependency i.e microservices depends on each other for resources, then we need RPC, hence RPC systems were made.
  • Remote procedure call (RPC) systems, including Java RMI, are synchronous i.e the caller must block and wait until the called method completes execution, and thus offer no potential for developing loosely coupled enterprise applications without the use of multiple threads.
  • In other words, RPC systems require the client and the server to be available at the same time. However, such tight coupling may not be possible or desired in some applications.
  • Message-Oriented Middleware (MOM) systems provide solutions to such problems. They are based on the asynchronous interaction model, and provide the abstraction of a message queue that can be accessed across a network. Note, however, that messaging here refers to asynchronous requests or events that are consumed by enterprise applications and not humans as in electronic mail (email). These messages contain formatted data that describe specific business actions.
  • The Java Message Service (JMS) was designed to make it easy to develop business applications that asynchronously send and receive business data and events. It defines a common enterprise messaging API that is designed to be easily and efficiently supported by a wide range of enterprise messaging products.
  • JMS supports both messaging models:
    • point-to-point (queuing)
    • publish-subscribe.
  • MOM is becoming an essential component for integrating intra-company operations as it allows separate business components to be combined into a reliable, yet flexible, system.
  • Benefits : JMS makes the learning curve easy by minimizing the set of concepts a Java developer must learn to use enterprise messaging products, and at the same time it maximizes the portability of messaging applications.
Architecture

A JMS application is composed of the following parts:

  • JMS provider: A messaging system that implements the JMS specification.
  • JMS clients: Java applications that send and receive messages.
  • Messages: Objects that are used to communicate information between JMS clients.
  • Administered objects: Preconfigured JMS objects that are created by an administrator for the use of JMS clients.

Message Delivery Models

JMS supports two different message delivery models:

  1. Point-to-Point (Queue destination):
    In this model, a message is delivered from a producer to one consumer. The messages are delivered to the destination, which is a queue, and then delivered to one of the consumers registered for the queue. While any number of producers can send messages to the queue, each message is guaranteed to be delivered, and consumed by one consumer. If no consumers are registered to consume the messages, the queue holds them until a consumer registers to consume them.
  2. Publish/Subscribe (Topic destination):
    In this model, a message is delivered from a producer to any number of consumers. Messages are delivered to the topic destination, and then to all active consumers who have subscribed to the topic. In addition, any number of producers can send messages to a topic destination, and each message can be delivered to any number of subscribers. If there are no consumers registered, the topic destination doesn't hold messages unless it has durable subscription for inactive consumers. A durable subscription represents a consumer registered with the topic destination that can be inactive at the time the messages are sent to the topic.
The JMS Programming Model


A JMS application consists of a set of application-defined messages and a set of clients that exchange them. JMS clients interact by sending and receiving messages using the JMS API. A message is composed of three parts: header, properties, and a body.

  • Header: required for every message, contains information that is used for routing and identifying messages. Some of these fields are set automatically, by the JMS provider, during producing and delivering a message, and others are set by the client on a message by message basis.
  • Properties: optional, provide values that clients can use to filter messages. They provide additional information about the data, such as which process created it, the time it was created. Properties can be considered as an extension to the header, and consist of property name/value pairs. Using properties, clients can fine-tune their selection of messages by specifying certain values that act as selection criteria.
  • The body: also optional, contains the actual data to be exchanged. The JMS specification defined six type or classes of messages that a JMS provider must support:
    • Message: This represents a message without a message body.
    • StreamMessage: A message whose body contains a stream of Java primitive types. It is written and read sequentially.
    • MapMessage: A message whose body contains a set of name/value pairs. The order of entries is not defined.
    • TextMessage: A message whose body contains a Java string...such as an XML message.
    • ObjectMessage: A message whose body contains a serialized Java object.
    • BytesMessage: A message whose body contains a stream of uninterpreted bytes.

Friday, March 22, 2019

Creating a Web Application


Generally, a web application starts with a login portal or registration portal, otherwise we could use simple html pages for rest things out there. Our first web application will be starting with a login portal, interacting with database through a servlet with validation and login error handling .
Requirements :

  • An IDE (most preferably Eclipse J2EE)  
  • Server (Apache Tomcat) // link to download is given in previous blog post.
  • Database (most preferably MySql 5.5)
Follow the steps below and code to create a login page :

1. Open Eclipse Enterprise edition. File -> New -> Dynamic Web Project.


2. Enter project name and choose a new runtime which is the server itself.

3. Select server name and specify it’s directory in the system.


4. click next, and put build/classes as the output folder (by default it’s same written here) and then next, check the web.xml for deployment description purpose.


5. Click on Finish to end setup.
6. Right click on project name (test here), Build Path -> Configure Build Path in order to configure project for servlet-api if not provided (to avoid compilation error in most cases).  A dialog box will open, click on Add External JAR, go to apache tomcat->lib directory and select servlet-api.jar, apply the change.


7. Click on project name, create a .jsp file named login.jsp as follows : WebContent->New->JSP File.


8. All we need now to write code for login.jsp, a servlet and web xml, along with a login table in database.
login.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
       pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>login page</title>
</head>
<body>
       <form action="Login" method="post">
             username: <input type="text" name="email" required><br>
             password: <input type="password" name="pass" required><br>
             <input type="submit" value="Login"> <input type="reset"
                    value="reset">
             <center style="color: red">
<%
if (null != request.getAttribute("errorMessage")) {
String s = (String) request.getAttribute("errorMessage");
if (s != null) {
out.println(s);
}
}
%>
</center><br>
       </form>
</body>
</html>
Action in form is “Login” which will be our servlet named Login.java.

9. Create a login table in database (let uid(user,pass) be the table.
Mysql:
·       User : root and pwd:4591
·       Database name: login
·       Code:
create table uid ( user varchar(20), pass varchar(20), primary key(user));
insert into uid() values(“a”, “a”);

10. To create Servlet, goto project name->new->servlet. A dialog box will open, enter package name (like com), it’s optional and class name as Login, to generate Login.class file (same as filename Login.java). Click on next and notice the Url mapping : /Login which will be used in web.xml for mapping purpose. Click next and finish to open Login.java file.
Login.java :
package com;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionActivationListener;

//import com.sun.xml.internal.bind.v2.schemagen.xmlschema.List;

public class Login extends HttpServlet {

       public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             RequestDispatcher dispatcher = request.getRequestDispatcher("/Login.jsp");
             dispatcher.forward(request, response);
       }

       public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              try {
                    String user = request.getParameter("email");
                    String pass = request.getParameter("pass");
                    int c = 0;
                    // jdbc call
                    Class.forName("com.mysql.jdbc.Driver");
                    Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/login?useSSL=false", "root", "4591");
                    System.out.println("Connected to mysql");
                    Statement st = con.createStatement();
                    ResultSet rs = st.executeQuery("select * from uid");
                                        while (rs.next()) {
                                 if (user.equals(rs.getString("user")) && pass.equals(rs.getString("pass"))) {
                                        response.sendRedirect("https://icodemac55.blogspot.com");
                                        c++;
                                 }
                    }
                    if (c == 0) {
                           request.setAttribute("errorMessage", "Invalid username or password");
                           RequestDispatcher dispatcher = request.getRequestDispatcher("/Login.jsp");
                           dispatcher.forward(request, response);
                           response.sendRedirect("Login.jsp");
                    }
             } catch (Exception e) {
                    e.printStackTrace();
             }

       }
}

11.  Last step is to write code for web.xml file which lie inside WebContent-> WEB-INF -> lib.
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>log</display-name>
  <servlet>
    <servlet-name>Logintesteeeee</servlet-name>
    <servlet-class>com.Login</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Logintesteeeee</servlet-name>
    <url-pattern>/Login</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>login.jsp</welcome-file>
  </welcome-file-list>
</web-app>

12. Run the project on server project-name(test)-> Run as -> Run on Server.

(if error occurred on http://localhost:8080/test, then run login.jsp as: http://localhost:8080/test/login.jsp).
If login credentials are correct, you’ll redirected to the required page, else an error will be thrown on same page.
Note: For user friendly view, choose in Eclipse EE ide (Window- Show View- Navigator).

Event Handling in Spring

Spring's event handling is single-threaded so if an event is published,  until and unless all the receivers get the message, the process...