Home » apache-log4j-1.2.15 » org.apache » log4j » net » [javadoc | source]
    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 org.apache.log4j.AppenderSkeleton;
   21   import org.apache.log4j.spi.LoggingEvent;
   22   import org.apache.log4j.spi.ErrorCode;
   23   import org.apache.log4j.helpers.LogLog;
   24   
   25   import java.util.Properties;
   26   import javax.jms.TopicConnection;
   27   import javax.jms.TopicConnectionFactory;
   28   import javax.jms.Topic;
   29   import javax.jms.TopicPublisher;
   30   import javax.jms.TopicSession;
   31   import javax.jms.Session;
   32   import javax.jms.ObjectMessage;
   33   import javax.naming.InitialContext;
   34   import javax.naming.Context;
   35   import javax.naming.NameNotFoundException;
   36   import javax.naming.NamingException;
   37   
   38   /**
   39    * A simple appender that publishes events to a JMS Topic. The events
   40    * are serialized and transmitted as JMS message type {@link
   41    * ObjectMessage}.
   42   
   43    * <p>JMS {@link Topic topics} and {@link TopicConnectionFactory topic
   44    * connection factories} are administered objects that are retrieved
   45    * using JNDI messaging which in turn requires the retreival of a JNDI
   46    * {@link Context}.
   47   
   48    * <p>There are two common methods for retrieving a JNDI {@link
   49    * Context}. If a file resource named <em>jndi.properties</em> is
   50    * available to the JNDI API, it will use the information found
   51    * therein to retrieve an initial JNDI context. To obtain an initial
   52    * context, your code will simply call:
   53   
   54      <pre>
   55      InitialContext jndiContext = new InitialContext();
   56      </pre>
   57     
   58    * <p>Calling the no-argument <code>InitialContext()</code> method
   59    * will also work from within Enterprise Java Beans (EJBs) because it
   60    * is part of the EJB contract for application servers to provide each
   61    * bean an environment naming context (ENC).
   62       
   63    * <p>In the second approach, several predetermined properties are set
   64    * and these properties are passed to the <code>InitialContext</code>
   65    * contructor to connect to the naming service provider. For example,
   66    * to connect to JBoss naming service one would write:
   67   
   68   <pre>
   69      Properties env = new Properties( );
   70      env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
   71      env.put(Context.PROVIDER_URL, "jnp://hostname:1099");
   72      env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
   73      InitialContext jndiContext = new InitialContext(env);
   74   </pre>
   75   
   76      * where <em>hostname</em> is the host where the JBoss applicaiton
   77      * server is running.
   78      *
   79      * <p>To connect to the the naming service of Weblogic application
   80      * server one would write:
   81   
   82   <pre>
   83      Properties env = new Properties( );
   84      env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
   85      env.put(Context.PROVIDER_URL, "t3://localhost:7001");
   86      InitialContext jndiContext = new InitialContext(env);
   87   </pre>
   88   
   89     * <p>Other JMS providers will obviously require different values.
   90     * 
   91     * The initial JNDI context can be obtained by calling the
   92     * no-argument <code>InitialContext()</code> method in EJBs. Only
   93     * clients running in a separate JVM need to be concerned about the
   94     * <em>jndi.properties</em> file and calling {@link
   95     * InitialContext#InitialContext()} or alternatively correctly
   96     * setting the different properties before calling {@link
   97     * InitialContext#InitialContext(java.util.Hashtable)} method.
   98   
   99   
  100      @author Ceki G&uuml;lc&uuml; */
  101   public class JMSAppender extends AppenderSkeleton {
  102   
  103     String securityPrincipalName;
  104     String securityCredentials;
  105     String initialContextFactoryName;
  106     String urlPkgPrefixes;
  107     String providerURL;
  108     String topicBindingName;
  109     String tcfBindingName;
  110     String userName;
  111     String password;
  112     boolean locationInfo;
  113   
  114     TopicConnection  topicConnection;
  115     TopicSession topicSession;
  116     TopicPublisher  topicPublisher;
  117   
  118     public
  119     JMSAppender() {
  120     }
  121   
  122     /**
  123        The <b>TopicConnectionFactoryBindingName</b> option takes a
  124        string value. Its value will be used to lookup the appropriate
  125        <code>TopicConnectionFactory</code> from the JNDI context.
  126      */
  127     public
  128     void setTopicConnectionFactoryBindingName(String tcfBindingName) {
  129       this.tcfBindingName = tcfBindingName;
  130     }
  131   
  132     /**
  133        Returns the value of the <b>TopicConnectionFactoryBindingName</b> option.
  134      */
  135     public
  136     String getTopicConnectionFactoryBindingName() {
  137       return tcfBindingName;
  138     }
  139   
  140     /**
  141        The <b>TopicBindingName</b> option takes a
  142        string value. Its value will be used to lookup the appropriate
  143        <code>Topic</code> from the JNDI context.
  144      */
  145     public
  146     void setTopicBindingName(String topicBindingName) {
  147       this.topicBindingName = topicBindingName;
  148     }
  149   
  150     /**
  151        Returns the value of the <b>TopicBindingName</b> option.
  152      */
  153     public
  154     String getTopicBindingName() {
  155       return topicBindingName;
  156     }
  157   
  158   
  159     /**
  160        Returns value of the <b>LocationInfo</b> property which
  161        determines whether location (stack) info is sent to the remote
  162        subscriber. */
  163     public
  164     boolean getLocationInfo() {
  165       return locationInfo;
  166     }
  167   
  168     /**
  169      *  Options are activated and become effective only after calling
  170      *  this method.*/
  171     public void activateOptions() {
  172       TopicConnectionFactory  topicConnectionFactory;
  173   
  174       try {
  175         Context jndi;
  176   
  177         LogLog.debug("Getting initial context.");
  178         if(initialContextFactoryName != null) {
  179   	Properties env = new Properties( );
  180   	env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName);
  181   	if(providerURL != null) {
  182   	  env.put(Context.PROVIDER_URL, providerURL);
  183   	} else {
  184   	  LogLog.warn("You have set InitialContextFactoryName option but not the "
  185   		     +"ProviderURL. This is likely to cause problems.");
  186   	}
  187   	if(urlPkgPrefixes != null) {
  188   	  env.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
  189   	}
  190   	
  191   	if(securityPrincipalName != null) {
  192   	  env.put(Context.SECURITY_PRINCIPAL, securityPrincipalName);
  193   	  if(securityCredentials != null) {
  194   	    env.put(Context.SECURITY_CREDENTIALS, securityCredentials);
  195   	  } else {
  196   	    LogLog.warn("You have set SecurityPrincipalName option but not the "
  197   			+"SecurityCredentials. This is likely to cause problems.");
  198   	  }
  199   	}	
  200   	jndi = new InitialContext(env);
  201         } else {
  202   	jndi = new InitialContext();
  203         }
  204   
  205         LogLog.debug("Looking up ["+tcfBindingName+"]");
  206         topicConnectionFactory = (TopicConnectionFactory) lookup(jndi, tcfBindingName);
  207         LogLog.debug("About to create TopicConnection.");
  208         if(userName != null) {
  209   	topicConnection = topicConnectionFactory.createTopicConnection(userName, 
  210   								       password); 
  211         } else {
  212   	topicConnection = topicConnectionFactory.createTopicConnection();
  213         }
  214   
  215         LogLog.debug("Creating TopicSession, non-transactional, "
  216   		   +"in AUTO_ACKNOWLEDGE mode.");
  217         topicSession = topicConnection.createTopicSession(false,
  218   							Session.AUTO_ACKNOWLEDGE);
  219   
  220         LogLog.debug("Looking up topic name ["+topicBindingName+"].");
  221         Topic topic = (Topic) lookup(jndi, topicBindingName);
  222   
  223         LogLog.debug("Creating TopicPublisher.");
  224         topicPublisher = topicSession.createPublisher(topic);
  225         
  226         LogLog.debug("Starting TopicConnection.");
  227         topicConnection.start();
  228   
  229         jndi.close();
  230       } catch(Exception e) {
  231         errorHandler.error("Error while activating options for appender named ["+name+
  232   			 "].", e, ErrorCode.GENERIC_FAILURE);
  233       }
  234     }
  235   
  236     protected Object lookup(Context ctx, String name) throws NamingException {
  237       try {
  238         return ctx.lookup(name);
  239       } catch(NameNotFoundException e) {
  240         LogLog.error("Could not find name ["+name+"].");
  241         throw e;
  242       }
  243     }
  244   
  245     protected boolean checkEntryConditions() {
  246       String fail = null;
  247   
  248       if(this.topicConnection == null) {
  249         fail = "No TopicConnection";
  250       } else if(this.topicSession == null) {
  251         fail = "No TopicSession";
  252       } else if(this.topicPublisher == null) {
  253         fail = "No TopicPublisher";
  254       }
  255   
  256       if(fail != null) {
  257         errorHandler.error(fail +" for JMSAppender named ["+name+"].");
  258         return false;
  259       } else {
  260         return true;
  261       }
  262     }
  263   
  264     /**
  265        Close this JMSAppender. Closing releases all resources used by the
  266        appender. A closed appender cannot be re-opened. */
  267     public synchronized void close() {
  268       // The synchronized modifier avoids concurrent append and close operations
  269   
  270       if(this.closed)
  271         return;
  272   
  273       LogLog.debug("Closing appender ["+name+"].");
  274       this.closed = true;
  275   
  276       try {
  277         if(topicSession != null)
  278   	topicSession.close();
  279         if(topicConnection != null)
  280   	topicConnection.close();
  281       } catch(Exception e) {
  282         LogLog.error("Error while closing JMSAppender ["+name+"].", e);
  283       }
  284       // Help garbage collection
  285       topicPublisher = null;
  286       topicSession = null;
  287       topicConnection = null;
  288     }
  289   
  290     /**
  291        This method called by {@link AppenderSkeleton#doAppend} method to
  292        do most of the real appending work.  */
  293     public void append(LoggingEvent event) {
  294       if(!checkEntryConditions()) {
  295         return;
  296       }
  297   
  298       try {
  299         ObjectMessage msg = topicSession.createObjectMessage();
  300         if(locationInfo) {
  301   	event.getLocationInformation();
  302         }
  303         msg.setObject(event);
  304         topicPublisher.publish(msg);
  305       } catch(Exception e) {
  306         errorHandler.error("Could not publish message in JMSAppender ["+name+"].", e,
  307   			 ErrorCode.GENERIC_FAILURE);
  308       }
  309     }
  310   
  311     /**
  312      * Returns the value of the <b>InitialContextFactoryName</b> option.
  313      * See {@link #setInitialContextFactoryName} for more details on the
  314      * meaning of this option.
  315      * */
  316     public String getInitialContextFactoryName() {
  317       return initialContextFactoryName;    
  318     }
  319     
  320     /**
  321      * Setting the <b>InitialContextFactoryName</b> method will cause
  322      * this <code>JMSAppender</code> instance to use the {@link
  323      * InitialContext#InitialContext(Hashtable)} method instead of the
  324      * no-argument constructor. If you set this option, you should also
  325      * at least set the <b>ProviderURL</b> option.
  326      * 
  327      * <p>See also {@link #setProviderURL(String)}.
  328      * */
  329     public void setInitialContextFactoryName(String initialContextFactoryName) {
  330       this.initialContextFactoryName = initialContextFactoryName;
  331     }
  332   
  333     public String getProviderURL() {
  334       return providerURL;    
  335     }
  336   
  337     public void setProviderURL(String providerURL) {
  338       this.providerURL = providerURL;
  339     }
  340   
  341     String getURLPkgPrefixes( ) {
  342       return urlPkgPrefixes;
  343     }
  344   
  345     public void setURLPkgPrefixes(String urlPkgPrefixes ) {
  346       this.urlPkgPrefixes = urlPkgPrefixes;
  347     }
  348     
  349     public String getSecurityCredentials() {
  350       return securityCredentials;    
  351     }
  352   
  353     public void setSecurityCredentials(String securityCredentials) {
  354       this.securityCredentials = securityCredentials;
  355     }
  356     
  357     
  358     public String getSecurityPrincipalName() {
  359       return securityPrincipalName;    
  360     }
  361   
  362     public void setSecurityPrincipalName(String securityPrincipalName) {
  363       this.securityPrincipalName = securityPrincipalName;
  364     }
  365   
  366     public String getUserName() {
  367       return userName;    
  368     }
  369   
  370     /**
  371      * The user name to use when {@link
  372      * TopicConnectionFactory#createTopicConnection(String, String)
  373      * creating a topic session}.  If you set this option, you should
  374      * also set the <b>Password</b> option. See {@link
  375      * #setPassword(String)}.
  376      * */
  377     public void setUserName(String userName) {
  378       this.userName = userName;
  379     }
  380   
  381     public String getPassword() {
  382       return password;    
  383     }
  384   
  385     /**
  386      * The paswword to use when creating a topic session.  
  387      */
  388     public void setPassword(String password) {
  389       this.password = password;
  390     }
  391   
  392   
  393     /**
  394         If true, the information sent to the remote subscriber will
  395         include caller's location information. By default no location
  396         information is sent to the subscriber.  */
  397     public void setLocationInfo(boolean locationInfo) {
  398       this.locationInfo = locationInfo;
  399     }
  400   
  401     /**
  402      * Returns the TopicConnection used for this appender.  Only valid after
  403      * activateOptions() method has been invoked.
  404      */
  405     protected TopicConnection  getTopicConnection() {
  406       return topicConnection;
  407     }
  408   
  409     /**
  410      * Returns the TopicSession used for this appender.  Only valid after
  411      * activateOptions() method has been invoked.
  412      */
  413     protected TopicSession  getTopicSession() {
  414       return topicSession;
  415     }
  416   
  417     /**
  418      * Returns the TopicPublisher used for this appender.  Only valid after
  419      * activateOptions() method has been invoked.
  420      */
  421     protected TopicPublisher  getTopicPublisher() {
  422       return topicPublisher;
  423     }
  424     
  425     /** 
  426      * The JMSAppender sends serialized events and consequently does not
  427      * require a layout.
  428      */
  429     public boolean requiresLayout() {
  430       return false;
  431     }
  432   }

Save This Page
Home » apache-log4j-1.2.15 » org.apache » log4j » net » [javadoc | source]