org.apache.log4j.net.JMSAppender Maven / Gradle / Ivy
Show all versions of reload4j Show documentation
/*
* 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.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}.
*
*
* 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}.
*
*
* There are two common methods for retrieving a JNDI {@link Context}. If a file
* resource named jndi.properties 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:
*
*
* InitialContext jndiContext = new InitialContext();
*
*
*
* Calling the no-argument InitialContext()
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).
*
*
* In the second approach, several predetermined properties are set and these
* properties are passed to the InitialContext
constructor to
* connect to the naming service provider. For example, to connect to JBoss
* naming service one would write:
*
*
* 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);
*
*
* where hostname is the host where the JBoss application server is
* running.
*
*
* To connect to the the naming service of Weblogic application server one would
* write:
*
*
* 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);
*
*
*
* Other JMS providers will obviously require different values.
*
* The initial JNDI context can be obtained by calling the no-argument
* InitialContext()
method in EJBs. Only clients running in a
* separate JVM need to be concerned about the jndi.properties 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ülcü
*/
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 TopicConnectionFactoryBindingName option takes a string value. Its
* value will be used to lookup the appropriate
* TopicConnectionFactory
from the JNDI context.
*/
public void setTopicConnectionFactoryBindingName(String tcfBindingName) {
this.tcfBindingName = tcfBindingName;
}
/**
* Returns the value of the TopicConnectionFactoryBindingName option.
*/
public String getTopicConnectionFactoryBindingName() {
return tcfBindingName;
}
/**
* The TopicBindingName option takes a string value. Its value will be
* used to lookup the appropriate Topic
from the JNDI context.
*/
public void setTopicBindingName(String topicBindingName) {
this.topicBindingName = topicBindingName;
}
/**
* Returns the value of the TopicBindingName option.
*/
public String getTopicBindingName() {
return topicBindingName;
}
/**
* Returns value of the LocationInfo 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 {
Object result = JNDIUtil.lookupObject(ctx, name);
if (result == null) {
String msg = "Could not find name [" + name + "].";
throw new NamingException(msg);
}
return result;
}
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 InitialContextFactoryName option. See
* {@link #setInitialContextFactoryName} for more details on the meaning of this
* option.
*/
public String getInitialContextFactoryName() {
return initialContextFactoryName;
}
/**
* Setting the InitialContextFactoryName method will cause this
* JMSAppender
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 ProviderURL option.
*
*
* See also {@link #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
* Password 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;
}
}