JAL-3026 srcjar files for VARNA and log4j
[jalview.git] / srcjar / org / apache / log4j / net / JMSAppender.java
diff --git a/srcjar/org/apache/log4j/net/JMSAppender.java b/srcjar/org/apache/log4j/net/JMSAppender.java
new file mode 100644 (file)
index 0000000..7e1e62e
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.net;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.LoggingEvent;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import java.util.Properties;
+
+/**
+ * A simple appender that publishes events to a JMS Topic. The events
+ * are serialized and transmitted as JMS message type {@link
+ * ObjectMessage}.
+
+ * <p>JMS {@link Topic topics} and {@link TopicConnectionFactory topic
+ * connection factories} are administered objects that are retrieved
+ * using JNDI messaging which in turn requires the retrieval of a JNDI
+ * {@link Context}.
+
+ * <p>There are two common methods for retrieving a JNDI {@link
+ * Context}. If a file resource named <em>jndi.properties</em> is
+ * available to the JNDI API, it will use the information found
+ * therein to retrieve an initial JNDI context. To obtain an initial
+ * context, your code will simply call:
+
+   <pre>
+   InitialContext jndiContext = new InitialContext();
+   </pre>
+  
+ * <p>Calling the no-argument <code>InitialContext()</code> method
+ * will also work from within Enterprise Java Beans (EJBs) because it
+ * is part of the EJB contract for application servers to provide each
+ * bean an environment naming context (ENC).
+    
+ * <p>In the second approach, several predetermined properties are set
+ * and these properties are passed to the <code>InitialContext</code>
+ * constructor to connect to the naming service provider. For example,
+ * to connect to JBoss naming service one would write:
+
+<pre>
+   Properties env = new Properties( );
+   env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
+   env.put(Context.PROVIDER_URL, "jnp://hostname:1099");
+   env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
+   InitialContext jndiContext = new InitialContext(env);
+</pre>
+
+   * where <em>hostname</em> is the host where the JBoss application
+   * server is running.
+   *
+   * <p>To connect to the the naming service of Weblogic application
+   * server one would write:
+
+<pre>
+   Properties env = new Properties( );
+   env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
+   env.put(Context.PROVIDER_URL, "t3://localhost:7001");
+   InitialContext jndiContext = new InitialContext(env);
+</pre>
+
+  * <p>Other JMS providers will obviously require different values.
+  * 
+  * The initial JNDI context can be obtained by calling the
+  * no-argument <code>InitialContext()</code> method in EJBs. Only
+  * clients running in a separate JVM need to be concerned about the
+  * <em>jndi.properties</em> file and calling {@link
+  * InitialContext#InitialContext()} or alternatively correctly
+  * setting the different properties before calling {@link
+  * InitialContext#InitialContext(java.util.Hashtable)} method.
+
+
+   @author Ceki G&uuml;lc&uuml; */
+public class JMSAppender extends AppenderSkeleton {
+
+  String securityPrincipalName;
+  String securityCredentials;
+  String initialContextFactoryName;
+  String urlPkgPrefixes;
+  String providerURL;
+  String topicBindingName;
+  String tcfBindingName;
+  String userName;
+  String password;
+  boolean locationInfo;
+
+  TopicConnection  topicConnection;
+  TopicSession topicSession;
+  TopicPublisher  topicPublisher;
+
+  public
+  JMSAppender() {
+  }
+
+  /**
+     The <b>TopicConnectionFactoryBindingName</b> option takes a
+     string value. Its value will be used to lookup the appropriate
+     <code>TopicConnectionFactory</code> from the JNDI context.
+   */
+  public
+  void setTopicConnectionFactoryBindingName(String tcfBindingName) {
+    this.tcfBindingName = tcfBindingName;
+  }
+
+  /**
+     Returns the value of the <b>TopicConnectionFactoryBindingName</b> option.
+   */
+  public
+  String getTopicConnectionFactoryBindingName() {
+    return tcfBindingName;
+  }
+
+  /**
+     The <b>TopicBindingName</b> option takes a
+     string value. Its value will be used to lookup the appropriate
+     <code>Topic</code> from the JNDI context.
+   */
+  public
+  void setTopicBindingName(String topicBindingName) {
+    this.topicBindingName = topicBindingName;
+  }
+
+  /**
+     Returns the value of the <b>TopicBindingName</b> option.
+   */
+  public
+  String getTopicBindingName() {
+    return topicBindingName;
+  }
+
+
+  /**
+     Returns value of the <b>LocationInfo</b> property which
+     determines whether location (stack) info is sent to the remote
+     subscriber. */
+  public
+  boolean getLocationInfo() {
+    return locationInfo;
+  }
+
+  /**
+   *  Options are activated and become effective only after calling
+   *  this method.*/
+  public void activateOptions() {
+    TopicConnectionFactory  topicConnectionFactory;
+
+    try {
+      Context jndi;
+
+      LogLog.debug("Getting initial context.");
+      if(initialContextFactoryName != null) {
+       Properties env = new Properties( );
+       env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName);
+       if(providerURL != null) {
+         env.put(Context.PROVIDER_URL, providerURL);
+       } else {
+         LogLog.warn("You have set InitialContextFactoryName option but not the "
+                    +"ProviderURL. This is likely to cause problems.");
+       }
+       if(urlPkgPrefixes != null) {
+         env.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+       }
+       
+       if(securityPrincipalName != null) {
+         env.put(Context.SECURITY_PRINCIPAL, securityPrincipalName);
+         if(securityCredentials != null) {
+           env.put(Context.SECURITY_CREDENTIALS, securityCredentials);
+         } else {
+           LogLog.warn("You have set SecurityPrincipalName option but not the "
+                       +"SecurityCredentials. This is likely to cause problems.");
+         }
+       }       
+       jndi = new InitialContext(env);
+      } else {
+       jndi = new InitialContext();
+      }
+
+      LogLog.debug("Looking up ["+tcfBindingName+"]");
+      topicConnectionFactory = (TopicConnectionFactory) lookup(jndi, tcfBindingName);
+      LogLog.debug("About to create TopicConnection.");
+      if(userName != null) {
+       topicConnection = topicConnectionFactory.createTopicConnection(userName, 
+                                                                      password); 
+      } else {
+       topicConnection = topicConnectionFactory.createTopicConnection();
+      }
+
+      LogLog.debug("Creating TopicSession, non-transactional, "
+                  +"in AUTO_ACKNOWLEDGE mode.");
+      topicSession = topicConnection.createTopicSession(false,
+                                                       Session.AUTO_ACKNOWLEDGE);
+
+      LogLog.debug("Looking up topic name ["+topicBindingName+"].");
+      Topic topic = (Topic) lookup(jndi, topicBindingName);
+
+      LogLog.debug("Creating TopicPublisher.");
+      topicPublisher = topicSession.createPublisher(topic);
+      
+      LogLog.debug("Starting TopicConnection.");
+      topicConnection.start();
+
+      jndi.close();
+    } catch(JMSException e) {
+      errorHandler.error("Error while activating options for appender named ["+name+
+                        "].", e, ErrorCode.GENERIC_FAILURE);
+    } catch(NamingException e) {
+      errorHandler.error("Error while activating options for appender named ["+name+
+                        "].", e, ErrorCode.GENERIC_FAILURE);
+    } catch(RuntimeException e) {
+      errorHandler.error("Error while activating options for appender named ["+name+
+                        "].", e, ErrorCode.GENERIC_FAILURE);
+    }
+  }
+
+  protected Object lookup(Context ctx, String name) throws NamingException {
+    try {
+      return ctx.lookup(name);
+    } catch(NameNotFoundException e) {
+      LogLog.error("Could not find name ["+name+"].");
+      throw e;
+    }
+  }
+
+  protected boolean checkEntryConditions() {
+    String fail = null;
+
+    if(this.topicConnection == null) {
+      fail = "No TopicConnection";
+    } else if(this.topicSession == null) {
+      fail = "No TopicSession";
+    } else if(this.topicPublisher == null) {
+      fail = "No TopicPublisher";
+    }
+
+    if(fail != null) {
+      errorHandler.error(fail +" for JMSAppender named ["+name+"].");
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+  /**
+     Close this JMSAppender. Closing releases all resources used by the
+     appender. A closed appender cannot be re-opened. */
+  public synchronized void close() {
+    // The synchronized modifier avoids concurrent append and close operations
+
+    if(this.closed) {
+        return;
+    }
+
+    LogLog.debug("Closing appender ["+name+"].");
+    this.closed = true;
+
+    try {
+      if(topicSession != null) {
+        topicSession.close();
+    }
+      if(topicConnection != null) {
+        topicConnection.close();
+    }
+    } catch(JMSException e) {
+      LogLog.error("Error while closing JMSAppender ["+name+"].", e);
+    } catch(RuntimeException e) {
+      LogLog.error("Error while closing JMSAppender ["+name+"].", e);
+    }
+    // Help garbage collection
+    topicPublisher = null;
+    topicSession = null;
+    topicConnection = null;
+  }
+
+  /**
+     This method called by {@link AppenderSkeleton#doAppend} method to
+     do most of the real appending work.  */
+  public void append(LoggingEvent event) {
+    if(!checkEntryConditions()) {
+      return;
+    }
+
+    try {
+      ObjectMessage msg = topicSession.createObjectMessage();
+      if(locationInfo) {
+       event.getLocationInformation();
+      }
+      msg.setObject(event);
+      topicPublisher.publish(msg);
+    } catch(JMSException e) {
+      errorHandler.error("Could not publish message in JMSAppender ["+name+"].", e,
+                        ErrorCode.GENERIC_FAILURE);
+    } catch(RuntimeException e) {
+      errorHandler.error("Could not publish message in JMSAppender ["+name+"].", e,
+                        ErrorCode.GENERIC_FAILURE);
+    }
+  }
+
+  /**
+   * Returns the value of the <b>InitialContextFactoryName</b> option.
+   * See {@link #setInitialContextFactoryName} for more details on the
+   * meaning of this option.
+   * */
+  public String getInitialContextFactoryName() {
+    return initialContextFactoryName;    
+  }
+  
+  /**
+   * Setting the <b>InitialContextFactoryName</b> method will cause
+   * this <code>JMSAppender</code> instance to use the {@link
+   * InitialContext#InitialContext(Hashtable)} method instead of the
+   * no-argument constructor. If you set this option, you should also
+   * at least set the <b>ProviderURL</b> option.
+   * 
+   * @see #setProviderURL(String)
+   * */
+  public void setInitialContextFactoryName(String initialContextFactoryName) {
+    this.initialContextFactoryName = initialContextFactoryName;
+  }
+
+  public String getProviderURL() {
+    return providerURL;    
+  }
+
+  public void setProviderURL(String providerURL) {
+    this.providerURL = providerURL;
+  }
+
+  String getURLPkgPrefixes( ) {
+    return urlPkgPrefixes;
+  }
+
+  public void setURLPkgPrefixes(String urlPkgPrefixes ) {
+    this.urlPkgPrefixes = urlPkgPrefixes;
+  }
+  
+  public String getSecurityCredentials() {
+    return securityCredentials;    
+  }
+
+  public void setSecurityCredentials(String securityCredentials) {
+    this.securityCredentials = securityCredentials;
+  }
+  
+  
+  public String getSecurityPrincipalName() {
+    return securityPrincipalName;    
+  }
+
+  public void setSecurityPrincipalName(String securityPrincipalName) {
+    this.securityPrincipalName = securityPrincipalName;
+  }
+
+  public String getUserName() {
+    return userName;    
+  }
+
+  /**
+   * The user name to use when {@link
+   * TopicConnectionFactory#createTopicConnection(String, String)
+   * creating a topic session}.  If you set this option, you should
+   * also set the <b>Password</b> option. See {@link
+   * #setPassword(String)}.
+   * */
+  public void setUserName(String userName) {
+    this.userName = userName;
+  }
+
+  public String getPassword() {
+    return password;    
+  }
+
+  /**
+   * The paswword to use when creating a topic session.  
+   */
+  public void setPassword(String password) {
+    this.password = password;
+  }
+
+
+  /**
+      If true, the information sent to the remote subscriber will
+      include caller's location information. By default no location
+      information is sent to the subscriber.  */
+  public void setLocationInfo(boolean locationInfo) {
+    this.locationInfo = locationInfo;
+  }
+
+  /**
+   * Returns the TopicConnection used for this appender.  Only valid after
+   * activateOptions() method has been invoked.
+   */
+  protected TopicConnection  getTopicConnection() {
+    return topicConnection;
+  }
+
+  /**
+   * Returns the TopicSession used for this appender.  Only valid after
+   * activateOptions() method has been invoked.
+   */
+  protected TopicSession  getTopicSession() {
+    return topicSession;
+  }
+
+  /**
+   * Returns the TopicPublisher used for this appender.  Only valid after
+   * activateOptions() method has been invoked.
+   */
+  protected TopicPublisher  getTopicPublisher() {
+    return topicPublisher;
+  }
+  
+  /** 
+   * The JMSAppender sends serialized events and consequently does not
+   * require a layout.
+   */
+  public boolean requiresLayout() {
+    return false;
+  }
+}