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