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 org.apache.logging.log4j.core.appender.ManagerFactory;
20  
21  import javax.jms.JMSException;
22  import javax.jms.Session;
23  import javax.jms.Topic;
24  import javax.jms.TopicConnection;
25  import javax.jms.TopicConnectionFactory;
26  import javax.jms.TopicPublisher;
27  import javax.jms.TopicSession;
28  import javax.naming.Context;
29  import javax.naming.NamingException;
30  import java.io.Serializable;
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         super.send(object, info.session, info.publisher);
108         try {
109             super.send(object, info.session, info.publisher);
110         } catch (final Exception ex) {
111             cleanup(true);
112             throw ex;
113         }
114     }
115 
116     @Override
117     public void releaseSub() {
118         if (info != null) {
119             cleanup(false);
120         }
121     }
122 
123     private void cleanup(final boolean quiet) {
124         try {
125             info.session.close();
126         } catch (final Exception e) {
127             if (!quiet) {
128                 LOGGER.error("Error closing session for " + getName(), e);
129             }
130         }
131         try {
132             info.conn.close();
133         } catch (final Exception e) {
134             if (!quiet) {
135                 LOGGER.error("Error closing connection for " + getName(), e);
136             }
137         }
138         info = null;
139     }
140 
141     /**
142      * Data for the factory.
143      */
144     private static class FactoryData {
145         private final String factoryName;
146         private final String providerURL;
147         private final String urlPkgPrefixes;
148         private final String securityPrincipalName;
149         private final String securityCredentials;
150         private final String factoryBindingName;
151         private final String topicBindingName;
152         private final String userName;
153         private final String password;
154 
155         public FactoryData(final String factoryName, final String providerURL, final String urlPkgPrefixes,
156                            final String securityPrincipalName, final String securityCredentials,
157                            final String factoryBindingName, final String topicBindingName,
158                            final String userName, final String password) {
159             this.factoryName = factoryName;
160             this.providerURL = providerURL;
161             this.urlPkgPrefixes = urlPkgPrefixes;
162             this.securityPrincipalName = securityPrincipalName;
163             this.securityCredentials = securityCredentials;
164             this.factoryBindingName = factoryBindingName;
165             this.topicBindingName = topicBindingName;
166             this.userName = userName;
167             this.password = password;
168         }
169     }
170 
171     private static TopicInfo connect(final Context context, final String factoryBindingName,
172                                      final String queueBindingName, final String userName, final String password,
173                                      final boolean suppress) throws Exception {
174         try {
175             final TopicConnectionFactory factory = (TopicConnectionFactory) lookup(context, factoryBindingName);
176             TopicConnection conn;
177             if (userName != null) {
178                 conn = factory.createTopicConnection(userName, password);
179             } else {
180                 conn = factory.createTopicConnection();
181             }
182             final TopicSession sess = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
183             final Topic topic = (Topic) lookup(context, queueBindingName);
184             final TopicPublisher publisher = sess.createPublisher(topic);
185             conn.start();
186             return new TopicInfo(conn, sess, publisher);
187         } catch (final NamingException ex) {
188             LOGGER.warn("Unable to locate connection factory " + factoryBindingName, ex);
189             if (!suppress) {
190                 throw ex;
191             }
192         } catch (final JMSException ex) {
193             LOGGER.warn("Unable to create connection to queue " + queueBindingName, ex);
194             if (!suppress) {
195                 throw ex;
196             }
197         }
198         return null;
199     }
200 
201     /** Topic connection information */
202     private static class TopicInfo {
203         private final TopicConnection conn;
204         private final TopicSession session;
205         private final TopicPublisher publisher;
206 
207         public TopicInfo(final TopicConnection conn, final TopicSession session, final TopicPublisher publisher) {
208             this.conn = conn;
209             this.session = session;
210             this.publisher = publisher;
211         }
212     }
213 
214     /**
215      * Factory to create the JMSQueueManager.
216      */
217     private static class JMSTopicManagerFactory implements ManagerFactory<JMSTopicManager, FactoryData> {
218 
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 }