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;
018    
019    import org.apache.logging.log4j.core.appender.ManagerFactory;
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    import java.io.Serializable;
031    
032    /**
033     * Manager for JMS Topic connections.
034     */
035    public class JMSTopicManager extends AbstractJMSManager {
036    
037        private static final JMSTopicManagerFactory FACTORY = new JMSTopicManagerFactory();
038    
039        private TopicInfo info;
040        private final String factoryBindingName;
041        private final String topicBindingName;
042        private final String userName;
043        private final String password;
044        private final Context context;
045        /**
046         * Constructor.
047         * @param name The unique name of the connection.
048         * @param context The context.
049         * @param factoryBindingName The factory binding name.
050         * @param topicBindingName The queue binding name.
051         * @param userName The user name.
052         * @param password The credentials for the user.
053         * @param info The Queue connection info.
054         */
055        protected JMSTopicManager(final String name, final Context context, final String factoryBindingName,
056                                  final String topicBindingName, final String userName, final String password,
057                                  final TopicInfo info) {
058            super(name);
059            this.context = context;
060            this.factoryBindingName = factoryBindingName;
061            this.topicBindingName = topicBindingName;
062            this.userName = userName;
063            this.password = password;
064            this.info = info;
065        }
066    
067        /**
068         * Obtain a JSMTopicManager.
069         * @param factoryName The fully qualified class name of the InitialContextFactory.
070         * @param providerURL The URL of the provider to use.
071         * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
072         * will create a URL context factory
073         * @param securityPrincipalName The name of the identity of the Principal.
074         * @param securityCredentials The security credentials of the Principal.
075         * @param factoryBindingName The name to locate in the Context that provides the TopicConnectionFactory.
076         * @param topicBindingName The name to use to locate the Topic.
077         * @param userName The userid to use to create the Topic Connection.
078         * @param password The password to use to create the Topic Connection.
079         * @return A JMSTopicManager.
080         */
081        public static JMSTopicManager getJMSTopicManager(final String factoryName, final String providerURL,
082                                                         final String urlPkgPrefixes, final String securityPrincipalName,
083                                                         final String securityCredentials, final String factoryBindingName,
084                                                         final String topicBindingName, final String userName,
085                                                         final String password) {
086    
087            if (factoryBindingName == null) {
088                LOGGER.error("No factory name provided for JMSTopicManager");
089                return null;
090            }
091            if (topicBindingName == null) {
092                LOGGER.error("No topic name provided for JMSTopicManager");
093                return null;
094            }
095    
096            final String name = "JMSTopic:" + factoryBindingName + '.' + topicBindingName;
097            return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
098                securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password));
099        }
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    }