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.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    
031    import org.apache.logging.log4j.core.appender.ManagerFactory;
032    
033    /**
034     * Manager for a JMS Queue.
035     */
036    public class JmsQueueManager extends AbstractJmsManager {
037    
038        private static final JMSQueueManagerFactory FACTORY = new JMSQueueManagerFactory();
039    
040        private QueueInfo info;
041        private final String factoryBindingName;
042        private final String queueBindingName;
043        private final String userName;
044        private final String password;
045        private final Context context;
046    
047        /**
048         * The Constructor.
049         * @param name The unique name of the connection.
050         * @param context The context.
051         * @param factoryBindingName The factory binding name.
052         * @param queueBindingName The queue binding name.
053         * @param userName The user name.
054         * @param password The credentials for the user.
055         * @param info The Queue connection info.
056         */
057        protected JmsQueueManager(final String name, final Context context, final String factoryBindingName,
058                                  final String queueBindingName, final String userName, final String password,
059                                  final QueueInfo info) {
060            super(name);
061            this.context = context;
062            this.factoryBindingName = factoryBindingName;
063            this.queueBindingName = queueBindingName;
064            this.userName = userName;
065            this.password = password;
066            this.info = info;
067        }
068    
069        /**
070         * Obtain a JmsQueueManager.
071         * @param factoryName The fully qualified class name of the InitialContextFactory.
072         * @param providerURL The URL of the provider to use.
073         * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
074         * will create a URL context factory
075         * @param securityPrincipalName The name of the identity of the Principal.
076         * @param securityCredentials The security credentials of the Principal.
077         * @param factoryBindingName The name to locate in the Context that provides the QueueConnectionFactory.
078         * @param queueBindingName The name to use to locate the Queue.
079         * @param userName The userid to use to create the Queue Connection.
080         * @param password The password to use to create the Queue Connection.
081         * @return The JmsQueueManager.
082         */
083        public static JmsQueueManager getJmsQueueManager(final String factoryName, final String providerURL,
084                                                         final String urlPkgPrefixes, final String securityPrincipalName,
085                                                         final String securityCredentials, final String factoryBindingName,
086                                                         final String queueBindingName, final String userName,
087                                                         final String password) {
088    
089            if (factoryBindingName == null) {
090                LOGGER.error("No factory name provided for JmsQueueManager");
091                return null;
092            }
093            if (queueBindingName == null) {
094                LOGGER.error("No topic name provided for JmsQueueManager");
095                return null;
096            }
097    
098            final String name = "JMSQueue:" + factoryBindingName + '.' + queueBindingName;
099            return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
100                securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password));
101        }
102    
103        @Override
104        public synchronized void send(final Serializable object) throws Exception {
105            if (info == null) {
106                info = connect(context, factoryBindingName, queueBindingName, userName, password, false);
107            }
108            try {
109                super.send(object, info.session, info.sender);
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 queueBindingName;
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 queueBindingName, final String userName,
158                               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.queueBindingName = queueBindingName;
166                this.userName = userName;
167                this.password = password;
168            }
169        }
170    
171        private static QueueInfo 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 QueueConnectionFactory factory = (QueueConnectionFactory) lookup(context, factoryBindingName);
176                QueueConnection conn;
177                if (userName != null) {
178                    conn = factory.createQueueConnection(userName, password);
179                } else {
180                    conn = factory.createQueueConnection();
181                }
182                final QueueSession sess = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
183                final Queue queue = (Queue) lookup(context, queueBindingName);
184                final QueueSender sender = sess.createSender(queue);
185                conn.start();
186                return new QueueInfo(conn, sess, sender);
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        /** Queue connection information */
202        private static class QueueInfo {
203            private final QueueConnection conn;
204            private final QueueSession session;
205            private final QueueSender sender;
206    
207            public QueueInfo(final QueueConnection conn, final QueueSession session, final QueueSender sender) {
208                this.conn = conn;
209                this.session = session;
210                this.sender = sender;
211            }
212        }
213    
214        /**
215         * Factory to create the JmsQueueManager.
216         */
217        private static class JMSQueueManagerFactory implements ManagerFactory<JmsQueueManager, FactoryData> {
218    
219            @Override
220            public JmsQueueManager 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 QueueInfo info = connect(ctx, data.factoryBindingName, data.queueBindingName, data.userName,
225                        data.password, true);
226                    return new JmsQueueManager(name, ctx, data.factoryBindingName, data.queueBindingName,
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    }