View Javadoc
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.logging.log4j.core.appender.mom;
19  
20  import java.io.Serializable;
21  import java.util.Properties;
22  import java.util.concurrent.TimeUnit;
23  
24  import javax.jms.JMSException;
25  
26  import org.apache.logging.log4j.core.Appender;
27  import org.apache.logging.log4j.core.Filter;
28  import org.apache.logging.log4j.core.Layout;
29  import org.apache.logging.log4j.core.LogEvent;
30  import org.apache.logging.log4j.core.appender.AbstractAppender;
31  import org.apache.logging.log4j.core.appender.AbstractManager;
32  import org.apache.logging.log4j.core.appender.mom.JmsManager.JmsManagerConfiguration;
33  import org.apache.logging.log4j.core.config.Node;
34  import org.apache.logging.log4j.core.config.Property;
35  import org.apache.logging.log4j.core.config.plugins.Plugin;
36  import org.apache.logging.log4j.core.config.plugins.PluginAliases;
37  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
38  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
39  import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
40  import org.apache.logging.log4j.core.net.JndiManager;
41  
42  /**
43   * Generic JMS Appender plugin for both queues and topics. This Appender replaces the previous split ones. However,
44   * configurations set up for the 2.0 version of the JMS appenders will still work.
45   */
46  @Plugin(name = "JMS", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true)
47  @PluginAliases({ "JMSQueue", "JMSTopic" })
48  public class JmsAppender extends AbstractAppender {
49  
50      public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>
51              implements org.apache.logging.log4j.core.util.Builder<JmsAppender> {
52  
53          public static final int DEFAULT_RECONNECT_INTERVAL_MILLIS = 5000;
54  
55          @PluginBuilderAttribute
56          private String factoryName;
57  
58          @PluginBuilderAttribute
59          private String providerUrl;
60  
61          @PluginBuilderAttribute
62          private String urlPkgPrefixes;
63  
64          @PluginBuilderAttribute
65          private String securityPrincipalName;
66  
67          @PluginBuilderAttribute(sensitive = true)
68          private String securityCredentials;
69  
70          @PluginBuilderAttribute
71          @Required(message = "A javax.jms.ConnectionFactory JNDI name must be specified")
72          private String factoryBindingName;
73  
74          @PluginBuilderAttribute
75          @PluginAliases({ "queueBindingName", "topicBindingName" })
76          @Required(message = "A javax.jms.Destination JNDI name must be specified")
77          private String destinationBindingName;
78  
79          @PluginBuilderAttribute
80          private String userName;
81  
82          @PluginBuilderAttribute(sensitive = true)
83          private char[] password;
84  
85          @PluginBuilderAttribute
86          private long reconnectIntervalMillis = DEFAULT_RECONNECT_INTERVAL_MILLIS;
87  
88          @PluginBuilderAttribute
89          private boolean immediateFail;
90  
91          // Programmatic access only for now.
92          private JmsManager jmsManager;
93  
94          private Builder() {
95          }
96  
97          @SuppressWarnings("resource") // actualJmsManager and jndiManager are managed by the JmsAppender
98          @Override
99          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 }