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.Queue;
20  import javax.jms.QueueConnection;
21  import javax.jms.QueueConnectionFactory;
22  import javax.jms.QueueSender;
23  import javax.jms.QueueSession;
24  import javax.jms.Session;
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 Queue. The events are
34   * serialized and transmitted as JMS message type {@link
35   * javax.jms.ObjectMessage}.
36   * <p>
37   * For more information about this appender, please refer to:
38   * http://logback.qos.ch/manual/appenders.html#JMSQueueAppender
39   * 
40   * @author Ceki G&uuml;lc&uuml;
41   */
42  public class JMSQueueAppender extends JMSAppenderBase<ILoggingEvent> {
43  
44    static int SUCCESSIVE_FAILURE_LIMIT = 3;
45  
46    String queueBindingName;
47    String qcfBindingName;
48    QueueConnection queueConnection;
49    QueueSession queueSession;
50    QueueSender queueSender;
51  
52    int successiveFailureCount = 0;
53  
54    private PreSerializationTransformer<ILoggingEvent> pst = new LoggingEventPreSerializationTransformer();
55    
56    public JMSQueueAppender() {
57    }
58  
59    /**
60     * The <b>QueueConnectionFactoryBindingName</b> option takes a string value.
61     * Its value will be used to lookup the appropriate
62     * <code>QueueConnectionFactory</code> from the JNDI context.
63     */
64    public void setQueueConnectionFactoryBindingName(String qcfBindingName) {
65      this.qcfBindingName = qcfBindingName;
66    }
67  
68    /**
69     * Returns the value of the <b>QueueConnectionFactoryBindingName</b> option.
70     */
71    public String getQueueConnectionFactoryBindingName() {
72      return qcfBindingName;
73    }
74  
75    /**
76     * The <b>QueueBindingName</b> option takes a string value. Its value will be
77     * used to lookup the appropriate <code>Queue</code> from the JNDI context.
78     */
79    public void setQueueBindingName(String queueBindingName) {
80      this.queueBindingName = queueBindingName;
81    }
82  
83    /**
84     * Returns the value of the <b>QueueBindingName</b> option.
85     */
86    public String getQueueBindingName() {
87      return queueBindingName;
88    }
89  
90    /**
91     * Options are activated and become effective only after calling this method.
92     */
93    public void start() {
94      QueueConnectionFactory queueConnectionFactory;
95  
96      try {
97        Context jndi = buildJNDIContext();
98  
99        // addInfo("Looking up [" + qcfBindingName + "]");
100       queueConnectionFactory = (QueueConnectionFactory) lookup(jndi,
101           qcfBindingName);
102       // addInfo("About to create QueueConnection.");
103       if (userName != null) {
104         this.queueConnection = queueConnectionFactory.createQueueConnection(
105             userName, password);
106       } else {
107         this.queueConnection = queueConnectionFactory.createQueueConnection();
108       }
109 
110       // addInfo(
111       // "Creating QueueSession, non-transactional, "
112       // + "in AUTO_ACKNOWLEDGE mode.");
113       this.queueSession = queueConnection.createQueueSession(false,
114           Session.AUTO_ACKNOWLEDGE);
115 
116       // addInfo("Looking up queue name [" + queueBindingName + "].");
117       Queue queue = (Queue) lookup(jndi, queueBindingName);
118 
119       // addInfo("Creating QueueSender.");
120       this.queueSender = queueSession.createSender(queue);
121 
122       // addInfo("Starting QueueConnection.");
123       queueConnection.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.queueConnection != null && this.queueSession != null
132         && this.queueSender != 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 (queueSession != null) {
151         queueSession.close();
152       }
153       if (queueConnection != null) {
154         queueConnection.close();
155       }
156     } catch (Exception e) {
157       addError("Error while closing JMSAppender [" + name + "].", e);
158     }
159 
160     // Help garbage collection
161     queueSender = null;
162     queueSession = null;
163     queueConnection = null;
164   }
165 
166   /**
167    * This method called by {@link AppenderBase#doAppend} method to do most
168    * of the real appending work.
169    */
170   public void append(ILoggingEvent event) {
171     if (!isStarted()) {
172       return;
173     }
174 
175     try {
176       ObjectMessage msg = queueSession.createObjectMessage();
177       Serializable so = pst.transform(event);
178       msg.setObject(so);
179       queueSender.send(msg);
180       successiveFailureCount = 0;
181     } catch (Exception e) {
182       successiveFailureCount++;
183       if (successiveFailureCount > SUCCESSIVE_FAILURE_LIMIT) {
184         stop();
185       }
186       addError("Could not send message in JMSQueueAppender [" + name + "].", e);
187 
188     }
189   }
190 
191   /**
192    * Returns the QueueConnection used for this appender. Only valid after
193    * start() method has been invoked.
194    */
195   protected QueueConnection getQueueConnection() {
196     return queueConnection;
197   }
198 
199   /**
200    * Returns the QueueSession used for this appender. Only valid after start()
201    * method has been invoked.
202    */
203   protected QueueSession getQueueSession() {
204     return queueSession;
205   }
206 
207   /**
208    * Returns the QueueSender used for this appender. Only valid after start()
209    * method has been invoked.
210    */
211   protected QueueSender getQueueSender() {
212     return queueSender;
213   }
214 }