View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2009, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.net;
15  
16  import java.io.Serializable;
17  
18  import javax.jms.ObjectMessage;
19  import javax.jms.Session;
20  import javax.jms.Topic;
21  import javax.jms.TopicConnection;
22  import javax.jms.TopicConnectionFactory;
23  import javax.jms.TopicPublisher;
24  import javax.jms.TopicSession;
25  import javax.naming.Context;
26  
27  import ch.qos.logback.classic.spi.ILoggingEvent;
28  import ch.qos.logback.core.AppenderBase;
29  import ch.qos.logback.core.net.JMSAppenderBase;
30  import ch.qos.logback.core.spi.PreSerializationTransformer;
31  
32  /**
33   * A simple appender that publishes events to a JMS Topic. The events are
34   * serialized and transmitted as JMS message type {@link
35   * javax.jms.ObjectMessage}.
36   * 
37   * For more information about this appender, please refer to
38   * http://logback.qos.ch/manual/appenders.html#JMSTopicAppender
39   * 
40   * @author Ceki Gülcü
41   */
42  public class JMSTopicAppender extends JMSAppenderBase<ILoggingEvent> {
43  
44    static int SUCCESSIVE_FAILURE_LIMIT = 3;
45  
46    String topicBindingName;
47    String tcfBindingName;
48    TopicConnection topicConnection;
49    TopicSession topicSession;
50    TopicPublisher topicPublisher;
51  
52    int successiveFailureCount = 0;
53    
54    private PreSerializationTransformer<ILoggingEvent> pst = new LoggingEventPreSerializationTransformer();
55    
56    public JMSTopicAppender() {
57    }
58  
59    /**
60     * The <b>TopicConnectionFactoryBindingName</b> option takes a string value.
61     * Its value will be used to lookup the appropriate
62     * <code>TopicConnectionFactory</code> from the JNDI context.
63     */
64    public void setTopicConnectionFactoryBindingName(String tcfBindingName) {
65      this.tcfBindingName = tcfBindingName;
66    }
67  
68    /**
69     * Returns the value of the <b>TopicConnectionFactoryBindingName</b> option.
70     */
71    public String getTopicConnectionFactoryBindingName() {
72      return tcfBindingName;
73    }
74  
75    /**
76     * The <b>TopicBindingName</b> option takes a string value. Its value will be
77     * used to lookup the appropriate <code>Topic</code> from the JNDI context.
78     */
79    public void setTopicBindingName(String topicBindingName) {
80      this.topicBindingName = topicBindingName;
81    }
82  
83    /**
84     * Returns the value of the <b>TopicBindingName</b> option.
85     */
86    public String getTopicBindingName() {
87      return topicBindingName;
88    }
89  
90    /**
91     * Options are activated and become effective only after calling this method.
92     */
93    public void start() {
94      TopicConnectionFactory topicConnectionFactory;
95  
96      try {
97        Context jndi = buildJNDIContext();
98  
99        // addInfo("Looking up [" + tcfBindingName + "]");
100       topicConnectionFactory = (TopicConnectionFactory) lookup(jndi,
101           tcfBindingName);
102       // addInfo("About to create TopicConnection.");
103       if (userName != null) {
104         this.topicConnection = topicConnectionFactory.createTopicConnection(
105             userName, password);
106       } else {
107         this.topicConnection = topicConnectionFactory.createTopicConnection();
108       }
109 
110       // addInfo(
111       // "Creating TopicSession, non-transactional, "
112       // + "in AUTO_ACKNOWLEDGE mode.");
113       this.topicSession = topicConnection.createTopicSession(false,
114           Session.AUTO_ACKNOWLEDGE);
115 
116       // addInfo("Looking up topic name [" + topicBindingName + "].");
117       Topic topic = (Topic) lookup(jndi, topicBindingName);
118 
119       // addInfo("Creating TopicPublisher.");
120       this.topicPublisher = topicSession.createPublisher(topic);
121 
122       // addInfo("Starting TopicConnection.");
123       topicConnection.start();
124 
125       jndi.close();
126     } catch (Exception e) {
127       addError("Error while activating options for appender named [" + name
128           + "].", e);
129     }
130 
131     if (this.topicConnection != null && this.topicSession != null
132         && this.topicPublisher != null) {
133       super.start();
134     }
135   }
136 
137   /**
138    * Close this JMSAppender. Closing releases all resources used by the
139    * appender. A closed appender cannot be re-opened.
140    */
141   public synchronized void stop() {
142     // The synchronized modifier avoids concurrent append and close operations
143     if (!this.started) {
144       return;
145     }
146 
147     this.started = false;
148 
149     try {
150       if (topicSession != null) {
151         topicSession.close();
152       }
153       if (topicConnection != null) {
154         topicConnection.close();
155       }
156     } catch (Exception e) {
157       addError("Error while closing JMSAppender [" + name + "].", e);
158     }
159 
160     // Help garbage collection
161     topicPublisher = null;
162     topicSession = null;
163     topicConnection = null;
164   }
165 
166 
167   /**
168    * This method called by {@link AppenderBase#doAppend} method to do most
169    * of the real appending work.
170    */
171   public void append(ILoggingEvent event) {
172     if (!isStarted()) {
173       return;
174     }
175 
176     try {
177       ObjectMessage msg = topicSession.createObjectMessage();
178       Serializable so = pst.transform(event);
179       msg.setObject(so);
180       topicPublisher.publish(msg);
181       successiveFailureCount = 0;
182     } catch (Exception e) {
183       successiveFailureCount++;
184       if (successiveFailureCount > SUCCESSIVE_FAILURE_LIMIT) {
185         stop();
186       }
187       addError("Could not publish message in JMSTopicAppender [" + name + "].", e);
188     }
189   }
190 
191   /**
192    * Returns the TopicConnection used for this appender. Only valid after
193    * start() method has been invoked.
194    */
195   protected TopicConnection getTopicConnection() {
196     return topicConnection;
197   }
198 
199   /**
200    * Returns the TopicSession used for this appender. Only valid after start()
201    * method has been invoked.
202    */
203   protected TopicSession getTopicSession() {
204     return topicSession;
205   }
206 
207   /**
208    * Returns the TopicPublisher used for this appender. Only valid after start()
209    * method has been invoked.
210    */
211   protected TopicPublisher getTopicPublisher() {
212     return topicPublisher;
213   }
214 }