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.plugins.Plugin;
35  import org.apache.logging.log4j.core.config.plugins.PluginAliases;
36  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
37  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
38  import org.apache.logging.log4j.core.config.plugins.PluginElement;
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 implements org.apache.logging.log4j.core.util.Builder<JmsAppender> {
51  
52          public static final int DEFAULT_RECONNECT_INTERVAL_MILLIS = 5000;
53  
54          @PluginBuilderAttribute
55          @Required(message = "A name for the JmsAppender must be specified")
56          private String name;
57  
58          @PluginBuilderAttribute
59          private String factoryName;
60  
61          @PluginBuilderAttribute
62          private String providerUrl;
63  
64          @PluginBuilderAttribute
65          private String urlPkgPrefixes;
66  
67          @PluginBuilderAttribute
68          private String securityPrincipalName;
69  
70          @PluginBuilderAttribute(sensitive = true)
71          private String securityCredentials;
72  
73          @PluginBuilderAttribute
74          @Required(message = "A javax.jms.ConnectionFactory JNDI name must be specified")
75          private String factoryBindingName;
76  
77          @PluginBuilderAttribute
78          @PluginAliases({ "queueBindingName", "topicBindingName" })
79          @Required(message = "A javax.jms.Destination JNDI name must be specified")
80          private String destinationBindingName;
81  
82          @PluginBuilderAttribute
83          private String userName;
84  
85          @PluginBuilderAttribute(sensitive = true)
86          private char[] password;
87  
88          @PluginElement("Layout")
89          private Layout<? extends Serializable> layout;
90  
91          @PluginElement("Filter")
92          private Filter filter;
93  
94          private long reconnectIntervalMillis = DEFAULT_RECONNECT_INTERVAL_MILLIS;
95  
96          @PluginBuilderAttribute
97          private boolean ignoreExceptions = true;
98  
99          @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, 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 }