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  package org.apache.logging.log4j.core.net;
18  
19  import java.io.Serializable;
20  import javax.jms.JMSException;
21  import javax.jms.Session;
22  import javax.jms.Topic;
23  import javax.jms.TopicConnection;
24  import javax.jms.TopicConnectionFactory;
25  import javax.jms.TopicPublisher;
26  import javax.jms.TopicSession;
27  import javax.naming.Context;
28  import javax.naming.NamingException;
29  
30  import org.apache.logging.log4j.core.appender.ManagerFactory;
31  
32  /**
33   * Manager for JMS Topic connections.
34   */
35  public class JMSTopicManager extends AbstractJMSManager {
36  
37      private static final JMSTopicManagerFactory FACTORY = new JMSTopicManagerFactory();
38  
39      private TopicInfo info;
40      private final String factoryBindingName;
41      private final String topicBindingName;
42      private final String userName;
43      private final String password;
44      private final Context context;
45      /**
46       * Constructor.
47       * @param name The unique name of the connection.
48       * @param context The context.
49       * @param factoryBindingName The factory binding name.
50       * @param topicBindingName The queue binding name.
51       * @param userName The user name.
52       * @param password The credentials for the user.
53       * @param info The Queue connection info.
54       */
55      protected JMSTopicManager(final String name, final Context context, final String factoryBindingName,
56                                final String topicBindingName, final String userName, final String password,
57                                final TopicInfo info) {
58          super(name);
59          this.context = context;
60          this.factoryBindingName = factoryBindingName;
61          this.topicBindingName = topicBindingName;
62          this.userName = userName;
63          this.password = password;
64          this.info = info;
65      }
66  
67      /**
68       * Obtain a JSMTopicManager.
69       * @param factoryName The fully qualified class name of the InitialContextFactory.
70       * @param providerURL The URL of the provider to use.
71       * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
72       * will create a URL context factory
73       * @param securityPrincipalName The name of the identity of the Principal.
74       * @param securityCredentials The security credentials of the Principal.
75       * @param factoryBindingName The name to locate in the Context that provides the TopicConnectionFactory.
76       * @param topicBindingName The name to use to locate the Topic.
77       * @param userName The userid to use to create the Topic Connection.
78       * @param password The password to use to create the Topic Connection.
79       * @return A JMSTopicManager.
80       */
81      public static JMSTopicManager getJMSTopicManager(final String factoryName, final String providerURL,
82                                                       final String urlPkgPrefixes, final String securityPrincipalName,
83                                                       final String securityCredentials, final String factoryBindingName,
84                                                       final String topicBindingName, final String userName,
85                                                       final String password) {
86  
87          if (factoryBindingName == null) {
88              LOGGER.error("No factory name provided for JMSTopicManager");
89              return null;
90          }
91          if (topicBindingName == null) {
92              LOGGER.error("No topic name provided for JMSTopicManager");
93              return null;
94          }
95  
96          final String name = "JMSTopic:" + factoryBindingName + '.' + topicBindingName;
97          return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
98              securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password));
99      }
100 
101 
102     @Override
103     public void send(final Serializable object) throws Exception {
104         if (info == null) {
105             info = connect(context, factoryBindingName, topicBindingName, userName, password, false);
106         }
107         try {
108             super.send(object, info.session, info.publisher);
109         } catch (final Exception ex) {
110             cleanup(true);
111             throw ex;
112         }
113     }
114 
115     @Override
116     public void releaseSub() {
117         if (info != null) {
118             cleanup(false);
119         }
120     }
121 
122     private void cleanup(final boolean quiet) {
123         try {
124             info.session.close();
125         } catch (final Exception e) {
126             if (!quiet) {
127                 LOGGER.error("Error closing session for " + getName(), e);
128             }
129         }
130         try {
131             info.conn.close();
132         } catch (final Exception e) {
133             if (!quiet) {
134                 LOGGER.error("Error closing connection for " + getName(), e);
135             }
136         }
137         info = null;
138     }
139 
140     /**
141      * Data for the factory.
142      */
143     private static class FactoryData {
144         private final String factoryName;
145         private final String providerURL;
146         private final String urlPkgPrefixes;
147         private final String securityPrincipalName;
148         private final String securityCredentials;
149         private final String factoryBindingName;
150         private final String topicBindingName;
151         private final String userName;
152         private final String password;
153 
154         public FactoryData(final String factoryName, final String providerURL, final String urlPkgPrefixes,
155                            final String securityPrincipalName, final String securityCredentials,
156                            final String factoryBindingName, final String topicBindingName,
157                            final String userName, final String password) {
158             this.factoryName = factoryName;
159             this.providerURL = providerURL;
160             this.urlPkgPrefixes = urlPkgPrefixes;
161             this.securityPrincipalName = securityPrincipalName;
162             this.securityCredentials = securityCredentials;
163             this.factoryBindingName = factoryBindingName;
164             this.topicBindingName = topicBindingName;
165             this.userName = userName;
166             this.password = password;
167         }
168     }
169 
170     private static TopicInfo connect(final Context context, final String factoryBindingName,
171                                      final String queueBindingName, final String userName, final String password,
172                                      final boolean suppress) throws Exception {
173         try {
174             final TopicConnectionFactory factory = (TopicConnectionFactory) lookup(context, factoryBindingName);
175             TopicConnection conn;
176             if (userName != null) {
177                 conn = factory.createTopicConnection(userName, password);
178             } else {
179                 conn = factory.createTopicConnection();
180             }
181             final TopicSession sess = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
182             final Topic topic = (Topic) lookup(context, queueBindingName);
183             final TopicPublisher publisher = sess.createPublisher(topic);
184             conn.start();
185             return new TopicInfo(conn, sess, publisher);
186         } catch (final NamingException ex) {
187             LOGGER.warn("Unable to locate connection factory " + factoryBindingName, ex);
188             if (!suppress) {
189                 throw ex;
190             }
191         } catch (final JMSException ex) {
192             LOGGER.warn("Unable to create connection to queue " + queueBindingName, ex);
193             if (!suppress) {
194                 throw ex;
195             }
196         }
197         return null;
198     }
199 
200     /** Topic connection information */
201     private static class TopicInfo {
202         private final TopicConnection conn;
203         private final TopicSession session;
204         private final TopicPublisher publisher;
205 
206         public TopicInfo(final TopicConnection conn, final TopicSession session, final TopicPublisher publisher) {
207             this.conn = conn;
208             this.session = session;
209             this.publisher = publisher;
210         }
211     }
212 
213     /**
214      * Factory to create the JMSQueueManager.
215      */
216     private static class JMSTopicManagerFactory implements ManagerFactory<JMSTopicManager, FactoryData> {
217 
218         @Override
219         public JMSTopicManager createManager(final String name, final FactoryData data) {
220             try {
221                 final Context ctx = createContext(data.factoryName, data.providerURL, data.urlPkgPrefixes,
222                     data.securityPrincipalName, data.securityCredentials);
223                 final TopicInfo info = connect(ctx, data.factoryBindingName, data.topicBindingName, data.userName,
224                     data.password, true);
225                 return new JMSTopicManager(name, ctx, data.factoryBindingName, data.topicBindingName,
226                     data.userName, data.password, info);
227             } catch (final NamingException ex) {
228                 LOGGER.error("Unable to locate resource", ex);
229             } catch (final Exception ex) {
230                 LOGGER.error("Unable to connect", ex);
231             }
232 
233             return null;
234         }
235     }
236 }