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 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     // only do full check if an instance of this class
    167     if (testPlugin instanceof JMSReceiver) {
    168
    169       JMSReceiver receiver = (JMSReceiver)testPlugin;
    170
    171       // check for same topic name and super class equivalency
    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         // give some more flexibility about the choice of a tab name
    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             // do nothing
    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       // mark this as no longer running
    258       setActive(false);
    259
    260       if (topicConnection != null) {
    261         try {
    262           topicConnection.close();
    263         } catch (Exception e) {
    264           // do nothing
    265         }
    266         topicConnection = null;
    267       }
    268     }
    269   }
    270
    271   public void onMessage(Message message) {
    272     try {
    273       if(message instanceof  ObjectMessage) {
    274         // get the logging event and post it to the repository
    275       	ObjectMessage objectMessage = (ObjectMessage) message;
    276       	LoggingEvent event = (LoggingEvent) objectMessage.getObject();
    277
    278       	// store the known remote info in an event property
    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 }