UsingJMSAndMDBToLogToAMySqlDatabase

Version 5

    Here is a fully working EJB3 tutorial for using JMS /Message Driven Beans

    as a logger to a MySql database. This should be everything you need in

    order to successfully run a working demo.

     

    This is tested with the following setup:

     *  Windows XP sp2
     *  MySQL 5.0.13 and Connector/J 3.1.11
     *  JBoss 4.0.3 (Oct.2005 release)
     *  Java 1.5.0_05
    

    Explanation. Here's how the logging works.

     

     

    Client:

    1. A Servlet, JSP or EJB wants to make a log entry, so they create a new par.LogEntry object

    2. The par.LogEntry object is sent to the ejb3.LoggerBean through the ejb3.Logger interface

    3. The ejb3.LoggerBean adds the par.LogEntry object to the JMS Message Driven Bean Queue

     

    Server:

    1. The ejb3.LoggingService grabs the par.LogEntry object from the Queue

    2. The par.LogEntry object is sent to the ejb3.LogWriterBean through the ejb3.LogWriter interface

    3. The ejb3.LogWriterBean persists the par.LogEntry object in the database

     

     

    This is what your ear should look like when everything is set up correctly.

    I'll go through the necessary portions of each file mentioned.

    EAR File (MyTest.ear)
    ---------------------
    |-meta-inf
    |----application.xml
    |----Manifest.mf
    |
    |-MyTest.par
    |----meta-inf
    |--------Manifest.mf
    |--------persistence.xml
    |----par
    |--------LogEntry.class
    |
    |-MyTest.war
    |----meta-inf
    |--------Manifest.mf
    |----web-inf
    |--------jboss-web.xml
    |--------web.xml
    |--------classes
    |------------web
    |----------------TestServlet.class
    |
    |-MyTest.ejb3
    |----meta-inf
    |--------Manifest.mf
    |----ejb3
    |--------Logger.class
    |--------LoggerBean.class
    |--------LoggingService.class
    |--------LogWriter.class
    |--------LogWriterBean.class
    

     

     

    1. MyTest.ear - Section 1 will go over the necessary files for MyTest.ear

     

    1a. application.xml - I am using the context-root of MyTest, so when

       you test the application, your url will be similar to

       http://localhost:8080/MyTest.

     

     

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN" 
    "http://java.sun.com/dtd/application_1_3.dtd">
    
    <application xmlns="http://java.sun.com/xml/ns/j2ee" version="1.4"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                            http://java.sun.com/xml/ns/j2ee/application_1_4.xsd">
      <display-name>My Logging Test</display-name>
    
    <!-- EJB3 Beans and Service POJOs -->
      <module>
        <ejb>MyTest.ejb3</ejb>
      </module>
    
    <!-- Database POJOs -->
      <module>
        <ejb>MyTest.par</ejb>
      </module>
    
    <!-- Servlets, JSPs -->
      <module>
        <web>
          <web-uri>MyTest.war</web-uri>
          <context-root>MyTest</context-root>
        </web>
      </module>
    </application>
    

     

    2. MyTest.par - Section 2 will go over the necessary files of MyTest.par

     

    2a. persistence.xml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <entity-manager>
      <name>MyTestPar</name>
      <jta-data-source>java:/MyTestDS</jta-data-source>
      <properties>
        <!-- The value of 'none' means don't auto-generate the database tables from the POJOs. -->
        <property name="hibernate.hbm2ddl.auto" value="none"></property>
      </properties>
    </entity-manager>
    
    2b. Set up the following table in MySql
    
    CREATE TABLE `log_records` (
      `id` int(10) unsigned NOT NULL auto_increment,
      `date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
      `level` enum('info','warning','debug','severe') NOT NULL,
      `message` text NOT NULL,
      PRIMARY KEY  (`id`)
    )
    
    

    2c. LogEntry.class - Since this is a basic example, we will only record

        the id, date, level and message

    package par;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratorType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    import javax.servlet.http.HttpServletRequest;
    import java.sql.Timestamp;
    import java.util.GregorianCalendar;
    import java.util.logging.Level;
    import java.io.Serializable;
    
    @Entity
    @Table(name = "log_records")
    public class LogEntry implements Serializable {
      private int id;
      private Timestamp date;
      private String level;
      private String message;
    
      public LogEntry() {
    
      }
    
      public LogEntry(final Level lvl, final String msg) {
        date = new Timestamp(new GregorianCalendar().getTimeInMillis());
        level = lvl.getName();
        message = msg;
      }
    
      @Id(generate = GeneratorType.AUTO)
      public int getId() {
        return id;
      }
    
      public void setId( final int id ) {
        this.id = id;
      }
    
      public Timestamp getDate() {
        return date;
      }
    
      public void setDate( final Timestamp date) {
        this.date= date;
      }
    
      public String getLevel() {
        return level;
      }
    
      public void setLevel( final String level ) {
        this.level = level;
      }
    
      public String getMessage() {
        return message;
      }
    
      public void setMessage( final String message ) {
        this.message = message;
      }
    }
    

    2d. Make sure you make a file called MyTest-ds.xml and place it

        in the deploy folder of JBoss server. You will need to set

        the mySchemaName, myUserName and myPassword variables for

        your own settings. Make sure your user has enough permissions

        to select,insert,delete, etc...

    <?xml version="1.0" encoding="UTF-8"?>
    <datasources>
        <local-tx-datasource>
            <jndi-name>MyTestDS</jndi-name>
            <connection-url> 
                jdbc:mysql://localhost:3306/mySchemaName
            </connection-url>
            <driver-class>com.mysql.jdbc.Driver</driver-class>
            <user-name>myUserName</user-name>
            <password>myPassword</password>
            <exception-sorter-class-name>
                org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
            </exception-sorter-class-name>
            <metadata>
                <type-mapping>mySQL</type-mapping>
            </metadata>
        </local-tx-datasource>
    </datasources>
    
    

    2e. Make sure you put the Connector/J jar file (mysql-connector-java-3.1.11-bin.jar)

        into your JBoss servers lib directory (i.e. c:\jboss-4.0.3\server\default\lib)

     

     

    3. MyTest.war - Section 3 will go over the necessary files of MyTest.war

     

    3a. jboss-web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <!DOCTYPE jboss-web PUBLIC
            "-//JBoss//DTD Web Application 2.4//EN"
            "http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd">
    <jboss-web>
      <security-domain>java:/jaas/MyTest</security-domain>
    
      <resource-ref>
        <res-ref-name>jdbc/LoggerDB</res-ref-name>
        <jndi-name>java:/MyTestDS</jndi-name>
      </resource-ref>
    
    </jboss-web>
    

     

    3b. web.xml

    <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
             version="2.4">
      <servlet>
        <servlet-name>TestServlet</servlet-name>
        <servlet-class>web.TestServlet</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>TestServlet</servlet-name>
        <url-pattern>/LogTest</url-pattern>
      </servlet-mapping>
      <resource-ref>
        <description>Log Writer Connection</description>
        <res-ref-name>jdbc/LoggerDB</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
      </resource-ref>
    </web-app>
    
    

    3c. TestServlet.class

    package web;
    
    import ejb3.Logger;
    import par.LogEntry;
    
    import javax.naming.InitialContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.logging.Level;
    
    public class TestServlet extends HttpServlet {
    
      protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
        testLogger(response);
      }
    
      protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
        testLogger(response);
      }
    
      private void testLogger(final HttpServletResponse response) throws IOException{
        int numberOfLogsToCreate = 5;
        final ServletOutputStream out = response.getOutputStream();
    
        try {
          final InitialContext ctx = new InitialContext();
          final Logger logger = (Logger) ctx.lookup(Logger.class.getName());
          for(int x=0; x < numberOfLogsToCreate; x++){
            final LogEntry logEntry = new LogEntry(Level.WARNING, "Msg #"+x);
            logger.sendEntry(logEntry);
          }
          out.println("<HTML><BODY>" + numberOfLogsToCreate + " Logs created</BODY></HTML>");
        }
        catch(Exception e){
          System.out.println("logging attempt failed: "+e.toString());
          out.println("<HTML><BODY>" + e.getMessage() + "</BODY></HTML>");
        }
        finally{
          out.flush();
          out.close();
        }
      }
    }
    

    4. MyTest.ejb3 - Section 4 will go over the necessary files of MyTest.ejb3

     

    4a. Logger.class

    package ejb3;
    
    import par.LogEntry;
    
    public interface Logger {
      void sendEntry(LogEntry logEntry);
    }
    
    

    4b. LoggerBean.class

    package ejb3;
    
    import par.LogEntry;
    
    import javax.ejb.Stateless;
    import javax.jms.*;
    import javax.naming.InitialContext;
    
    @Stateless
    public class LoggerBean implements Logger {
    
      public void sendEntry(final LogEntry logEntry) {
        QueueConnection cnn = null;
        QueueSession sess = null;
        QueueSender sender = null;
    
        try {
          final InitialContext ctx = new InitialContext();
          final Queue queue = (Queue) ctx.lookup("queue/myCustomLog");
          final QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("ConnectionFactory");
          cnn = factory.createQueueConnection();
          sess = cnn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
    
          final ObjectMessage objMessage = sess.createObjectMessage(logEntry);
          sender = sess.createSender(queue);
          sender.send(objMessage);
        }
        catch (Exception e) {
          e.printStackTrace();
        }
        finally {
          try {
            if (sess != null) {
              sess.close();
            }
          }
          catch (Exception e2) {
            System.err.println("Could not close logger session");
          }
          try {
            if (sender != null) {
              sender.close();
            }
          }
          catch (Exception e2) {
            System.err.println("Could not close logger sender");
          }
          try {
            if (cnn != null) {
              cnn.close();
            }
          }
          catch (Exception e2) {
            System.err.println("Could not close logger queue connection");
          }
    
        }
    
      }
    }
    
    

    4c. LoggingService.class

    package ejb3;
    
    import par.LogEntry;
    
    import javax.ejb.ActivationConfigProperty;
    import javax.ejb.MessageDriven;
    import javax.jms.Message;
    import javax.jms.MessageListener;
    import javax.jms.ObjectMessage;
    import javax.naming.InitialContext;
    
    @MessageDriven(activateConfig =
    {
      @ActivationConfigProperty(propertyName="destinationType",
        propertyValue="javax.jms.Queue"),
      @ActivationConfigProperty(propertyName="destination",
        propertyValue="queue/myCustomLog")
    })
    public class LoggingService implements MessageListener {
    
      public void onMessage (Message msg) {
        try {
    
          final ObjectMessage objMessage = (ObjectMessage)msg;
          final LogEntry logEntry = (LogEntry)objMessage.getObject();
    
          try{
             final InitialContext ctx = new InitialContext();
             final LogWriter logWriter = (LogWriter) ctx.lookup(LogWriter.class.getName());
             logWriter.addLog(logEntry);
          }
          catch(Exception e){
            // If you get an error, you probably want to insert code
            // in here to failover the logs to a text file
            System.err.println("Could not log file to database. "+e.toString());
          }
        } 
        catch (Exception e) {
          e.printStackTrace ();
        }
      }
    }
    
    

    4d. LogWriter.class

    package ejb3;
    
    import par.LogEntry;
    
    public interface LogWriter {
      void addLog(LogEntry logEntry );
    }
    
    

    4e. LogWriterBean.class

    package ejb3;
    
    import par.LogEntry;
    
    import javax.persistence.PersistenceContext;
    import javax.persistence.EntityManager;
    import javax.ejb.Stateless;
    
    @Stateless
    public class LogWriterBean implements LogWriter {
      @PersistenceContext(unitName = "MyTestPar")
      EntityManager em;
    
      public void addLog( final LogEntry logEntry ) {
        System.out.println( "Setting Log Entry: "+logEntry.toString() );
        em.merge(logEntry);
      }
    }
    

    5. Create and deploy the ear file from the files shown above. You may have to restart JBoss so

       it can pick up the MyTest-ds.xml file.

     

    6. Test the program by calling the url (Most likely http://localhost:8080/MyTest/LogTest)