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:
A Servlet, JSP or EJB wants to make a log entry, so they create a new par.LogEntry object
The par.LogEntry object is sent to the ejb3.LoggerBean through the ejb3.Logger interface
The ejb3.LoggerBean adds the par.LogEntry object to the JMS Message Driven Bean Queue
Server:
The ejb3.LoggingService grabs the par.LogEntry object from the Queue
The par.LogEntry object is sent to the ejb3.LogWriterBean through the ejb3.LogWriter interface
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
<?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)
Comments