Going Further into JLog

The simple example above only demonstrates a small portion of what is available with the JLog package. Returning to the premise that there are two main types of components in a JLog implementation, event listeners and producers, the following two sections will cover in a little more detail some other options available when configuring an application log like the one in the last example.

JLog LogEventListeners

The following class diagram illustrates the relationships that exist between sonic of the various writers and filters that implement the ILogEventListener interface. It is by no means exhaustive; for more types refer to the JLog JavaDoc.

Looking at the diagram, an important thing to note is that all of the log writers implement the ILogEventListener interface, which means that all of these writers can be added to an object implementing ILogEventProducer, such as the log, reader, and filter classes:

click to expand

As you can see from the diagram above, all of the Writer objects implement the ILogWriter interface, which extends the ILogEventListener interface. Any of these components can be added as a listener to the log event producers covered in the next section. In our last example LogOutputStream was an event listener that was listening for events from the event producer Log.global.

This diagram also contains an ILogFormatter interface, which is used by some of the LogWriter objects. Objects that implement the ILogFormatter interface are intended to perform message formatting for the writer instance that uses them. There are two types of writer objects (JMS, JDBC) that do not use this interface and the reason for that is because they do not require formatted text log messages.

Other than log writers, the other type of interface to extend the ILogEventListener interface is the ILogFilter interface. Classes that implement the ILogFilter interface are event listeners as well as event producers, so you will see them in this diagram and in the next section's diagram as well. Filters act as readers and writers in one. They are a kind of message mediator, because they monitor event producers for a specific type of message and then they themselves produce an event when the specified message type is observed. For more information about log filters please refer to the JavaDoc and example code that come with JLog.

JLog LogEventProducers

The following class diagram shows all of the various ILogEventProducer objects such as the Log object, and various readers, and filters. In our example above, the LogEventProducer was the object Log.global. Again, any LogEventListener can be registered with a LogEventProducer:

click to expand

Looking at the diagram, we see that there are two interfaces that extend ILogEventProducer, namely ILogFilter and ILogReader. Since we have already covered ILogFilter, we'll focus on ILogReader. Classes implementing this interface are event producers that can have event listeners observing them. For example, a JMSLogReader can be observed by a LogWriter.

The LogWriter would be in a position to process log messages that the JMSLogReader reads. We will cover in more detail the specifics of JMS logging in the next section. The final thing to note about this diagram is the Log object. The Log object is the heart of the logging package. You add log messages to Log instances, whether you create an instance of your own or use a globally available Log instance, which we talked about earlier (Log.global), to perform all of the actual logging. The Log object is what the various writers and filters listen to for messages.

JLog JMS Implementation

Now that we've covered the basics of what the JLog package has to offer, let's narrow our focus a bit. Since our interests lie mostly with distributed logging usingJMS, let's look specifically at the JMSLogWriter and JMSLogReader class diagram:

click to expand

As you can see from the class diagram, the JMSLogReader is a LogEventProducer and the JMSLogWriter is a LogEventListener. You might be thinking to yourself: shouldn't the JMSLogWriter be the producer and the JMSLogReader be the listener? The answer to that question is that each of these two components is both a listener and a producer. The following diagram illustrates this point:

click to expand

As the above diagram shows, the JMSLogWriter is an event producer in that it publishes log messages to a specified JMS topic. The JMSLogReader is an event listener in that it subscribes to a specified JMS topic and receives messages. So taking the previous class diagram and combining it with the diagram above, the next diagram illustrates the entire picture as it relates to the roles of log readers and log writers:

click to expand

Looking at the above diagram we can see in Step 1 that the JMSLogWriter is an event listener listening to the Log object. Then once a Log event occurs the JMSLogWriter turns into an event producer because it publishes the new log event to a JMS topic. In Step 3 the JMSLogReader is an event listener listening to the topic to which the JMSLogWriter published a message. Once the JMSLogReader has been notified of a new message published to the topic Step 4 occurs where the JMSLogReader acts as an event producer informing a log writer that a new message is to be logged.

The use of JLog and JMS together is a very natural fit because they are both based on the same Publish/Subscribe mechanism, which is a very flexible, low-coupling way to piece together event-logging systems.

Logging Example Using JLog and JMS

Now that we've covered how JLog and JMS fit together it's time for an example to illustrate how an implementation works.

Important 

For this and the rest of the examples throughout the chapter you will need to have BEA's Weblogic 6.0 installed (http://www.bea.com).

In the following example, the JMSLogExample program performs the following tasks:

  • It connects to the application server's naming service to retrieve a JMS TopicConnectionFactory

  • It retrieves a JMS Topic for publishers and subscribers to use

  • It creates a publisher or subscriber based on the run-time parameters passed in as command-line arguments

  • It publishes messages to the topic if publisher is the selected mode, or subscribes to the topic if subscriber is the selected mode

In this example we have a subscriber, who will be subscribing to a JMS topic to receive messages from, and a publisher, who will be producing the messages. This example code can be run as either the subscriber or the publisher role. Two separate application instances will be needed to execute this example, either in two different windows on the same machine or different windows on different machines.

The example shows how to use JMSLogWriter to publish log entries to JMS and how to use JMSLogReader to receive log entries from JMS. Let's take a look at the code listing:

     package examples.jlogexamples.jms;     import com.jtrack.jlog.implementation.*;     import com.jtrack.jlog.implementation.jms.*;     import javax.jms. *;     import javax.naming.*;     import java.util.Hashtable;     public class JMSLogExample {         //IntialContextFactory that's used to retrieve the JMS factory private String JNDI_FACTORY;         //Factory used to create JMS topic connections         private String JMS_FACTORY;         //JMS topic used in this example to publish/subscribe log messages private String TOPIC;         private String HOST;         private static boolean client;         protected TopicSession topicSession = null;         protected TopicConnection topicConnection = null;         private InitialContext ctx;         private LogWriter logWriter; 

A URL pointing to the JMS provider, and the mode of operation (publisher or subscriber) are needed:

         public static void main(String[] args) {            try {               JMSLogExample example = new JMSLogExample(args);               example.runExample(client);               example.shutdown();            }            catch(JMSException e) {               e.printStackTrace() ;            }         } 

In the constructor we use JNDI to get the initial context as well as to process the input arguments. Using the initial context, we retrieve the TopicConnectionFactory used to create a TopicConnection. That TopicConnection is then used to create a TopicSession, which could be used later to create a topic in the event that it doesn't already exist. In fact, we will look up an existing administered topic. This can be configured either using a WebLogic startup property or by using the administration console, as we'll see later. After we create an instance of the JMSLogExample class we call runExample() passing a parameter client to indicate what mode of operation this example should run in:

       private JMSLogExample(String[] args) {          super();          if (args.length != 5) {             System.out.println("Usage: java " +                "framework.test.jmstest.JMSLogExample {URL} {context} " +                "{topic} {JMSfactory} {(subscriber(publisher)}\n");             System.out.println("e.g. t3://localhost:7001 " +                "weblogic.jndi.WLInitialContextFactory exampleTopic " +                "javax.jms.TopicConnectionFactory subscriber ");             System.exit(1);          }          HOST         = args[0];          JNDI_FACTORY = args[1]          TOPIC        = args[2]          JMS_FACTORY  = args[3];          if(args[4].equalsIgnoreCase("subscriber")) {             client = true;          }          try {             ctx = getInitialContext(HOST);          }          catch(NamingException e) {             System.out.println(e.getMessage());             System.exit(1);          }          try {             System.out.println("Creating topic connection.");             topicConnection = getConnectionFactory().createTopicConnection();             topicConnection.start();             System.out.println("Creating topic session.");             topicSession = topicConnection.createTopicSession(false,                Session.AUTO_ACKNOWLEDGE );          }          catch (JMSException e) {             e.printStackTrace() ;          }       }       private InitialContext getInitialContext(String url)            throws NamingException {          Hashtable env = new Hashtable();          env. put (Context. INITIAL_CONTEXT_FACTORY, JNDI_FACTORY) ;          env. put (Context. PROVIDER_URL, url) ;          return new InitialContext(env);       } 

Create a TopicConnectionFactory needed to work with JMS

       private TopicConnectionFactory getConnectionFactory() {          TopicConnectionFactory factory = null;          try {             factory = (TopicConnectionFactory)ctx.lookup(JMS_FACTORY);          }          catch(NamingException e) {             System.out.println(e.getMessage());          }          return factory;       } 

Based on whether it's run as a publisher or subscriber, we'll execute different methods:

       private void runExample(boolean subscriber) throws JMSException {          Topic topic = null;          try {             topic = (Topic)ctx.lookup(TOPIC);          }          catch(NamingException e) {             System.out.println(e.getMessage()) ;             System.out.println("Topic lookup failed. Using createTopic().") ;             topic = topicSession.createTopic(TOPIC);             try {                ctx.bind(TOPIC, topic) ;             }             catch(NamingException ex) {                System.out.println("Error binding topic: " + e.getMessage()) ;                System.exit (1) ;             }          }          if(subscriber) {              runSubscriber(topic) ;              try {                 System.out.println("Waiting for messages to be published " +                   "to topic: " + TOPIC + "\n") ;                 synchronized(this) {                    wait();                 }              }              catch(InterruptedException e) {                 System.out.println(e.getMessage());              }           }           else {              runPublisher(topic) ;           }       } 

If the example is run in publisher mode we will create aJMS TopicPublisher and pass that as an argument to JMSLogWriter's constructor, along with a message object. Once the writer is created, we add it as a listener to the global log:

       private void runPublisher(Topic topic) throws JMSException { 

Create the TopicPublisher and ObjectMessage needed for creating a JMSLogWriter and then create the actual JMSLogWriter:

         System.out.println("Creating topic publisher") ;         TopicPublisher topicPub = topicSession.createPublisher( topic ) ;         ObjectMessage message = topicSession.createObjectMessage() ;         JMSLogWriter logWriter = new JMSLogWriter(topicPub, message ) ; 

Add the log writer to the global log listeners:

         Log.global.addLogEventListener(logWriter); 

Write some log entries to the global log, which are then published on JMS by the JMSLogWriter listening to the global log. They are received by the JMSLogReader, which forwards them to a LogOutputStream, which finally prints the log message:

         Log.global.trace( "A trace message",           "This is a trace message from the JMSLogExample." ) ;         Log.global.debug( "A debug message",           "This is a debug message from the JMSLogExample." ) ;         Log.global.warn( "A warning message",           "This is a warning message from the JMSLogExample." ) ;         System.out.println( "published 3 messages to topic: " + TOPIC);       } 

If the example is run in subscriber mode, we will create a JMS TopicSubscriber and pass that as an argument to the JMSLogReader's constructor. Once we create the JMSLogReader we need to create a LogWriter to listen to the JMSLogReader and to log messages received by the reader. In this case, we'll be logging messages to System.out:

       private void runSubscriber(Topic topic) throws JMSException {          System. out .println( "Creating topic subscriber") ;          TopicSubscriber topicSubscriber =             topicSession.createSubscriber( topic ) ;          JMSLogReader subscriber =             new JMSLogReader ("A Log Subscriber", topicSubscriber ) ; 

Create a log writer to log to console all messages received by the JMSLogReader:

           new LogOutputStream( subscriber, System.out, new LongLogFormatter()) ;        }        private void shutdown() {           try {              topicSession.close() ;              topicConnection.close() ;           }           catch (JMSException e) {              e.printStackTrace() ;           }        }     } 

This example demonstrates a simple implementation of logging using JMS but if we look closer we can see that there's a lot of overhead associated with the creation of Log objects. For example, performing JNDI lookups, creating JMS components, and setting up log readers and log writers would be extremely tedious if every single application that wanted to implement logging had to perform these tasks itself. Later, we solve this problem by providing a "logging service" that can be used to abstract the details of initializing and configuring logging so that an application developer can easily implement logging within their applications by using this service.

Compiling and Running the Example

Going back to how we ran our first example, it is very similar for this one. We have already set up all the machinery to compile and deploy the classes, but we need to set up the administered JMS objects at both the client and server ends.

Configure JMS objects in WebLogic

Firstly, configure a topic and a topic connection factory in WebLogic. This is carried out using exactly the same procedure as described in Appendix A. Choose the names TCFactory and myTopic for the factory and topic respectively. Ensure that both the JMS server and the connection factory are targeted to a live server (you can use the one called myserver in the default installation):

click to expand

While you are doing this, you may as well set up the other administered objects that we will need later in the chapter, although we'll remind you again about these. Create three more topics examples.frameworkServlet, examples.frameworkEJB, and LogServiceConfig, and create a topic connection factory javax.jms.TopicConnectionFactory. Remember to assign the connection factories to the myserver target.

Next, we need to set some more properties at the client end. These are the values the clients need to get a reference to the administered JMS objects:

       <property name="host" value="t3://localhost:7001"/>       <property name="topic" value="myTopic"/>       <property name="initctx" value="weblogic.jndi.T3InitialContextFactory"/>       <property name="connfac" value="TCFactory"/> 

Now we can run the program as a publisher or subscriber using the Ant targets:

       <target name="JMSLogExample.sub" depends="examples">         <java            classname="examples.jlogexamples.jms.JMSLogExample"            fork="yes"            classpath="${framework_examples_classpath}"         >           <arg value="${host}"/>           <arg value="${initctx}"/>           <arg value="${topic}"/>           <arg value="${connfac}"/>           <arg value="subscriber" />         </java>       </target>       <target name="JMSLogExample.pub" depends="example">         <java            classname="examples.jlogexamples.jms.JMSLogExample"            fork="yes"            classpath="${framework_examples_classpath}"         >           <arg value="${host}"/>           <arg value="${initctx}"/>           <arg value="${topic}"/>           <arg value="${connfac}"/>           <arg value="publisher" />         </java>       </target> 

To get the program running as a subscriber, type ant JMSLogExample.sub:

click to expand

Then start up the publisher in a new window by typing ant JMSLogExample.pub:

click to expand

Then go back to the previous screen, and you will see the logging output come through:

click to expand

Our logging system has informed us that three messages were published to the topic myTopic.



Professional JMS
Professional JMS
ISBN: 1861004931
EAN: 2147483647
Year: 2000
Pages: 154

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net