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
018package org.apache.logging.log4j.core.appender.mom;
019
020import java.io.Serializable;
021import javax.jms.Connection;
022import javax.jms.ConnectionFactory;
023import javax.jms.Destination;
024import javax.jms.JMSException;
025import javax.jms.Message;
026import javax.jms.MessageConsumer;
027import javax.jms.MessageProducer;
028import javax.jms.Session;
029import javax.naming.NamingException;
030
031import org.apache.logging.log4j.Logger;
032import org.apache.logging.log4j.core.appender.AbstractManager;
033import org.apache.logging.log4j.core.appender.ManagerFactory;
034import org.apache.logging.log4j.core.net.JndiManager;
035import org.apache.logging.log4j.status.StatusLogger;
036
037/**
038 * JMS connection and session manager. Can be used to access MessageProducer, MessageConsumer, and Message objects
039 * involving a configured ConnectionFactory and Destination.
040 */
041public class JmsManager extends AbstractManager {
042
043    private static final Logger LOGGER = StatusLogger.getLogger();
044
045    private static final JmsManagerFactory FACTORY = new JmsManagerFactory();
046
047    private final JndiManager jndiManager;
048    private final Connection connection;
049    private final Session session;
050    private final Destination destination;
051
052    private JmsManager(final String name, final JndiManager jndiManager, final String connectionFactoryName,
053                       final String destinationName, final String username, final String password)
054        throws NamingException, JMSException {
055        super(name);
056        this.jndiManager = jndiManager;
057        final ConnectionFactory connectionFactory = this.jndiManager.lookup(connectionFactoryName);
058        if (username != null && password != null) {
059            this.connection = connectionFactory.createConnection(username, password);
060        } else {
061            this.connection = connectionFactory.createConnection();
062        }
063        this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
064        this.destination = this.jndiManager.lookup(destinationName);
065        this.connection.start();
066    }
067
068    /**
069     * Gets a JmsManager using the specified configuration parameters.
070     *
071     * @param name                  The name to use for this JmsManager.
072     * @param jndiManager           The JndiManager to look up JMS information through.
073     * @param connectionFactoryName The binding name for the {@link javax.jms.ConnectionFactory}.
074     * @param destinationName       The binding name for the {@link javax.jms.Destination}.
075     * @param username              The username to connect with or {@code null} for no authentication.
076     * @param password              The password to use with the given username or {@code null} for no authentication.
077     * @return The JmsManager as configured.
078     */
079    public static JmsManager getJmsManager(final String name, final JndiManager jndiManager,
080                                           final String connectionFactoryName, final String destinationName,
081                                           final String username, final String password) {
082        final JmsConfiguration configuration = new JmsConfiguration(jndiManager, connectionFactoryName, destinationName,
083            username, password);
084        return FACTORY.createManager(name, configuration);
085    }
086
087    /**
088     * Creates a MessageConsumer on this Destination using the current Session.
089     *
090     * @return A MessageConsumer on this Destination.
091     * @throws JMSException
092     */
093    public MessageConsumer createMessageConsumer() throws JMSException {
094        return this.session.createConsumer(this.destination);
095    }
096
097    /**
098     * Creates a MessageProducer on this Destination using the current Session.
099     *
100     * @return A MessageProducer on this Destination.
101     * @throws JMSException
102     */
103    public MessageProducer createMessageProducer() throws JMSException {
104        return this.session.createProducer(this.destination);
105    }
106
107    /**
108     * Creates a TextMessage or ObjectMessage from a Serializable object. For instance, when using a text-based
109     * {@link org.apache.logging.log4j.core.Layout} such as {@link org.apache.logging.log4j.core.layout.PatternLayout},
110     * the {@link org.apache.logging.log4j.core.LogEvent} message will be serialized to a String. When using a
111     * layout such as {@link org.apache.logging.log4j.core.layout.SerializedLayout}, the LogEvent message will be
112     * serialized as a Java object.
113     *
114     * @param object The LogEvent or String message to wrap.
115     * @return A new JMS message containing the provided object.
116     * @throws JMSException
117     */
118    public Message createMessage(final Serializable object) throws JMSException {
119        if (object instanceof String) {
120            return this.session.createTextMessage((String) object);
121        }
122        return this.session.createObjectMessage(object);
123    }
124
125    @Override
126    protected void releaseSub() {
127        try {
128            this.session.close();
129        } catch (final JMSException ignored) {
130        }
131        try {
132            this.connection.close();
133        } catch (final JMSException ignored) {
134        }
135        this.jndiManager.release();
136    }
137
138    private static class JmsConfiguration {
139        private final JndiManager jndiManager;
140        private final String connectionFactoryName;
141        private final String destinationName;
142        private final String username;
143        private final String password;
144
145        private JmsConfiguration(final JndiManager jndiManager, final String connectionFactoryName, final String destinationName,
146                                 final String username, final String password) {
147            this.jndiManager = jndiManager;
148            this.connectionFactoryName = connectionFactoryName;
149            this.destinationName = destinationName;
150            this.username = username;
151            this.password = password;
152        }
153    }
154
155    private static class JmsManagerFactory implements ManagerFactory<JmsManager, JmsConfiguration> {
156
157        @Override
158        public JmsManager createManager(final String name, final JmsConfiguration data) {
159            if (JndiManager.isJndiJmsEnabled()) {
160                try {
161                    return new JmsManager(name, data.jndiManager, data.connectionFactoryName, data.destinationName,
162                        data.username, data.password);
163                } catch (final Exception e) {
164                    LOGGER.error("Error creating JmsManager using ConnectionFactory [{}] and Destination [{}].",
165                        data.connectionFactoryName, data.destinationName, e);
166                    return null;
167                }
168            } else {
169                LOGGER.error("JNDI must be enabled by setting log4j2.enableJndiJms=true");
170                return null;
171            }
172        }
173    }
174
175}