KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > log4j > net > JMSAppender


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.log4j.net;
18
19 import org.apache.log4j.AppenderSkeleton;
20 import org.apache.log4j.spi.LoggingEvent;
21 import org.apache.log4j.spi.ErrorCode;
22 import org.apache.log4j.helpers.LogLog;
23
24 import java.util.Properties JavaDoc;
25 import javax.jms.TopicConnection JavaDoc;
26 import javax.jms.TopicConnectionFactory JavaDoc;
27 import javax.jms.Topic JavaDoc;
28 import javax.jms.TopicPublisher JavaDoc;
29 import javax.jms.TopicSession JavaDoc;
30 import javax.jms.Session JavaDoc;
31 import javax.jms.ObjectMessage JavaDoc;
32 import javax.naming.InitialContext JavaDoc;
33 import javax.naming.Context JavaDoc;
34 import javax.naming.NameNotFoundException JavaDoc;
35 import javax.naming.NamingException JavaDoc;
36
37 /**
38  * A simple appender that publishes events to a JMS Topic. The events
39  * are serialized and transmitted as JMS message type {@link
40  * ObjectMessage}.
41
42  * <p>JMS {@link Topic topics} and {@link TopicConnectionFactory topic
43  * connection factories} are administered objects that are retrieved
44  * using JNDI messaging which in turn requires the retreival of a JNDI
45  * {@link Context}.
46
47  * <p>There are two common methods for retrieving a JNDI {@link
48  * Context}. If a file resource named <em>jndi.properties</em> is
49  * available to the JNDI API, it will use the information found
50  * therein to retrieve an initial JNDI context. To obtain an initial
51  * context, your code will simply call:
52
53    <pre>
54    InitialContext jndiContext = new InitialContext();
55    </pre>
56   
57  * <p>Calling the no-argument <code>InitialContext()</code> method
58  * will also work from within Enterprise Java Beans (EJBs) because it
59  * is part of the EJB contract for application servers to provide each
60  * bean an environment naming context (ENC).
61     
62  * <p>In the second approach, several predetermined properties are set
63  * and these properties are passed to the <code>InitialContext</code>
64  * contructor to connect to the naming service provider. For example,
65  * to connect to JBoss naming service one would write:
66
67 <pre>
68    Properties env = new Properties( );
69    env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
70    env.put(Context.PROVIDER_URL, "jnp://hostname:1099");
71    env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
72    InitialContext jndiContext = new InitialContext(env);
73 </pre>
74
75    * where <em>hostname</em> is the host where the JBoss applicaiton
76    * server is running.
77    *
78    * <p>To connect to the the naming service of Weblogic application
79    * server one would write:
80
81 <pre>
82    Properties env = new Properties( );
83    env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
84    env.put(Context.PROVIDER_URL, "t3://localhost:7001");
85    InitialContext jndiContext = new InitialContext(env);
86 </pre>
87
88   * <p>Other JMS providers will obviously require different values.
89   *
90   * The initial JNDI context can be obtained by calling the
91   * no-argument <code>InitialContext()</code> method in EJBs. Only
92   * clients running in a separate JVM need to be concerned about the
93   * <em>jndi.properties</em> file and calling {@link
94   * InitialContext#InitialContext()} or alternatively correctly
95   * setting the different properties before calling {@link
96   * InitialContext#InitialContext(java.util.Hashtable)} method.
97
98
99    @author Ceki G&uuml;lc&uuml; */

100 public class JMSAppender extends AppenderSkeleton {
101
102   String JavaDoc securityPrincipalName;
103   String JavaDoc securityCredentials;
104   String JavaDoc initialContextFactoryName;
105   String JavaDoc urlPkgPrefixes;
106   String JavaDoc providerURL;
107   String JavaDoc topicBindingName;
108   String JavaDoc tcfBindingName;
109   String JavaDoc userName;
110   String JavaDoc password;
111   boolean locationInfo;
112
113   TopicConnection JavaDoc topicConnection;
114   TopicSession JavaDoc topicSession;
115   TopicPublisher JavaDoc topicPublisher;
116
117   public
118   JMSAppender() {
119   }
120
121   /**
122      The <b>TopicConnectionFactoryBindingName</b> option takes a
123      string value. Its value will be used to lookup the appropriate
124      <code>TopicConnectionFactory</code> from the JNDI context.
125    */

126   public
127   void setTopicConnectionFactoryBindingName(String JavaDoc tcfBindingName) {
128     this.tcfBindingName = tcfBindingName;
129   }
130
131   /**
132      Returns the value of the <b>TopicConnectionFactoryBindingName</b> option.
133    */

134   public
135   String JavaDoc getTopicConnectionFactoryBindingName() {
136     return tcfBindingName;
137   }
138
139   /**
140      The <b>TopicBindingName</b> option takes a
141      string value. Its value will be used to lookup the appropriate
142      <code>Topic</code> from the JNDI context.
143    */

144   public
145   void setTopicBindingName(String JavaDoc topicBindingName) {
146     this.topicBindingName = topicBindingName;
147   }
148
149   /**
150      Returns the value of the <b>TopicBindingName</b> option.
151    */

152   public
153   String JavaDoc getTopicBindingName() {
154     return topicBindingName;
155   }
156
157
158   /**
159      Returns value of the <b>LocationInfo</b> property which
160      determines whether location (stack) info is sent to the remote
161      subscriber. */

162   public
163   boolean getLocationInfo() {
164     return locationInfo;
165   }
166
167   /**
168    * Options are activated and become effective only after calling
169    * this method.*/

170   public void activateOptions() {
171     TopicConnectionFactory JavaDoc topicConnectionFactory;
172
173     try {
174       Context JavaDoc jndi;
175
176       LogLog.debug("Getting initial context.");
177       if(initialContextFactoryName != null) {
178     Properties JavaDoc env = new Properties JavaDoc( );
179     env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName);
180     if(providerURL != null) {
181       env.put(Context.PROVIDER_URL, providerURL);
182     } else {
183       LogLog.warn("You have set InitialContextFactoryName option but not the "
184              +"ProviderURL. This is likely to cause problems.");
185     }
186     if(urlPkgPrefixes != null) {
187       env.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
188     }
189     
190     if(securityPrincipalName != null) {
191       env.put(Context.SECURITY_PRINCIPAL, securityPrincipalName);
192       if(securityCredentials != null) {
193         env.put(Context.SECURITY_CREDENTIALS, securityCredentials);
194       } else {
195         LogLog.warn("You have set SecurityPrincipalName option but not the "
196             +"SecurityCredentials. This is likely to cause problems.");
197       }
198     }
199     jndi = new InitialContext JavaDoc(env);
200       } else {
201     jndi = new InitialContext JavaDoc();
202       }
203
204       LogLog.debug("Looking up ["+tcfBindingName+"]");
205       topicConnectionFactory = (TopicConnectionFactory JavaDoc) lookup(jndi, tcfBindingName);
206       LogLog.debug("About to create TopicConnection.");
207       if(userName != null) {
208     topicConnection = topicConnectionFactory.createTopicConnection(userName,
209                                        password);
210       } else {
211     topicConnection = topicConnectionFactory.createTopicConnection();
212       }
213
214       LogLog.debug("Creating TopicSession, non-transactional, "
215            +"in AUTO_ACKNOWLEDGE mode.");
216       topicSession = topicConnection.createTopicSession(false,
217                             Session.AUTO_ACKNOWLEDGE);
218
219       LogLog.debug("Looking up topic name ["+topicBindingName+"].");
220       Topic JavaDoc topic = (Topic JavaDoc) lookup(jndi, topicBindingName);
221
222       LogLog.debug("Creating TopicPublisher.");
223       topicPublisher = topicSession.createPublisher(topic);
224       
225       LogLog.debug("Starting TopicConnection.");
226       topicConnection.start();
227
228       jndi.close();
229     } catch(Exception JavaDoc e) {
230       errorHandler.error("Error while activating options for appender named ["+name+
231              "].", e, ErrorCode.GENERIC_FAILURE);
232     }
233   }
234
235   protected Object JavaDoc lookup(Context JavaDoc ctx, String JavaDoc name) throws NamingException JavaDoc {
236     try {
237       return ctx.lookup(name);
238     } catch(NameNotFoundException JavaDoc e) {
239       LogLog.error("Could not find name ["+name+"].");
240       throw e;
241     }
242   }
243
244   protected boolean checkEntryConditions() {
245     String JavaDoc fail = null;
246
247     if(this.topicConnection == null) {
248       fail = "No TopicConnection";
249     } else if(this.topicSession == null) {
250       fail = "No TopicSession";
251     } else if(this.topicPublisher == null) {
252       fail = "No TopicPublisher";
253     }
254
255     if(fail != null) {
256       errorHandler.error(fail +" for JMSAppender named ["+name+"].");
257       return false;
258     } else {
259       return true;
260     }
261   }
262
263   /**
264      Close this JMSAppender. Closing releases all resources used by the
265      appender. A closed appender cannot be re-opened. */

266   public synchronized void close() {
267     // The synchronized modifier avoids concurrent append and close operations
268

269     if(this.closed)
270       return;
271
272     LogLog.debug("Closing appender ["+name+"].");
273     this.closed = true;
274
275     try {
276       if(topicSession != null)
277     topicSession.close();
278       if(topicConnection != null)
279     topicConnection.close();
280     } catch(Exception JavaDoc e) {
281       LogLog.error("Error while closing JMSAppender ["+name+"].", e);
282     }
283     // Help garbage collection
284
topicPublisher = null;
285     topicSession = null;
286     topicConnection = null;
287   }
288
289   /**
290      This method called by {@link AppenderSkeleton#doAppend} method to
291      do most of the real appending work. */

292   public void append(LoggingEvent event) {
293     if(!checkEntryConditions()) {
294       return;
295     }
296
297     try {
298       ObjectMessage JavaDoc msg = topicSession.createObjectMessage();
299       if(locationInfo) {
300     event.getLocationInformation();
301       }
302       msg.setObject(event);
303       topicPublisher.publish(msg);
304     } catch(Exception JavaDoc e) {
305       errorHandler.error("Could not publish message in JMSAppender ["+name+"].", e,
306              ErrorCode.GENERIC_FAILURE);
307     }
308   }
309
310   /**
311    * Returns the value of the <b>InitialContextFactoryName</b> option.
312    * See {@link #setInitialContextFactoryName} for more details on the
313    * meaning of this option.
314    * */

315   public String JavaDoc getInitialContextFactoryName() {
316     return initialContextFactoryName;
317   }
318   
319   /**
320    * Setting the <b>InitialContextFactoryName</b> method will cause
321    * this <code>JMSAppender</code> instance to use the {@link
322    * InitialContext#InitialContext(Hashtable)} method instead of the
323    * no-argument constructor. If you set this option, you should also
324    * at least set the <b>ProviderURL</b> option.
325    *
326    * <p>See also {@link #setProviderURL(String)}.
327    * */

328   public void setInitialContextFactoryName(String JavaDoc initialContextFactoryName) {
329     this.initialContextFactoryName = initialContextFactoryName;
330   }
331
332   public String JavaDoc getProviderURL() {
333     return providerURL;
334   }
335
336   public void setProviderURL(String JavaDoc providerURL) {
337     this.providerURL = providerURL;
338   }
339
340   String JavaDoc getURLPkgPrefixes( ) {
341     return urlPkgPrefixes;
342   }
343
344   public void setURLPkgPrefixes(String JavaDoc urlPkgPrefixes ) {
345     this.urlPkgPrefixes = urlPkgPrefixes;
346   }
347   
348   public String JavaDoc getSecurityCredentials() {
349     return securityCredentials;
350   }
351
352   public void setSecurityCredentials(String JavaDoc securityCredentials) {
353     this.securityCredentials = securityCredentials;
354   }
355   
356   
357   public String JavaDoc getSecurityPrincipalName() {
358     return securityPrincipalName;
359   }
360
361   public void setSecurityPrincipalName(String JavaDoc securityPrincipalName) {
362     this.securityPrincipalName = securityPrincipalName;
363   }
364
365   public String JavaDoc getUserName() {
366     return userName;
367   }
368
369   /**
370    * The user name to use when {@link
371    * TopicConnectionFactory#createTopicConnection(String, String)
372    * creating a topic session}. If you set this option, you should
373    * also set the <b>Password</b> option. See {@link
374    * #setPassword(String)}.
375    * */

376   public void setUserName(String JavaDoc userName) {
377     this.userName = userName;
378   }
379
380   public String JavaDoc getPassword() {
381     return password;
382   }
383
384   /**
385    * The paswword to use when creating a topic session.
386    */

387   public void setPassword(String JavaDoc password) {
388     this.password = password;
389   }
390
391
392   /**
393       If true, the information sent to the remote subscriber will
394       include caller's location information. By default no location
395       information is sent to the subscriber. */

396   public void setLocationInfo(boolean locationInfo) {
397     this.locationInfo = locationInfo;
398   }
399
400
401   /**
402    * The JMSAppender sends serialized events and consequently does not
403    * require a layout.
404    * */

405   public boolean requiresLayout() {
406     return false;
407   }
408 }
409
Popular Tags