package org.xdi.oxauth.audit; import java.io.IOException; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ejb.Asynchronous; import javax.ejb.DependsOn; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.event.Observes; import javax.inject.Inject; import javax.inject.Named; import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.QueueConnection; import javax.jms.QueueSession; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.pool.PooledConnectionFactory; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.BooleanUtils; import org.slf4j.Logger; import org.xdi.oxauth.model.audit.OAuth2AuditLog; import org.xdi.oxauth.model.configuration.AppConfiguration; import org.xdi.oxauth.util.ServerUtil; import org.xdi.service.cdi.event.ConfigurationUpdate; import com.google.common.base.Objects; @Named @ApplicationScoped @DependsOn("appInitializer") public class ApplicationAuditLogger { @Inject private Logger log; private final String BROKER_URL_PREFIX = "failover:("; private final String BROKER_URL_SUFFIX = ")?timeout=5000&jms.useAsyncSend=true"; private final int ACK_MODE = Session.AUTO_ACKNOWLEDGE; private final String CLIENT_QUEUE_NAME = "oauth2.audit.logging"; private final boolean transacted = false; private volatile PooledConnectionFactory pooledConnectionFactory; private Set<String> jmsBrokerURISet; private String jmsUserName; private String jmsPassword; @Inject private AppConfiguration appConfiguration; private final ReentrantLock lock = new ReentrantLock(); private boolean updateState; private Boolean enabledOAuthAuditnLogging; public void updateConfiguration(@Observes @ConfigurationUpdate AppConfiguration appConfiguration) { this.updateState = true; } @PostConstruct public void init() { if (BooleanUtils.isNotTrue(isEnabledOAuthAuditnLogging())) { return; } tryToEstablishJMSConnection(); } @Asynchronous public void sendMessage(OAuth2AuditLog oAuth2AuditLog) { if (BooleanUtils.isNotTrue(isEnabledOAuthAuditnLogging())) { return; } if ((this.pooledConnectionFactory == null) || isJmsConfigChanged()) { if (tryToEstablishJMSConnection()) loggingThroughJMS(oAuth2AuditLog); else loggingThroughFile(oAuth2AuditLog); } else { loggingThroughJMS(oAuth2AuditLog); } } @PreDestroy public void destroy() { if (this.pooledConnectionFactory == null) return; this.pooledConnectionFactory.clear(); this.pooledConnectionFactory = null; } private boolean tryToEstablishJMSConnection() { lock.lock(); try { // Check if another thread init JMS pool already if ((this.pooledConnectionFactory != null) && !isJmsConfigChanged()) { return tryToEstablishJMSConnectionImpl(); } return true; } finally { lock.unlock(); } } private boolean tryToEstablishJMSConnectionImpl() { destroy(); Set<String> jmsBrokerURISet = getJmsBrokerURISet(); if (BooleanUtils.isNotTrue(isEnabledOAuthAuditnLogging()) || CollectionUtils.isEmpty(jmsBrokerURISet)) return false; this.jmsBrokerURISet = new HashSet<String>(jmsBrokerURISet); this.jmsUserName = getJmsUserName(); this.jmsPassword = getJmsPassword(); Iterator<String> jmsBrokerURIIterator = jmsBrokerURISet.iterator(); StringBuilder uriBuilder = new StringBuilder(); while (jmsBrokerURIIterator.hasNext()) { String jmsBrokerURI = jmsBrokerURIIterator.next(); uriBuilder.append("tcp://"); uriBuilder.append(jmsBrokerURI); if (jmsBrokerURIIterator.hasNext()) uriBuilder.append(","); } String brokerUrl = BROKER_URL_PREFIX + uriBuilder + BROKER_URL_SUFFIX; ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(this.jmsUserName, this.jmsPassword, brokerUrl); this.pooledConnectionFactory = new PooledConnectionFactory(connectionFactory); pooledConnectionFactory.setIdleTimeout(5000); pooledConnectionFactory.setMaxConnections(10); pooledConnectionFactory.start(); return true; } private void loggingThroughJMS(OAuth2AuditLog oAuth2AuditLog) { QueueConnection connection = null; try { connection = pooledConnectionFactory.createQueueConnection(); connection.start(); QueueSession session = connection.createQueueSession(transacted, ACK_MODE); MessageProducer producer = session.createProducer(session.createQueue(CLIENT_QUEUE_NAME)); TextMessage txtMessage = session.createTextMessage(); txtMessage.setText(ServerUtil.asPrettyJson(oAuth2AuditLog)); producer.send(txtMessage); } catch (JMSException e) { log.error("Can't send message", e); } catch (IOException e) { log.error("Can't serialize the audit log", e); } catch (Exception e) { log.error("Can't send message, please check your activeMQ configuration.", e); } finally { if (connection == null) return; try { connection.close(); } catch (JMSException e) { log.error("Can't close connection."); } } } private void loggingThroughFile(OAuth2AuditLog oAuth2AuditLog) { try { log.info(ServerUtil.asPrettyJson(oAuth2AuditLog)); } catch (IOException e) { log.error("Can't serialize the audit log", e); } } private boolean isJmsConfigChanged() { return !Objects.equal(this.jmsUserName, getJmsUserName()) || !Objects.equal(this.jmsPassword, getJmsPassword()) || !Objects.equal(this.jmsBrokerURISet, getJmsBrokerURISet()); } private Boolean isEnabledOAuthAuditnLogging() { if (this.updateState) { this.enabledOAuthAuditnLogging = appConfiguration.getEnabledOAuthAuditLogging(); } return this.enabledOAuthAuditnLogging; } private Set<String> getJmsBrokerURISet() { return appConfiguration.getJmsBrokerURISet(); } private String getJmsUserName() { return appConfiguration.getJmsUserName(); } private String getJmsPassword() { return appConfiguration.getJmsPassword(); } }