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