KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > protomatter > syslog > SyslogServer


1 package com.protomatter.syslog;
2
3 /**
4  * {{{ The Protomatter Software License, Version 1.0
5  * derived from The Apache Software License, Version 1.1
6  *
7  * Copyright (c) 1998-2002 Nate Sammons. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution,
22  * if any, must include the following acknowledgment:
23  * "This product includes software developed for the
24  * Protomatter Software Project
25  * (http://protomatter.sourceforge.net/)."
26  * Alternately, this acknowledgment may appear in the software itself,
27  * if and wherever such third-party acknowledgments normally appear.
28  *
29  * 4. The names "Protomatter" and "Protomatter Software Project" must
30  * not be used to endorse or promote products derived from this
31  * software without prior written permission. For written
32  * permission, please contact support@protomatter.com.
33  *
34  * 5. Products derived from this software may not be called "Protomatter",
35  * nor may "Protomatter" appear in their name, without prior written
36  * permission of the Protomatter Software Project
37  * (support@protomatter.com).
38  *
39  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
40  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42  * DISCLAIMED. IN NO EVENT SHALL THE PROTOMATTER SOFTWARE PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
46  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE. }}}
51  */

52
53 import java.io.*;
54 import java.net.*;
55 import java.util.*;
56 import java.text.*;
57
58 import javax.jms.*;
59 import javax.naming.*;
60
61 import com.protomatter.syslog.xml.*;
62 import com.protomatter.util.*;
63
64 /**
65  * A standalone log processing server which either
66  * reads messages from a JMS topic, or through RMI.
67  * Reads configuration information from a properties
68  * file, given as the first command-line argument.
69  * System properties will override ones given in the
70  * properties file.<P>
71  *
72  * Basic properties are:<P>
73  *
74  * <UL><TABLE BORDER=1 CELLPADDING=2 CELLSPACING=0 WIDTH="90%">
75  *
76  * <TR CLASS="TableHeadingColor">
77  * <TD><B>name</B></TD>
78  * <TD><B>value</B></TD>
79  * </TR>
80  *
81  * <TR CLASS="TableRowColor">
82  * <TD VALIGN=TOP><TT>Syslog.config.xml</TT></TD>
83  * <TD>Path to a syslog configuration XML file.
84  * Messages are pulled off of JMS and sent into
85  * the "local" Syslog instance for processing
86  * according to this configuration file.
87  * </TD>
88  * </TR>
89  *
90  * </TABLE></UL><P>
91  *
92  * If the server will listen to a JMS topic for messages,
93  * the following properties are used:<P>
94  *
95  * <UL><TABLE BORDER=1 CELLPADDING=2 CELLSPACING=0 WIDTH="90%">
96  *
97  * <TR CLASS="TableHeadingColor">
98  * <TD><B>name</B></TD>
99  * <TD><B>value</B></TD>
100  * </TR>
101  *
102  * <TR CLASS="TableRowColor">
103  * <TD VALIGN=TOP><TT>jms.topic</TT></TD>
104  * <TD>The JNDI name of the JMS topic to listen to.
105  * </TD>
106  * </TR>
107  *
108  * <TR CLASS="TableRowColor">
109  * <TD VALIGN=TOP><TT>jms.connection.user</TT></TD>
110  * <TD>A location in JNDI where an instance of
111  * <tt>javax.jms.TopicConnectionFactory</tt> can
112  * be found.
113  * </TD>
114  * </TR>
115  *
116  * <TR CLASS="TableRowColor">
117  * <TD VALIGN=TOP><TT>jms.connection.pass</TT></TD>
118  * <TD>Optional. The password to use when creating the
119  * JMS connection. This is <i>not</i> the JNDI
120  * credentials.
121  * </TD>
122  * </TR>
123  *
124  * <TR CLASS="TableRowColor">
125  * <TD VALIGN=TOP><TT>jms.connection.factory</TT></TD>
126  * <TD>The JNDI location of a JMS connection factory to use.
127  * </TD>
128  * </TR>
129  *
130  * <TR CLASS="TableRowColor">
131  * <TD VALIGN=TOP><TT>jms.message.selector</TT></TD>
132  * <TD>Optional. A JMS message selector for choosing
133  * messages that this server will receive.
134  * </TD>
135  * </TR>
136  *
137  * </TABLE></UL><P>
138  *
139  * If the server will bind a log receiver
140  * callback object into JNDI on an application server,
141  * the following properties are used:<P>
142  *
143  * <UL><TABLE BORDER=1 CELLPADDING=2 CELLSPACING=0 WIDTH="90%">
144  *
145  * <TR CLASS="TableHeadingColor">
146  * <TD><B>name</B></TD>
147  * <TD><B>value</B></TD>
148  * </TR>
149  *
150  * <TR CLASS="TableRowColor">
151  * <TD VALIGN=TOP><TT>jndi.name</TT></TD>
152  * <TD> The location in JNDI to bind the
153  * receiver object at. The string
154  * "<tt>com.protomatter.syslog.remote.</tt>"
155  * is prepended to this value. The
156  * value should not contain
157  * the "<TT>.</TT>" character.
158  * </TD>
159  * </TR>
160  *
161  * <TR CLASS="TableRowColor">
162  * <TD VALIGN=TOP><TT>receiver.class</TT></TD>
163  * <TD>Optional. The full classname of an
164  * object that implements the
165  * <TT>{@link RemoteLogReceiver RemoteLogReceiver}</TT> interface.
166  * The object must have a default (no-argument)
167  * constructor. The default value for this
168  * property is
169  * <TT>{@link RemoteLogReceiverImpl com.protomatter.syslog.RemoteLogReceiverImpl}</TT>.
170  * </TD>
171  * </TR>
172  *
173  * </TABLE></UL><P>
174  *
175  * The properties file is also used when creating the
176  * JNDI context (it is passed to the constructor for
177  * <tt>javax.naming.InitialContext</tt>), so the following
178  * properties are also needed, and are specific to the
179  * JNDI provider you're using (WebLogic, etc):<P>
180  *
181  * <UL><TABLE BORDER=1 CELLPADDING=2 CELLSPACING=0 WIDTH="90%">
182  *
183  * <TR CLASS="TableHeadingColor">
184  * <TD><B>name</B></TD>
185  * <TD><B>value</B></TD>
186  * </TR>
187  *
188  * <TR CLASS="TableRowColor">
189  * <TD VALIGN=TOP><TT>java.naming.factory.initial</TT></TD>
190  * <TD>The JNDI provider factory to use. For instance,
191  * with BEA WebLogic, this should be set to
192  * "<tt>weblogic.jndi.WLInitialContextFactory</tt>".
193  * </TD>
194  * </TR>
195  *
196  * <TR CLASS="TableRowColor">
197  * <TD VALIGN=TOP><TT>java.naming.provider.url</TT></TD>
198  * <TD>The JNDI provider URL. With BEA WebLogic, this
199  * should be set to something like
200  * "<tt>t3://<i>hostname</i>:<i>port</i></tt>".
201  * </TD>
202  * </TR>
203  *
204  * </TABLE></UL><P>
205  *
206  * See the
207  * JavaDoc for <tt>javax.naming.Context</tt> for more
208  * information on JNDI connection properties.<P>
209  *
210  * Here's an example that works with BEA WebLogic Server. First,
211  * configure a JMS connection factory and a topic in WLS. Add the
212  * following to your <tt>weblogic.properties</tt> file:<P>
213  *
214  * <UL><TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0 WIDTH="90%">
215  * <TR><TD>
216  * <PRE><B>
217  * ## The JMS topic
218  * weblogic.jms.topic.syslogTopic=\
219  * javax.jms.topic.Syslog
220  *
221  * ## The JMS connection factory
222  * weblogic.jms.connectionFactoryName.syslogTopicFactory=\
223  * javax.jms.connection.syslog
224  *
225  *
226  * ## The Syslog startup class for WLS
227  * weblogic.system.startupClass.syslog=\
228  * com.protomatter.syslog.SyslogT3Startup
229  *
230  * ## The configuration file for Syslog.
231  * java.system.property.Syslog.config.xml=\
232  * /opt/weblogic/weblogic-syslog.xml
233  * </B></PRE>
234  * </TD></TR></TABLE></UL><P>
235  *
236  * Now, configure the syslog instance that runs in WLS. This file is
237  * referenced above in the <tt>weblogic.properties</tt> file as
238  * <tt>/opt/weblogic/weblogic-syslog.xml</tt>. This configuration
239  * includes a JMS logger and a logger that writes to <tt>System.out</tt>.
240  * <P>
241  *
242  * <UL><TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0 WIDTH="90%">
243  * <TR><TD>
244  * <PRE><B>
245  *
246  * &lt;Syslog defaultMask="DEBUG"&gt;
247  * &lt;Logger name="out" class="com.protomatter.syslog.PrintWriterLog"&gt;
248  * &lt;stream&gt;System.out&lt;/stream&gt;
249  * &lt;/Logger&gt;
250  *
251  * &lt;Logger name="jms" class="com.protomatter.syslog.JMSLog"&gt;
252  * &lt;factoryName&gt;javax.jms.connection.syslog&lt;/factoryName&gt;
253  * &lt;topicName&gt;javax.jms.topic.Syslog&lt;/topicName&gt;
254  * &lt;/Logger&gt;
255  * &lt;/Syslog&gt;
256  * </B></PRE>
257  * </TD></TR></TABLE></UL><P>
258  *
259  * Next, configure the Syslog instance that will run inside the
260  * remote log server. This is a simple configuration that just prints
261  * things out to <tt>System.out</tt>. We'll refer to this file
262  * as <tt>/home/nate/syslog.xml</tt>.<P>
263  *
264  * <UL><TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0 WIDTH="90%">
265  * <TR><TD>
266  * <PRE><B>
267  *
268  * &lt;Syslog defaultMask="DEBUG"&gt;
269  * &lt;Logger name="out" class="com.protomatter.syslog.PrintWriterLog"&gt;
270  * &lt;stream&gt;System.out&lt;/stream&gt;
271  * &lt;/Logger&gt;
272  * &lt;/Syslog&gt;
273  * </B></PRE>
274  * </TD></TR></TABLE></UL><P>
275  *
276  * Next, we need a configuration file for the remote log server.
277  * <P>
278  *
279  * <UL><TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0 WIDTH="90%">
280  * <TR><TD>
281  * <PRE><B>
282  * Syslog.config.xml=/home/nate/syslog.xml
283  *
284  * ## The JNDI connection factory to use
285  * java.naming.factory.initial=\
286  * weblogic.jndi.WLInitialContextFactory
287  *
288  * ## The JNDI URL of the server to connect to
289  * java.naming.provider.url=\
290  * t3://hostname:port
291  *
292  * ## The JMS factory location in JNDI
293  * jms.connection.factory=\
294  * javax.jms.connection.syslog
295  *
296  * ## The JMS topic name in JNDI
297  * jms.topic=javax.jms.topic.Syslog
298  * </B></PRE>
299  * </TD></TR></TABLE></UL><P>
300  *
301  * Now, pass this properties file to the <tt>SyslogServer</tt> program as its
302  * only command-line argument. It should come up, connect to the server
303  * and start sucking messages off of JMS.
304  *
305  * @see JMSLog
306  * @see RemoteLog
307  */

308 public class SyslogServer
309 {
310   /**
311    * Private constructor so people don't go around creating these.
312    */

313   private SyslogServer()
314   {
315     super();
316   }
317
318   /**
319    * Start the syslog log server. The first command-line argument
320    * must be the path to a properties file.
321    */

322   public static final void main(String JavaDoc args[])
323   {
324     if (args.length != 1)
325     {
326       System.out.println(Syslog.getResourceString(MessageConstants.SERVER_USAGE_MESSAGE) + ": SyslogServer config-file");
327       System.exit(0);
328     }
329
330     try
331     {
332       System.out.println(MessageFormat.format(
333         Syslog.getResourceString(MessageConstants.SERVER_LOADING_MESSAGE),
334         new Object JavaDoc[] { args[0] }));
335       Properties props = new Properties();
336       props.load(new FileInputStream(new File(args[0])));
337       Properties systemProps = System.getProperties();
338       Enumeration e = systemProps.keys();
339       while (e.hasMoreElements())
340       {
341         String JavaDoc key = (String JavaDoc)e.nextElement();
342         props.put(key, systemProps.getProperty(key));
343       }
344
345       String JavaDoc syslogConfig = props.getProperty("Syslog.config.xml");
346       if (syslogConfig == null)
347       {
348         System.out.println(MessageFormat.format(
349           Syslog.getResourceString(MessageConstants.SERVER_CONFIG_PROP_ERROR_MESSAGE),
350           new Object JavaDoc[] { "Syslog.config.xml" }));
351         System.exit(0);
352       }
353       System.out.println(Syslog.getResourceString(MessageConstants.CONFIGURING_SYSLOG_MESSAGE));
354       SyslogXML.configure(new File(syslogConfig));
355
356       // the properties file needs to have all the connection info.
357
System.out.println(Syslog.getResourceString(MessageConstants.SERVER_INIT_JNDI_MESSAGE));
358       Context ctx = new InitialContext(props);
359
360       String JavaDoc topicName = props.getProperty("jms.topic");
361       if (topicName != null)
362       {
363         System.out.println(Syslog.getResourceString(MessageConstants.SERVER_LISTEN_JMS_TOPIC_MESSAGE));
364         String JavaDoc factoryName = props.getProperty("jms.connection.factory");
365
366         String JavaDoc user = (String JavaDoc)props.getProperty("jms.connection.user");
367         String JavaDoc pass = (String JavaDoc)props.getProperty("jms.connection.pass");
368
369         Topic topic = (Topic)ctx.lookup(topicName);
370
371         TopicConnectionFactory tcf = (TopicConnectionFactory)ctx.lookup(factoryName);
372         TopicConnection connection = null;
373         if (user != null)
374           connection = tcf.createTopicConnection(user, pass);
375         else
376           connection = tcf.createTopicConnection();
377         TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
378
379         String JavaDoc selector = props.getProperty("jms.message.selector");
380
381         TopicSubscriber subscriber = null;
382         if (selector != null)
383           subscriber = session.createSubscriber(topic, selector, false);
384         else
385           subscriber = session.createSubscriber(topic);
386         subscriber.setMessageListener(new InternalMessageListener());
387
388         System.out.println(Syslog.getResourceString(MessageConstants.SERVER_STARTING_JMS_MESSAGE));
389         connection.start();
390       }
391       else
392       {
393         String JavaDoc jndiName = props.getProperty("jndi.name");
394         jndiName = "com.protomatter.syslog.remote." + jndiName;
395         String JavaDoc receiverClass = props.getProperty("receiver.class");
396         if (receiverClass == null)
397           receiverClass = "com.protomatter.syslog.RemoteLogReceiverImpl";
398         System.out.println(MessageFormat.format(
399           Syslog.getResourceString(MessageConstants.SERVER_BINDING_MESSAGE),
400           new Object JavaDoc[] { jndiName }));
401         Class JavaDoc theClass = Class.forName(receiverClass);
402         RemoteLogReceiver receiver = (RemoteLogReceiver)theClass.newInstance();
403
404         ctx.rebind(jndiName, receiver);
405       }
406
407       System.out.println(Syslog.getResourceString(MessageConstants.SERVER_SUSPEND_MESSAGE));
408       Object JavaDoc sync = new Object JavaDoc();
409       synchronized(sync)
410       {
411         sync.wait();
412       }
413     }
414     catch (Exception JavaDoc x)
415     {
416       x.printStackTrace();
417     }
418   }
419
420   private static class InternalMessageListener
421   implements MessageListener, JMSConstants
422   {
423     public void onMessage(Message msg)
424     {
425       try
426       {
427         if (!(msg instanceof TextMessage))
428           return;
429         TextMessage m = (TextMessage)msg;
430         if (!JMS_PROP_MSG_TYPE_VALUE.equals(m.getStringProperty(JMS_PROP_MSG_TYPE)))
431           return;
432
433         long time = m.getLongProperty(JMS_PROP_TIME);
434         String JavaDoc host = m.getStringProperty(JMS_PROP_HOST);
435         String JavaDoc channel = m.getStringProperty(JMS_PROP_CHANNEL);
436         String JavaDoc logger = m.getStringProperty(JMS_PROP_LOGGER);
437         String JavaDoc message = m.getStringProperty(JMS_PROP_MESSAGE);
438         int level = m.getIntProperty(JMS_PROP_LEVEL);
439         String JavaDoc threadName = m.getStringProperty(JMS_PROP_THREAD);
440         String JavaDoc detail = m.getText();
441
442         // drop the message into Syslog locally.
443
Syslog.log(InetAddress.getByName(host), logger, channel, message, detail, level, null, threadName, time);
444       }
445       catch (JMSException x)
446       {
447         x.printStackTrace();
448       }
449       catch (Exception JavaDoc xx)
450       {
451         xx.printStackTrace();
452       }
453     }
454   }
455 }
456
Popular Tags