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 @PluginBuilderAttribute 092 private String allowedLdapClasses; 093 094 @PluginBuilderAttribute 095 private String allowedLdapHosts; 096 097 @PluginBuilderAttribute 098 private String allowedJndiProtocols; 099 100 // Programmatic access only for now. 101 private JmsManager jmsManager; 102 103 private Builder() { 104 } 105 106 @SuppressWarnings("resource") // actualJmsManager and jndiManager are managed by the JmsAppender 107 @Override 108 public JmsAppender build() { 109 JmsManager actualJmsManager = jmsManager; 110 JmsManagerConfiguration configuration = null; 111 if (actualJmsManager == null) { 112 Properties additionalProperties = null; 113 if (allowedLdapClasses != null || allowedLdapHosts != null) { 114 additionalProperties = new Properties(); 115 if (allowedLdapHosts != null) { 116 additionalProperties.put(JndiManager.ALLOWED_HOSTS, allowedLdapHosts); 117 } 118 if (allowedLdapClasses != null) { 119 additionalProperties.put(JndiManager.ALLOWED_CLASSES, allowedLdapClasses); 120 } 121 if (allowedJndiProtocols != null) { 122 additionalProperties.put(JndiManager.ALLOWED_PROTOCOLS, allowedJndiProtocols); 123 } 124 } 125 final Properties jndiProperties = JndiManager.createProperties(factoryName, providerUrl, urlPkgPrefixes, 126 securityPrincipalName, securityCredentials, additionalProperties); 127 configuration = new JmsManagerConfiguration(jndiProperties, factoryBindingName, destinationBindingName, 128 userName, password, false, reconnectIntervalMillis); 129 actualJmsManager = AbstractManager.getManager(getName(), JmsManager.FACTORY, configuration); 130 } 131 if (actualJmsManager == null) { 132 // JmsManagerFactory has already logged an ERROR. 133 return null; 134 } 135 final Layout<? extends Serializable> layout = getLayout(); 136 if (layout == null) { 137 LOGGER.error("No layout provided for JmsAppender"); 138 return null; 139 } 140 try { 141 return new JmsAppender(getName(), getFilter(), layout, isIgnoreExceptions(), getPropertyArray(), 142 actualJmsManager); 143 } catch (final JMSException e) { 144 // Never happens since the ctor no longer actually throws a JMSException. 145 throw new IllegalStateException(e); 146 } 147 } 148 149 public Builder setDestinationBindingName(final String destinationBindingName) { 150 this.destinationBindingName = destinationBindingName; 151 return this; 152 } 153 154 public Builder setFactoryBindingName(final String factoryBindingName) { 155 this.factoryBindingName = factoryBindingName; 156 return this; 157 } 158 159 public Builder setFactoryName(final String factoryName) { 160 this.factoryName = factoryName; 161 return this; 162 } 163 164 public Builder setImmediateFail(final boolean immediateFail) { 165 this.immediateFail = immediateFail; 166 return this; 167 } 168 169 public Builder setJmsManager(final JmsManager jmsManager) { 170 this.jmsManager = jmsManager; 171 return this; 172 } 173 174 public Builder setPassword(final char[] password) { 175 this.password = password; 176 return this; 177 } 178 179 /** 180 * @deprecated Use setPassword(char[]) 181 */ 182 @Deprecated 183 public Builder setPassword(final String password) { 184 this.password = password == null ? null : password.toCharArray(); 185 return this; 186 } 187 188 public Builder setProviderUrl(final String providerUrl) { 189 this.providerUrl = providerUrl; 190 return this; 191 } 192 193 public Builder setReconnectIntervalMillis(final long reconnectIntervalMillis) { 194 this.reconnectIntervalMillis = reconnectIntervalMillis; 195 return this; 196 } 197 198 public Builder setSecurityCredentials(final String securityCredentials) { 199 this.securityCredentials = securityCredentials; 200 return this; 201 } 202 203 public Builder setSecurityPrincipalName(final String securityPrincipalName) { 204 this.securityPrincipalName = securityPrincipalName; 205 return this; 206 } 207 208 public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) { 209 this.urlPkgPrefixes = urlPkgPrefixes; 210 return this; 211 } 212 213 /** 214 * @deprecated Use {@link #setUserName(String)}. 215 */ 216 @Deprecated 217 public Builder setUsername(final String username) { 218 this.userName = username; 219 return this; 220 } 221 222 public Builder setUserName(final String userName) { 223 this.userName = userName; 224 return this; 225 } 226 227 public Builder setAllowedLdapClasses(final String allowedLdapClasses) { 228 this.allowedLdapClasses = allowedLdapClasses; 229 return this; 230 } 231 232 public Builder setAllowedLdapHosts(final String allowedLdapHosts) { 233 this.allowedLdapHosts = allowedLdapHosts; 234 return this; 235 } 236 237 public Builder setAllowedJndiProtocols(final String allowedJndiProtocols) { 238 this.allowedJndiProtocols = allowedJndiProtocols; 239 return this; 240 } 241 242 /** 243 * Does not include the password. 244 */ 245 @Override 246 public String toString() { 247 return "Builder [name=" + getName() + ", factoryName=" + factoryName + ", providerUrl=" + providerUrl 248 + ", urlPkgPrefixes=" + urlPkgPrefixes + ", securityPrincipalName=" + securityPrincipalName 249 + ", securityCredentials=" + securityCredentials + ", factoryBindingName=" + factoryBindingName 250 + ", destinationBindingName=" + destinationBindingName + ", username=" + userName + ", layout=" 251 + getLayout() + ", filter=" + getFilter() + ", ignoreExceptions=" + isIgnoreExceptions() 252 + ", jmsManager=" + jmsManager + ", allowedLdapClasses=" + allowedLdapClasses 253 + ", allowedLdapHosts=" + allowedLdapHosts + ", allowedJndiProtocols=" + allowedJndiProtocols + "]"; 254 } 255 256 } 257 258 @PluginBuilderFactory 259 public static Builder newBuilder() { 260 return new Builder(); 261 } 262 263 private volatile JmsManager manager; 264 265 /** 266 * 267 * @throws JMSException not thrown as of 2.9 but retained in the signature for compatibility, will be removed in 3.0 268 */ 269 protected JmsAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout, 270 final boolean ignoreExceptions, final Property[] properties, final JmsManager manager) throws JMSException { 271 super(name, filter, layout, ignoreExceptions, properties); 272 this.manager = manager; 273 } 274 275 /** 276 * 277 * @throws JMSException not thrown as of 2.9 but retained in the signature for compatibility, will be removed in 3.0 278 * @deprecated 279 */ 280 @Deprecated 281 protected JmsAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout, 282 final boolean ignoreExceptions, final JmsManager manager) throws JMSException { 283 super(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY); 284 this.manager = manager; 285 } 286 287 @Override 288 public void append(final LogEvent event) { 289 this.manager.send(event, toSerializable(event)); 290 } 291 292 public JmsManager getManager() { 293 return manager; 294 } 295 296 @Override 297 public boolean stop(final long timeout, final TimeUnit timeUnit) { 298 setStopping(); 299 boolean stopped = super.stop(timeout, timeUnit, false); 300 stopped &= this.manager.stop(timeout, timeUnit); 301 setStopped(); 302 return stopped; 303 } 304 305}