/*
* JBoss, Home of Professional Open Source.
* Copyright 2016, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.narayana.tomcat.jta;
import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException;
import com.arjuna.ats.arjuna.common.arjPropertyManager;
import com.arjuna.ats.arjuna.common.recoveryPropertyManager;
import com.arjuna.ats.arjuna.coordinator.TransactionReaper;
import com.arjuna.ats.arjuna.coordinator.TxControl;
import com.arjuna.ats.arjuna.recovery.RecoveryManager;
import com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule;
import com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner;
import com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter;
import com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter;
import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule;
import com.arjuna.ats.jdbc.TransactionalDriver;
import com.arjuna.ats.jta.common.jtaPropertyManager;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Class responsible for configuring and initializing Narayana JTA services for the servlet container.
*
* @author <a href="mailto:gytis@redhat.com">Gytis Trikleris</a>
*/
public class NarayanaJtaServletContextListener implements ServletContextListener {
private static final Logger LOGGER = Logger.getLogger(NarayanaJtaServletContextListener.class.getSimpleName());
private static final String DEFAULT_NODE_IDENTIFIER = "1";
private static final List<String> DEFAULT_RECOVERY_MODULES = Arrays.asList(AtomicActionRecoveryModule.class.getName(),
XARecoveryModule.class.getName());
private static final List<String> DEFAULT_ORPHAN_FILTERS = Arrays
.asList(JTATransactionLogXAResourceOrphanFilter.class.getName(), JTANodeNameXAResourceOrphanFilter.class.getName());
private static final List<String> DEFAULT_EXPIRY_SCANNERS = Arrays
.asList(ExpiredTransactionStatusManagerScanner.class.getName());
/**
* Initialize and start Narayana JTA services.
* <p>
* During the setup node identifier, recovery modules, orphan filters, and expiry scanners are setup. Configuration file
* will be used to get initial values. If one doesn't exist, following defaults will be used.
* The settings from configuration file is transfered to {@link com.arjuna.ats.jta.common.JTAEnvironmentBean} where runtime
* configuration resides.
* <p>
* <ul>
* <li>Node identifier: "1"
* <li>Recovery modules: {@link AtomicActionRecoveryModule}, {@link XARecoveryModule}
* <li>Orphan filters: {@link JTATransactionLogXAResourceOrphanFilter}, {@link JTANodeNameXAResourceOrphanFilter}
* <li>Expiry scanners: {@link ExpiredTransactionStatusManagerScanner}
* </ul>
* <p>
* After setup recovery manager, transaction status manager, and transaction reaper are started.
*
* @param servletContextEvent containing the ServletContext that is being initialized
*/
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
LOGGER.fine("Initializing Narayana");
initNodeIdentifier();
initRecoveryModules();
initOrphanFilters();
initExpiryScanners();
RecoveryManager.manager();
TxControl.enable();
TransactionReaper.instantiate();
}
/**
* First, stop recovery manager, transaction status manager, and transaction reaper. Then, remove transactional driver from
* jdbc driver manager's list.
*
* @param servletContextEvent containing the ServletContext that is being destroyed
*/
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
LOGGER.fine("Disabling Narayana");
TransactionReaper.terminate(false);
TxControl.disable(true);
RecoveryManager.manager().terminate();
Collections.list(DriverManager.getDrivers()).stream().filter(d -> d instanceof TransactionalDriver).forEach(d -> {
try {
DriverManager.deregisterDriver(d);
} catch (SQLException e) {
LOGGER.log(Level.WARNING, e.getMessage(), e);
}
});
}
/**
* If node identifier wasn't set by property manager, then set default {@link #DEFAULT_NODE_IDENTIFIER}.
*/
private void initNodeIdentifier() {
if (arjPropertyManager.getCoreEnvironmentBean().getNodeIdentifier() == null) {
LOGGER.warning("Node identifier was not set. Setting it to the default value: " + DEFAULT_NODE_IDENTIFIER);
try {
arjPropertyManager.getCoreEnvironmentBean().setNodeIdentifier(DEFAULT_NODE_IDENTIFIER);
} catch (CoreEnvironmentBeanException e) {
LOGGER.log(Level.WARNING, e.getMessage(), e);
}
}
jtaPropertyManager.getJTAEnvironmentBean()
.setXaRecoveryNodes(Collections.singletonList(arjPropertyManager.getCoreEnvironmentBean().getNodeIdentifier()));
}
/**
* If recovery modules were not set by property manager, then set defaults {@link #DEFAULT_RECOVERY_MODULES}.
*/
private void initRecoveryModules() {
if (!recoveryPropertyManager.getRecoveryEnvironmentBean().getRecoveryModuleClassNames().isEmpty()) {
return;
}
LOGGER.fine("Recovery modules were not enabled. Enabling default modules: " + DEFAULT_RECOVERY_MODULES);
recoveryPropertyManager.getRecoveryEnvironmentBean().setRecoveryModuleClassNames(DEFAULT_RECOVERY_MODULES);
}
/**
* If orphan filters were not set by property manager, then set defaults {@link #DEFAULT_ORPHAN_FILTERS}.
*/
private void initOrphanFilters() {
if (!jtaPropertyManager.getJTAEnvironmentBean().getXaResourceOrphanFilterClassNames().isEmpty()) {
return;
}
LOGGER.fine("Orphan filters were not enabled. Enabling default filters: " + DEFAULT_ORPHAN_FILTERS);
jtaPropertyManager.getJTAEnvironmentBean().setXaResourceOrphanFilterClassNames(DEFAULT_ORPHAN_FILTERS);
}
/**
* If expiry scanners were not set by property manager, then set defaults {@link #DEFAULT_EXPIRY_SCANNERS}.
*/
private void initExpiryScanners() {
if (!recoveryPropertyManager.getRecoveryEnvironmentBean().getExpiryScannerClassNames().isEmpty()) {
return;
}
LOGGER.fine("Expiry scanners were not enabled. Enabling default scanners: " + DEFAULT_EXPIRY_SCANNERS);
recoveryPropertyManager.getRecoveryEnvironmentBean().setExpiryScannerClassNames(DEFAULT_EXPIRY_SCANNERS);
}
}