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.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 031 import org.apache.logging.log4j.core.appender.ManagerFactory; 032 033 /** 034 * Manager for JMS Topic connections. 035 */ 036 public class JmsTopicManager extends AbstractJmsManager { 037 038 private static final JMSTopicManagerFactory FACTORY = new JMSTopicManagerFactory(); 039 040 private TopicInfo info; 041 private final String factoryBindingName; 042 private final String topicBindingName; 043 private final String userName; 044 private final String password; 045 private final Context context; 046 /** 047 * Constructor. 048 * @param name The unique name of the connection. 049 * @param context The context. 050 * @param factoryBindingName The factory binding name. 051 * @param topicBindingName 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 JmsTopicManager(final String name, final Context context, final String factoryBindingName, 057 final String topicBindingName, final String userName, final String password, 058 final TopicInfo info) { 059 super(name); 060 this.context = context; 061 this.factoryBindingName = factoryBindingName; 062 this.topicBindingName = topicBindingName; 063 this.userName = userName; 064 this.password = password; 065 this.info = info; 066 } 067 068 /** 069 * Obtain a JSMTopicManager. 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 TopicConnectionFactory. 077 * @param topicBindingName The name to use to locate the Topic. 078 * @param userName The userid to use to create the Topic Connection. 079 * @param password The password to use to create the Topic Connection. 080 * @return A JmsTopicManager. 081 */ 082 public static JmsTopicManager getJmsTopicManager(final String factoryName, final String providerURL, 083 final String urlPkgPrefixes, final String securityPrincipalName, 084 final String securityCredentials, final String factoryBindingName, 085 final String topicBindingName, final String userName, 086 final String password) { 087 088 if (factoryBindingName == null) { 089 LOGGER.error("No factory name provided for JmsTopicManager"); 090 return null; 091 } 092 if (topicBindingName == null) { 093 LOGGER.error("No topic name provided for JmsTopicManager"); 094 return null; 095 } 096 097 final String name = "JMSTopic:" + factoryBindingName + '.' + topicBindingName; 098 return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes, 099 securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password)); 100 } 101 102 103 @Override 104 public void send(final Serializable object) throws Exception { 105 if (info == null) { 106 info = connect(context, factoryBindingName, topicBindingName, userName, password, false); 107 } 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 @Override 220 public JmsTopicManager 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 TopicInfo info = connect(ctx, data.factoryBindingName, data.topicBindingName, data.userName, 225 data.password, true); 226 return new JmsTopicManager(name, ctx, data.factoryBindingName, data.topicBindingName, 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 }