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.Queue;
023    import javax.jms.QueueConnection;
024    import javax.jms.QueueConnectionFactory;
025    import javax.jms.QueueSender;
026    import javax.jms.QueueSession;
027    import javax.jms.Session;
028    import javax.naming.Context;
029    import javax.naming.NamingException;
030    import java.io.Serializable;
031    
032    /**
033     * Manager for a JMS Queue.
034     */
035    public class JMSQueueManager extends AbstractJMSManager {
036    
037        private static final JMSQueueManagerFactory FACTORY = new JMSQueueManagerFactory();
038    
039        private QueueInfo info;
040        private final String factoryBindingName;
041        private final String queueBindingName;
042        private final String userName;
043        private final String password;
044        private final Context context;
045    
046        /**
047         * The Constructor.
048         * @param name The unique name of the connection.
049         * @param context The context.
050         * @param factoryBindingName The factory binding name.
051         * @param queueBindingName 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 JMSQueueManager(final String name, final Context context, final String factoryBindingName,
057                                  final String queueBindingName, final String userName, final String password,
058                                  final QueueInfo info) {
059            super(name);
060            this.context = context;
061            this.factoryBindingName = factoryBindingName;
062            this.queueBindingName = queueBindingName;
063            this.userName = userName;
064            this.password = password;
065            this.info = info;
066        }
067    
068        /**
069         * Obtain a JMSQueueManager.
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 QueueConnectionFactory.
077         * @param queueBindingName The name to use to locate the Queue.
078         * @param userName The userid to use to create the Queue Connection.
079         * @param password The password to use to create the Queue Connection.
080         * @return The JMSQueueManager.
081         */
082        public static JMSQueueManager getJMSQueueManager(final String factoryName, final String providerURL,
083                                                         final String urlPkgPrefixes, final String securityPrincipalName,
084                                                         final String securityCredentials, final String factoryBindingName,
085                                                         final String queueBindingName, final String userName,
086                                                         final String password) {
087    
088            if (factoryBindingName == null) {
089                LOGGER.error("No factory name provided for JMSQueueManager");
090                return null;
091            }
092            if (queueBindingName == null) {
093                LOGGER.error("No topic name provided for JMSQueueManager");
094                return null;
095            }
096    
097            final String name = "JMSQueue:" + factoryBindingName + '.' + queueBindingName;
098            return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
099                securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password));
100        }
101    
102        @Override
103        public synchronized void send(final Serializable object) throws Exception {
104            if (info == null) {
105                info = connect(context, factoryBindingName, queueBindingName, userName, password, false);
106            }
107            try {
108                super.send(object, info.session, info.sender);
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 queueBindingName;
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 queueBindingName, final String userName,
157                               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.queueBindingName = queueBindingName;
165                this.userName = userName;
166                this.password = password;
167            }
168        }
169    
170        private static QueueInfo 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 QueueConnectionFactory factory = (QueueConnectionFactory) lookup(context, factoryBindingName);
175                QueueConnection conn;
176                if (userName != null) {
177                    conn = factory.createQueueConnection(userName, password);
178                } else {
179                    conn = factory.createQueueConnection();
180                }
181                final QueueSession sess = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
182                final Queue queue = (Queue) lookup(context, queueBindingName);
183                final QueueSender sender = sess.createSender(queue);
184                conn.start();
185                return new QueueInfo(conn, sess, sender);
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        /** Queue connection information */
201        private static class QueueInfo {
202            private final QueueConnection conn;
203            private final QueueSession session;
204            private final QueueSender sender;
205    
206            public QueueInfo(final QueueConnection conn, final QueueSession session, final QueueSender sender) {
207                this.conn = conn;
208                this.session = session;
209                this.sender = sender;
210            }
211        }
212    
213        /**
214         * Factory to create the JMSQueueManager.
215         */
216        private static class JMSQueueManagerFactory implements ManagerFactory<JMSQueueManager, FactoryData> {
217    
218            @Override
219            public JMSQueueManager 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 QueueInfo info = connect(ctx, data.factoryBindingName, data.queueBindingName, data.userName,
224                        data.password, true);
225                    return new JMSQueueManager(name, ctx, data.factoryBindingName, data.queueBindingName,
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    }