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.plugins.Plugin; 035import org.apache.logging.log4j.core.config.plugins.PluginAliases; 036import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 037import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 038import org.apache.logging.log4j.core.config.plugins.PluginElement; 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 implements org.apache.logging.log4j.core.util.Builder<JmsAppender> { 051 052 public static final int DEFAULT_RECONNECT_INTERVAL_MILLIS = 5000; 053 054 @PluginBuilderAttribute 055 @Required(message = "A name for the JmsAppender must be specified") 056 private String name; 057 058 @PluginBuilderAttribute 059 private String factoryName; 060 061 @PluginBuilderAttribute 062 private String providerUrl; 063 064 @PluginBuilderAttribute 065 private String urlPkgPrefixes; 066 067 @PluginBuilderAttribute 068 private String securityPrincipalName; 069 070 @PluginBuilderAttribute(sensitive = true) 071 private String securityCredentials; 072 073 @PluginBuilderAttribute 074 @Required(message = "A javax.jms.ConnectionFactory JNDI name must be specified") 075 private String factoryBindingName; 076 077 @PluginBuilderAttribute 078 @PluginAliases({ "queueBindingName", "topicBindingName" }) 079 @Required(message = "A javax.jms.Destination JNDI name must be specified") 080 private String destinationBindingName; 081 082 @PluginBuilderAttribute 083 private String userName; 084 085 @PluginBuilderAttribute(sensitive = true) 086 private char[] password; 087 088 @PluginElement("Layout") 089 private Layout<? extends Serializable> layout; 090 091 @PluginElement("Filter") 092 private Filter filter; 093 094 private long reconnectIntervalMillis = DEFAULT_RECONNECT_INTERVAL_MILLIS; 095 096 @PluginBuilderAttribute 097 private boolean ignoreExceptions = true; 098 099 @PluginBuilderAttribute 100 private boolean immediateFail; 101 102 // Programmatic access only for now. 103 private JmsManager jmsManager; 104 105 private Builder() { 106 } 107 108 @SuppressWarnings("resource") // actualJmsManager and jndiManager are managed by the JmsAppender 109 @Override 110 public JmsAppender build() { 111 JmsManager actualJmsManager = jmsManager; 112 JmsManagerConfiguration configuration = null; 113 if (actualJmsManager == null) { 114 final Properties jndiProperties = JndiManager.createProperties(factoryName, providerUrl, urlPkgPrefixes, 115 securityPrincipalName, securityCredentials, null); 116 configuration = new JmsManagerConfiguration(jndiProperties, factoryBindingName, destinationBindingName, 117 userName, password, false, reconnectIntervalMillis); 118 actualJmsManager = AbstractManager.getManager(name, JmsManager.FACTORY, configuration); 119 } 120 if (actualJmsManager == null) { 121 // JmsManagerFactory has already logged an ERROR. 122 return null; 123 } 124 if (layout == null) { 125 LOGGER.error("No layout provided for JmsAppender"); 126 return null; 127 } 128 try { 129 return new JmsAppender(name, filter, layout, ignoreExceptions, actualJmsManager); 130 } catch (final JMSException e) { 131 // Never happens since the ctor no longer actually throws a JMSException. 132 throw new IllegalStateException(e); 133 } 134 } 135 136 public Builder setDestinationBindingName(final String destinationBindingName) { 137 this.destinationBindingName = destinationBindingName; 138 return this; 139 } 140 141 public Builder setFactoryBindingName(final String factoryBindingName) { 142 this.factoryBindingName = factoryBindingName; 143 return this; 144 } 145 146 public Builder setFactoryName(final String factoryName) { 147 this.factoryName = factoryName; 148 return this; 149 } 150 151 public Builder setFilter(final Filter filter) { 152 this.filter = filter; 153 return this; 154 } 155 156 public Builder setIgnoreExceptions(final boolean ignoreExceptions) { 157 this.ignoreExceptions = ignoreExceptions; 158 return this; 159 } 160 161 public Builder setImmediateFail(final boolean immediateFail) { 162 this.immediateFail = immediateFail; 163 return this; 164 } 165 166 public Builder setJmsManager(final JmsManager jmsManager) { 167 this.jmsManager = jmsManager; 168 return this; 169 } 170 171 public Builder setLayout(final Layout<? extends Serializable> layout) { 172 this.layout = layout; 173 return this; 174 } 175 176 public Builder setName(final String name) { 177 this.name = name; 178 return this; 179 } 180 181 public Builder setPassword(final char[] password) { 182 this.password = password; 183 return this; 184 } 185 186 /** 187 * @deprecated Use setPassword(char[]) 188 */ 189 @Deprecated 190 public Builder setPassword(final String password) { 191 this.password = password == null ? null : password.toCharArray(); 192 return this; 193 } 194 195 public Builder setProviderUrl(final String providerUrl) { 196 this.providerUrl = providerUrl; 197 return this; 198 } 199 200 public Builder setReconnectIntervalMillis(final long reconnectIntervalMillis) { 201 this.reconnectIntervalMillis = reconnectIntervalMillis; 202 return this; 203 } 204 205 public Builder setSecurityCredentials(final String securityCredentials) { 206 this.securityCredentials = securityCredentials; 207 return this; 208 } 209 210 public Builder setSecurityPrincipalName(final String securityPrincipalName) { 211 this.securityPrincipalName = securityPrincipalName; 212 return this; 213 } 214 215 public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) { 216 this.urlPkgPrefixes = urlPkgPrefixes; 217 return this; 218 } 219 220 /** 221 * @deprecated Use {@link #setUserName(String)}. 222 */ 223 @Deprecated 224 public Builder setUsername(final String username) { 225 this.userName = username; 226 return this; 227 } 228 229 public Builder setUserName(final String userName) { 230 this.userName = userName; 231 return this; 232 } 233 234 /** 235 * Does not include the password. 236 */ 237 @Override 238 public String toString() { 239 return "Builder [name=" + name + ", factoryName=" + factoryName + ", providerUrl=" + providerUrl 240 + ", urlPkgPrefixes=" + urlPkgPrefixes + ", securityPrincipalName=" + securityPrincipalName 241 + ", securityCredentials=" + securityCredentials + ", factoryBindingName=" + factoryBindingName 242 + ", destinationBindingName=" + destinationBindingName + ", username=" + userName + ", layout=" 243 + layout + ", filter=" + filter + ", ignoreExceptions=" + ignoreExceptions + ", jmsManager=" 244 + jmsManager + "]"; 245 } 246 247 } 248 249 @PluginBuilderFactory 250 public static Builder newBuilder() { 251 return new Builder(); 252 } 253 254 private volatile JmsManager manager; 255 256 /** 257 * 258 * @throws JMSException 259 * not thrown as of 2.9 but retained in the signature for compatibility, will be removed in 3.0 260 */ 261 protected JmsAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout, 262 final boolean ignoreExceptions, final JmsManager manager) throws JMSException { 263 super(name, filter, layout, ignoreExceptions); 264 this.manager = manager; 265 } 266 267 @Override 268 public void append(final LogEvent event) { 269 this.manager.send(event, getLayout().toSerializable(event)); 270 } 271 272 public JmsManager getManager() { 273 return manager; 274 } 275 276 @Override 277 public boolean stop(final long timeout, final TimeUnit timeUnit) { 278 setStopping(); 279 boolean stopped = super.stop(timeout, timeUnit, false); 280 stopped &= this.manager.stop(timeout, timeUnit); 281 setStopped(); 282 return stopped; 283 } 284 285}