001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 018package org.apache.logging.log4j.core.appender.mom; 019 020import java.io.Serializable; 021import java.util.Properties; 022import java.util.concurrent.TimeUnit; 023 024import javax.jms.JMSException; 025 026import org.apache.logging.log4j.core.Appender; 027import org.apache.logging.log4j.core.Filter; 028import org.apache.logging.log4j.core.Layout; 029import org.apache.logging.log4j.core.LogEvent; 030import org.apache.logging.log4j.core.appender.AbstractAppender; 031import org.apache.logging.log4j.core.appender.AbstractManager; 032import org.apache.logging.log4j.core.appender.mom.JmsManager.JmsManagerConfiguration; 033import org.apache.logging.log4j.core.config.Node; 034import org.apache.logging.log4j.core.config.Property; 035import org.apache.logging.log4j.core.config.plugins.Plugin; 036import org.apache.logging.log4j.core.config.plugins.PluginAliases; 037import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 038import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 039import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; 040import org.apache.logging.log4j.core.net.JndiManager; 041 042/** 043 * Generic JMS Appender plugin for both queues and topics. This Appender replaces the previous split ones. However, 044 * configurations set up for the 2.0 version of the JMS appenders will still work. 045 */ 046@Plugin(name = "JMS", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true) 047@PluginAliases({ "JMSQueue", "JMSTopic" }) 048public class JmsAppender extends AbstractAppender { 049 050 public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B> 051 implements org.apache.logging.log4j.core.util.Builder<JmsAppender> { 052 053 public static final int DEFAULT_RECONNECT_INTERVAL_MILLIS = 5000; 054 055 @PluginBuilderAttribute 056 private String factoryName; 057 058 @PluginBuilderAttribute 059 private String providerUrl; 060 061 @PluginBuilderAttribute 062 private String urlPkgPrefixes; 063 064 @PluginBuilderAttribute 065 private String securityPrincipalName; 066 067 @PluginBuilderAttribute(sensitive = true) 068 private String securityCredentials; 069 070 @PluginBuilderAttribute 071 @Required(message = "A javax.jms.ConnectionFactory JNDI name must be specified") 072 private String factoryBindingName; 073 074 @PluginBuilderAttribute 075 @PluginAliases({ "queueBindingName", "topicBindingName" }) 076 @Required(message = "A javax.jms.Destination JNDI name must be specified") 077 private String destinationBindingName; 078 079 @PluginBuilderAttribute 080 private String userName; 081 082 @PluginBuilderAttribute(sensitive = true) 083 private char[] password; 084 085 @PluginBuilderAttribute 086 private long reconnectIntervalMillis = DEFAULT_RECONNECT_INTERVAL_MILLIS; 087 088 @PluginBuilderAttribute 089 private boolean immediateFail; 090 091 // Programmatic access only for now. 092 private JmsManager jmsManager; 093 094 private Builder() { 095 } 096 097 @SuppressWarnings("resource") // actualJmsManager and jndiManager are managed by the JmsAppender 098 @Override 099 public JmsAppender build() { 100 JmsManager actualJmsManager = jmsManager; 101 JmsManagerConfiguration configuration = null; 102 if (actualJmsManager == null) { 103 final Properties jndiProperties = JndiManager.createProperties(factoryName, providerUrl, urlPkgPrefixes, 104 securityPrincipalName, securityCredentials, null); 105 configuration = new JmsManagerConfiguration(jndiProperties, factoryBindingName, destinationBindingName, 106 userName, password, false, reconnectIntervalMillis); 107 actualJmsManager = AbstractManager.getManager(getName(), JmsManager.FACTORY, configuration); 108 } 109 if (actualJmsManager == null) { 110 // JmsManagerFactory has already logged an ERROR. 111 return null; 112 } 113 final Layout<? extends Serializable> layout = getLayout(); 114 if (layout == null) { 115 LOGGER.error("No layout provided for JmsAppender"); 116 return null; 117 } 118 try { 119 return new JmsAppender(getName(), getFilter(), layout, isIgnoreExceptions(), getPropertyArray(), 120 actualJmsManager); 121 } catch (final JMSException e) { 122 // Never happens since the ctor no longer actually throws a JMSException. 123 throw new IllegalStateException(e); 124 } 125 } 126 127 public Builder setDestinationBindingName(final String destinationBindingName) { 128 this.destinationBindingName = destinationBindingName; 129 return this; 130 } 131 132 public Builder setFactoryBindingName(final String factoryBindingName) { 133 this.factoryBindingName = factoryBindingName; 134 return this; 135 } 136 137 public Builder setFactoryName(final String factoryName) { 138 this.factoryName = factoryName; 139 return this; 140 } 141 142 public Builder setImmediateFail(final boolean immediateFail) { 143 this.immediateFail = immediateFail; 144 return this; 145 } 146 147 public Builder setJmsManager(final JmsManager jmsManager) { 148 this.jmsManager = jmsManager; 149 return this; 150 } 151 152 public Builder setPassword(final char[] password) { 153 this.password = password; 154 return this; 155 } 156 157 /** 158 * @deprecated Use setPassword(char[]) 159 */ 160 @Deprecated 161 public Builder setPassword(final String password) { 162 this.password = password == null ? null : password.toCharArray(); 163 return this; 164 } 165 166 public Builder setProviderUrl(final String providerUrl) { 167 this.providerUrl = providerUrl; 168 return this; 169 } 170 171 public Builder setReconnectIntervalMillis(final long reconnectIntervalMillis) { 172 this.reconnectIntervalMillis = reconnectIntervalMillis; 173 return this; 174 } 175 176 public Builder setSecurityCredentials(final String securityCredentials) { 177 this.securityCredentials = securityCredentials; 178 return this; 179 } 180 181 public Builder setSecurityPrincipalName(final String securityPrincipalName) { 182 this.securityPrincipalName = securityPrincipalName; 183 return this; 184 } 185 186 public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) { 187 this.urlPkgPrefixes = urlPkgPrefixes; 188 return this; 189 } 190 191 /** 192 * @deprecated Use {@link #setUserName(String)}. 193 */ 194 @Deprecated 195 public Builder setUsername(final String username) { 196 this.userName = username; 197 return this; 198 } 199 200 public Builder setUserName(final String userName) { 201 this.userName = userName; 202 return this; 203 } 204 205 /** 206 * Does not include the password. 207 */ 208 @Override 209 public String toString() { 210 return "Builder [name=" + getName() + ", factoryName=" + factoryName + ", providerUrl=" + providerUrl 211 + ", urlPkgPrefixes=" + urlPkgPrefixes + ", securityPrincipalName=" + securityPrincipalName 212 + ", securityCredentials=" + securityCredentials + ", factoryBindingName=" + factoryBindingName 213 + ", destinationBindingName=" + destinationBindingName + ", username=" + userName + ", layout=" 214 + getLayout() + ", filter=" + getFilter() + ", ignoreExceptions=" + isIgnoreExceptions() 215 + ", jmsManager=" + jmsManager + "]"; 216 } 217 218 } 219 220 @PluginBuilderFactory 221 public static Builder newBuilder() { 222 return new Builder(); 223 } 224 225 private volatile JmsManager manager; 226 227 /** 228 * 229 * @throws JMSException not thrown as of 2.9 but retained in the signature for compatibility, will be removed in 3.0 230 */ 231 protected JmsAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout, 232 final boolean ignoreExceptions, final Property[] properties, final JmsManager manager) throws JMSException { 233 super(name, filter, layout, ignoreExceptions, properties); 234 this.manager = manager; 235 } 236 237 /** 238 * 239 * @throws JMSException not thrown as of 2.9 but retained in the signature for compatibility, will be removed in 3.0 240 * @deprecated 241 */ 242 @Deprecated 243 protected JmsAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout, 244 final boolean ignoreExceptions, final JmsManager manager) throws JMSException { 245 super(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY); 246 this.manager = manager; 247 } 248 249 @Override 250 public void append(final LogEvent event) { 251 this.manager.send(event, toSerializable(event)); 252 } 253 254 public JmsManager getManager() { 255 return manager; 256 } 257 258 @Override 259 public boolean stop(final long timeout, final TimeUnit timeUnit) { 260 setStopping(); 261 boolean stopped = super.stop(timeout, timeUnit, false); 262 stopped &= this.manager.stop(timeout, timeUnit); 263 setStopped(); 264 return stopped; 265 } 266 267}