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 */
017package org.apache.logging.log4j.core.net;
018
019import java.io.Serializable;
020import javax.jms.JMSException;
021import javax.jms.Queue;
022import javax.jms.QueueConnection;
023import javax.jms.QueueConnectionFactory;
024import javax.jms.QueueSender;
025import javax.jms.QueueSession;
026import javax.jms.Session;
027import javax.naming.Context;
028import javax.naming.NamingException;
029
030import org.apache.logging.log4j.core.appender.ManagerFactory;
031
032/**
033 * Manager for a JMS Queue.
034 */
035public 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}