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    package org.apache.logging.log4j.core.net.jms;
018    
019    import java.io.Serializable;
020    
021    import javax.jms.JMSException;
022    import javax.jms.Session;
023    import javax.jms.Topic;
024    import javax.jms.TopicConnection;
025    import javax.jms.TopicConnectionFactory;
026    import javax.jms.TopicPublisher;
027    import javax.jms.TopicSession;
028    import javax.naming.Context;
029    import javax.naming.NamingException;
030    
031    import org.apache.logging.log4j.core.appender.ManagerFactory;
032    
033    /**
034     * Manager for JMS Topic connections.
035     */
036    public class JmsTopicManager extends AbstractJmsManager {
037    
038        private static final JMSTopicManagerFactory FACTORY = new JMSTopicManagerFactory();
039    
040        private TopicInfo info;
041        private final String factoryBindingName;
042        private final String topicBindingName;
043        private final String userName;
044        private final String password;
045        private final Context context;
046        /**
047         * Constructor.
048         * @param name The unique name of the connection.
049         * @param context The context.
050         * @param factoryBindingName The factory binding name.
051         * @param topicBindingName The queue binding name.
052         * @param userName The user name.
053         * @param password The credentials for the user.
054         * @param info The Queue connection info.
055         */
056        protected JmsTopicManager(final String name, final Context context, final String factoryBindingName,
057                                  final String topicBindingName, final String userName, final String password,
058                                  final TopicInfo info) {
059            super(name);
060            this.context = context;
061            this.factoryBindingName = factoryBindingName;
062            this.topicBindingName = topicBindingName;
063            this.userName = userName;
064            this.password = password;
065            this.info = info;
066        }
067    
068        /**
069         * Obtain a JSMTopicManager.
070         * @param factoryName The fully qualified class name of the InitialContextFactory.
071         * @param providerURL The URL of the provider to use.
072         * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
073         * will create a URL context factory
074         * @param securityPrincipalName The name of the identity of the Principal.
075         * @param securityCredentials The security credentials of the Principal.
076         * @param factoryBindingName The name to locate in the Context that provides the TopicConnectionFactory.
077         * @param topicBindingName The name to use to locate the Topic.
078         * @param userName The userid to use to create the Topic Connection.
079         * @param password The password to use to create the Topic Connection.
080         * @return A JmsTopicManager.
081         */
082        public static JmsTopicManager getJmsTopicManager(final String factoryName, final String providerURL,
083                                                         final String urlPkgPrefixes, final String securityPrincipalName,
084                                                         final String securityCredentials, final String factoryBindingName,
085                                                         final String topicBindingName, final String userName,
086                                                         final String password) {
087    
088            if (factoryBindingName == null) {
089                LOGGER.error("No factory name provided for JmsTopicManager");
090                return null;
091            }
092            if (topicBindingName == null) {
093                LOGGER.error("No topic name provided for JmsTopicManager");
094                return null;
095            }
096    
097            final String name = "JMSTopic:" + factoryBindingName + '.' + topicBindingName;
098            return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
099                securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password));
100        }
101    
102    
103        @Override
104        public void send(final Serializable object) throws Exception {
105            if (info == null) {
106                info = connect(context, factoryBindingName, topicBindingName, userName, password, false);
107            }
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            @Override
220            public JmsTopicManager createManager(final String name, final FactoryData data) {
221                try {
222                    final Context ctx = createContext(data.factoryName, data.providerURL, data.urlPkgPrefixes,
223                        data.securityPrincipalName, data.securityCredentials);
224                    final TopicInfo info = connect(ctx, data.factoryBindingName, data.topicBindingName, data.userName,
225                        data.password, true);
226                    return new JmsTopicManager(name, ctx, data.factoryBindingName, data.topicBindingName,
227                        data.userName, data.password, info);
228                } catch (final NamingException ex) {
229                    LOGGER.error("Unable to locate resource", ex);
230                } catch (final Exception ex) {
231                    LOGGER.error("Unable to connect", ex);
232                }
233    
234                return null;
235            }
236        }
237    }