/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat, Inc. and/or its affiliates, * and individual contributors as indicated by the @author tags. * See the copyright.txt in the distribution for a * full listing of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * 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, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * (C) 2010, * @author JBoss, by Red Hat. */ package org.jboss.jbossts.txbridge.tests.inbound.utility; import com.arjuna.ats.arjuna.common.arjPropertyManager; import com.arjuna.ats.arjuna.recovery.RecoveryManager; import com.arjuna.ats.arjuna.recovery.RecoveryModule; import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule; import com.arjuna.ats.jta.recovery.XAResourceRecoveryHelper; import org.jboss.logging.Logger; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ejb.Singleton; import javax.ejb.Startup; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import java.io.*; import java.util.HashSet; import java.util.Set; import java.util.Vector; /** * Implementation of XAResourceRecoveryHelper for use in txbridge recovery tests. * Provides persistence for TestXAResource via a file in the ObjectStore. * * @author Jonathan Halliday (jonathan.halliday@redhat.com) 2010-01 */ @Singleton @Startup public class TestXAResourceRecoveryHelper implements XAResourceRecoveryHelper { private static Logger log = Logger.getLogger(TestXAResourceRecoveryHelper.class); private static final TestXAResourceRecoveryHelper instance = new TestXAResourceRecoveryHelper(); private static final TestXAResourceRecovered xaResourceInstance = new TestXAResourceRecovered(); private final Set<Xid> preparedXids = new HashSet<Xid>(); public static TestXAResourceRecoveryHelper getInstance() { return instance; } /** * This is required to be public (not protected) because of * Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException: JBAS014227: EJB TestXAResourceRecoveryHelper of type org.jboss.jbossts.txbridge.tests.inbound.utility.TestXAResourceRecoveryHelper must have public default constructor * Clearly two instances will be created, one by the container for postConstruct/preDestroy and one by ourselves but as they both use getInstance() the code should work fine. * This is not a new way of working it would have been like that before. */ public TestXAResourceRecoveryHelper() { } /** * MC lifecycle callback, used to register the recovery module with the transaction manager. */ @PostConstruct public void postConstruct() { log.info("TestXAResourceRecoveryHelper starting"); getRecoveryModule().addXAResourceRecoveryHelper(getInstance()); getInstance().recoverFromDisk(); } /** * MC lifecycle callback, used to unregister the recovery module from the transaction manager. */ @PreDestroy public void preDestroy() { log.info("TestXAResourceRecoveryHelper stopping"); getRecoveryModule().removeXAResourceRecoveryHelper(getInstance()); } private XARecoveryModule getRecoveryModule() { for (RecoveryModule recoveryModule : ((Vector<RecoveryModule>) RecoveryManager.manager().getModules())) { if (recoveryModule instanceof XARecoveryModule) { return (XARecoveryModule) recoveryModule; } } return null; } @Override public boolean initialise(String param) throws Exception { log.trace("initialise(param=" + param + ")"); return true; } @Override public XAResource[] getXAResources() throws Exception { log.trace("getXAResources()"); XAResource values[] = new XAResource[1]; values[0] = xaResourceInstance; return values; } /////////////////////////// public void logPrepared(Xid xid) throws XAException { log.trace("logPrepared(xid=" + xid + ")"); synchronized (preparedXids) { if (preparedXids.add(xid)) { writeToDisk(); } else { throw new XAException(XAException.XAER_PROTO); } } } public void removeLog(Xid xid) throws XAException { log.trace("removeLog(xid=" + xid); synchronized (preparedXids) { if (preparedXids.remove(xid)) { writeToDisk(); } else { log.trace("no log present for " + xid); } } } public Xid[] recover() { log.trace("recover()"); return preparedXids.toArray(new Xid[preparedXids.size()]); } private void writeToDisk() { File logFile = getLogFile(); log.trace("logging " + preparedXids.size() + " records to " + logFile.getAbsolutePath()); try { FileOutputStream fos = new FileOutputStream(logFile); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(preparedXids); oos.close(); fos.close(); } catch (IOException e) { log.error(e); } } private void recoverFromDisk() { File logFile = getLogFile(); log.trace("recovering from " + logFile.getAbsolutePath()); if (!logFile.exists()) { return; } try { FileInputStream fis = new FileInputStream(logFile); ObjectInputStream ois = new ObjectInputStream(fis); Set<Xid> xids = (Set<Xid>) ois.readObject(); preparedXids.addAll(xids); log.trace("Recovered " + xids + " Xids"); ois.close(); fis.close(); } catch (Exception e) { log.error(e); } } private File getLogFile() { String parentDir = arjPropertyManager.getObjectStoreEnvironmentBean().getObjectStoreDir(); String childDir = arjPropertyManager.getObjectStoreEnvironmentBean().getLocalOSRoot(); File logDir = new File(parentDir, childDir); logDir.mkdirs(); File logFile = new File(logDir, "TestXAResource.ser"); return logFile; } }