View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j.net;
19  
20  import org.apache.log4j.plugins.Plugin;
21  import org.apache.log4j.plugins.Receiver;
22  import org.apache.log4j.spi.LoggingEvent;
23  
24  import javax.jms.*;
25  import javax.naming.Context;
26  import javax.naming.InitialContext;
27  import javax.naming.NameNotFoundException;
28  import javax.naming.NamingException;
29  import java.io.FileInputStream;
30  import java.util.Properties;
31  
32  /**
33   * JMSReceiver receives a remote logging event on a configured
34   * JSM topic and "posts" it to a LoggerRepository as if the event was
35   * generated locally. This class is designed to receive events from
36   * the JMSAppender class (or classes that send compatible events).
37   * <p>
38   * <p>Once the event has been "posted", it will be handled by the
39   * appenders currently configured in the LoggerRespository.
40   * <p>
41   * <p>This implementation borrows heavily from the JMSSink
42   * implementation.
43   *
44   * @author Mark Womack
45   * @author Paul Smith
46   * @author Stephen Pain
47   */
48  public class JMSReceiver extends Receiver implements MessageListener {
49  
50      private boolean active = false;
51  
52      protected String topicFactoryName;
53      protected String topicName;
54      protected String userId;
55      protected String password;
56      protected TopicConnection topicConnection;
57      protected String jndiPath;
58  
59      private String remoteInfo;
60      private String providerUrl;
61  
62      public JMSReceiver() {
63      }
64  
65      public JMSReceiver(String _topicFactoryName, String _topicName,
66                         String _userId, String _password, String _jndiPath) {
67          topicFactoryName = _topicFactoryName;
68          topicName = _topicName;
69          userId = _userId;
70          password = _password;
71          jndiPath = _jndiPath;
72      }
73  
74      /**
75       * Sets the path to a properties file containing
76       * the initial context and jndi provider url
77       */
78      public void setJndiPath(String _jndiPath) {
79          jndiPath = _jndiPath;
80      }
81  
82      /**
83       * Gets the path to a properties file containing
84       * the initial context and jndi provider url
85       */
86      public String getJndiPath() {
87          return jndiPath;
88      }
89  
90      /**
91       * Sets the JMS topic factory name to use when creating the
92       * JMS connection.
93       */
94      public void setTopicFactoryName(String _topicFactoryName) {
95          topicFactoryName = _topicFactoryName;
96      }
97  
98      /**
99       * Gets the curernt JMS topic factory name property.
100      */
101     public String getTopicFactoryName() {
102         return topicFactoryName;
103     }
104 
105     /**
106      * Sets the JMS topic name to use when creating the
107      * JMS connection.
108      */
109     public void setTopicName(String _topicName) {
110         topicName = _topicName;
111     }
112 
113     /**
114      * Gets the curernt JMS topic name property.
115      */
116     public String getTopicName() {
117         return topicName;
118     }
119 
120     /**
121      * Sets the user id to use when creating the
122      * JMS connection.
123      */
124     public void setUserId(String _userId) {
125         userId = _userId;
126     }
127 
128     /**
129      * Gets the current user id property.
130      */
131     public String getUserId() {
132         return userId;
133     }
134 
135     /**
136      * Sets the password to use when creating the
137      * JMS connection.
138      */
139     public void setPassword(String _password) {
140         password = _password;
141     }
142 
143     /**
144      * Gets the curernt password property.
145      */
146     public String getPassword() {
147         return password;
148     }
149 
150     /**
151      * Returns true if the receiver is the same class and they are
152      * configured for the same properties, and super class also considers
153      * them to be equivalent. This is used by PluginRegistry when determining
154      * if the a similarly configured receiver is being started.
155      *
156      * @param testPlugin The plugin to test equivalency against.
157      * @return boolean True if the testPlugin is equivalent to this plugin.
158      */
159     public boolean isEquivalent(Plugin testPlugin) {
160         // only do full check if an instance of this class
161         if (testPlugin instanceof JMSReceiver) {
162 
163             JMSReceiver receiver = (JMSReceiver) testPlugin;
164 
165             // check for same topic name and super class equivalency
166             return (
167                 topicFactoryName.equals(receiver.getTopicFactoryName()) &&
168                     (jndiPath == null || jndiPath.equals(receiver.getJndiPath())) &&
169                     super.isEquivalent(testPlugin)
170             );
171         }
172 
173         return false;
174     }
175 
176     /**
177      * Returns true if this receiver is active.
178      */
179     public synchronized boolean isActive() {
180         return active;
181     }
182 
183     /**
184      * Sets the flag to indicate if receiver is active or not.
185      */
186     protected synchronized void setActive(boolean _active) {
187         active = _active;
188     }
189 
190     /**
191      * Starts the JMSReceiver with the current options.
192      */
193     public void activateOptions() {
194         if (!isActive()) {
195             try {
196                 remoteInfo = topicFactoryName + ":" + topicName;
197 
198                 Context ctx;
199                 if (jndiPath == null || jndiPath.equals("")) {
200                     ctx = new InitialContext();
201                 } else {
202                     FileInputStream is = new FileInputStream(jndiPath);
203                     Properties p = new Properties();
204                     p.load(is);
205                     is.close();
206                     ctx = new InitialContext(p);
207                 }
208 
209                 // give some more flexibility about the choice of a tab name
210                 providerUrl = (String) ctx.getEnvironment().get(Context.PROVIDER_URL);
211                 TopicConnectionFactory topicConnectionFactory;
212                 topicConnectionFactory =
213                     (TopicConnectionFactory) lookup(ctx, topicFactoryName);
214 
215                 if (userId != null && password != null) {
216                     topicConnection =
217                         topicConnectionFactory.createTopicConnection(userId, password);
218                 } else {
219                     topicConnection =
220                         topicConnectionFactory.createTopicConnection();
221                 }
222 
223                 TopicSession topicSession =
224                     topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
225 
226                 Topic topic = (Topic) ctx.lookup(topicName);
227 
228                 TopicSubscriber topicSubscriber = topicSession.createSubscriber(topic);
229 
230                 topicSubscriber.setMessageListener(this);
231 
232                 topicConnection.start();
233 
234                 setActive(true);
235             } catch (Exception e) {
236                 setActive(false);
237                 if (topicConnection != null) {
238                     try {
239                         topicConnection.close();
240                     } catch (Exception e2) {
241                         // do nothing
242                     }
243                     topicConnection = null;
244                 }
245                 getLogger().error("Could not start JMSReceiver.", e);
246             }
247         }
248     }
249 
250     /**
251      * Called when the receiver should be stopped.
252      */
253     public synchronized void shutdown() {
254         if (isActive()) {
255             // mark this as no longer running
256             setActive(false);
257 
258             if (topicConnection != null) {
259                 try {
260                     topicConnection.close();
261                 } catch (Exception e) {
262                     // do nothing
263                 }
264                 topicConnection = null;
265             }
266         }
267     }
268 
269     public void onMessage(Message message) {
270         try {
271             if (message instanceof ObjectMessage) {
272                 // get the logging event and post it to the repository
273                 ObjectMessage objectMessage = (ObjectMessage) message;
274                 LoggingEvent event = (LoggingEvent) objectMessage.getObject();
275 
276                 // store the known remote info in an event property
277                 event.setProperty("log4j.remoteSourceInfo", remoteInfo);
278                 event.setProperty("log4j.jmsProviderUrl", providerUrl);
279 
280                 doPost(event);
281             } else {
282                 getLogger().warn("Received message is of type " + message.getJMSType()
283                     + ", was expecting ObjectMessage.");
284             }
285         } catch (Exception e) {
286             getLogger().error("Exception thrown while processing incoming message.", e);
287         }
288     }
289 
290     protected Object lookup(Context ctx, String name) throws NamingException {
291         try {
292             return ctx.lookup(name);
293         } catch (NameNotFoundException e) {
294             getLogger().error("Could not find name [" + name + "].");
295             throw e;
296         }
297     }
298 
299 }