/* * JBoss, Home of Professional Open Source. * Copyright 2013, 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 io.narayana.spi.util; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import javax.transaction.xa.XAException; import javax.transaction.Synchronization; import java.io.Serializable; import java.util.Set; import java.util.HashSet; import java.util.Map; import java.util.HashMap; /** * Simulate a variety of faults during the various phases of the XA protocol */ public class DummyXAResource implements Synchronization, XAResource, Serializable { private static final Map<String, XAException> xaCodeMap = new HashMap<String, XAException>(); private ASFailureType _xaFailureType = ASFailureType.NONE; private ASFailureMode _xaFailureMode = ASFailureMode.NONE; private int _suspend; private int _recoveryAttempts = 1; private XAException _xaException; private int txTimeout = 10; private Set<Xid> _xids = new HashSet<Xid>(); private transient boolean _isPrepared = false; // transient so it doesn't get persisted in the tx store static { init(); } public DummyXAResource() { } public DummyXAResource(ASFailureSpec spec) { this(); if (spec == null) throw new IllegalArgumentException("Invalid XA resource failure injection specification"); setFailureMode(spec.getMode(), spec.getModeArg()); setFailureType(spec.getType()); } public void applySpec(String message) throws XAException { applySpec(message, _isPrepared); } public void applySpec(String message, boolean prepared) throws XAException { if (_xaFailureType.equals(ASFailureType.NONE) || _xaFailureMode.equals(ASFailureMode.NONE) || !prepared) { System.out.println(message + (_isPrepared ? " ... " : " recovery")); return; // NB if !_isPrepared then we must have been called from the recovery subsystem } System.out.println("Applying fault injection with " + _xids.size() + " active branches"); if (_xaException != null) { System.out.println(message + " ... xa error: " + _xaException.getMessage()); throw _xaException; } else if (_xaFailureMode.equals(ASFailureMode.HALT)) { System.out.println(message + " ... halting"); Runtime.getRuntime().halt(1); } else if (_xaFailureMode.equals(ASFailureMode.EXIT)) { System.out.println(message + " ... exiting"); System.exit(1); } else if (_xaFailureMode.equals(ASFailureMode.SUSPEND)) { System.out.println(message + " ... suspending for " + _suspend); suspend(_suspend); System.out.println(message + " ... resuming"); } } public String toString() { // return _xaFailureType + ", " + _xaFailureMode + ", " + (_args != null && _args.length != 0 ? _args[0] : ""); StringBuilder sb = new StringBuilder(); sb.append("XAResourceWrapperImpl@").append(Integer.toHexString(System.identityHashCode(this))); sb.append(" pad=").append("false"); sb.append(" overrideRmValue=").append("false"); sb.append(" productName=").append("Dummy Product"); sb.append(" productVersion=").append("0.0.0"); sb.append(" jndiName=").append("java:/dummyProd"); sb.append("]"); return sb.toString(); } private void suspend(int msecs) { try { Thread.sleep(msecs); } catch (InterruptedException e) { e.printStackTrace(); } } public void setFailureMode(ASFailureMode mode, String ... args) throws IllegalArgumentException { _xaFailureMode = mode; if (args != null && args.length != 0) { if (_xaFailureMode.equals(ASFailureMode.SUSPEND)) { _suspend = Integer.parseInt(args[0]); } else if (_xaFailureMode.equals(ASFailureMode.XAEXCEPTION)) { _xaException = xaCodeMap.get(args[0]); if (_xaException == null) _xaException = new XAException(XAException.XAER_RMFAIL); } } } public void setFailureType(ASFailureType type) { _xaFailureType = type; } public ASFailureType getFailureType() { return _xaFailureType; } public void setRecoveryAttempts(int _recoveryAttempts) { this._recoveryAttempts = _recoveryAttempts; } // Synchronizatons public void beforeCompletion() { if (_xaFailureType.equals(ASFailureType.SYNCH_BEFORE)) try { applySpec("Before completion"); } catch (XAException e) { throw new RuntimeException(e); } } public void afterCompletion(int i) { if (_xaFailureType.equals(ASFailureType.SYNCH_AFTER)) try { applySpec("After completion"); } catch (XAException e) { throw new RuntimeException(e); } } // XA Interface implementation public void commit(Xid xid, boolean b) throws XAException { if (_xaFailureType.equals(ASFailureType.XARES_COMMIT)) applySpec("xa commit"); _isPrepared = false; _xids.remove(xid); } public void rollback(Xid xid) throws XAException { if (_xaFailureType.equals(ASFailureType.XARES_ROLLBACK)) applySpec("xa rollback"); _isPrepared = false; _xids.remove(xid); } public void end(Xid xid, int i) throws XAException { if (_xaFailureType.equals(ASFailureType.XARES_END)) applySpec("xa end"); } public void forget(Xid xid) throws XAException { if (_xaFailureType.equals(ASFailureType.XARES_FORGET)) applySpec("xa forget"); _isPrepared = false; _xids.remove(xid); } public int getTransactionTimeout() throws XAException { return txTimeout; } public boolean isSameRM(XAResource xaResource) throws XAException { return false; } public int prepare(Xid xid) throws XAException { _isPrepared = true; if (_xaFailureType.equals(ASFailureType.XARES_PREPARE)) applySpec("xa prepare"); _xids.add(xid); return XA_OK; } public Xid[] recover(int i) throws XAException { if (_recoveryAttempts <= 0) return _xids.toArray(new Xid[_xids.size()]); _recoveryAttempts -= 1; if (_xaFailureType.equals(ASFailureType.XARES_RECOVER)) applySpec("xa recover"); return new Xid[0]; } public boolean setTransactionTimeout(int txTimeout) throws XAException { this.txTimeout = txTimeout; return true; // set was successfull } public void start(Xid xid, int i) throws XAException { _xids.add(xid); if (_xaFailureType.equals(ASFailureType.XARES_START)) applySpec("xa start"); } public String getEISProductName() { return "Test XAResouce";} public String getEISProductVersion() { return "v666.0";} @SuppressWarnings({"ThrowableInstanceNeverThrown"}) private static void init() { xaCodeMap.put("XA_HEURCOM", new XAException(XAException.XA_HEURCOM)); xaCodeMap.put("XA_HEURHAZ", new XAException(XAException.XA_HEURHAZ)); xaCodeMap.put("XA_HEURMIX", new XAException(XAException.XA_HEURMIX)); xaCodeMap.put("XA_HEURRB", new XAException(XAException.XA_HEURRB)); xaCodeMap.put("XA_NOMIGRATE", new XAException(XAException.XA_NOMIGRATE)); xaCodeMap.put("XA_RBBASE", new XAException(XAException.XA_RBBASE)); xaCodeMap.put("XA_RBCOMMFAIL", new XAException(XAException.XA_RBCOMMFAIL)); xaCodeMap.put("XA_RBDEADLOCK", new XAException(XAException.XA_RBDEADLOCK)); xaCodeMap.put("XA_RBEND", new XAException(XAException.XA_RBEND)); xaCodeMap.put("XA_RBINTEGRITY", new XAException(XAException.XA_RBINTEGRITY)); xaCodeMap.put("XA_RBOTHER", new XAException(XAException.XA_RBOTHER)); xaCodeMap.put("XA_RBPROTO", new XAException(XAException.XA_RBPROTO)); xaCodeMap.put("XA_RBROLLBACK", new XAException(XAException.XA_RBROLLBACK)); xaCodeMap.put("XA_RBTIMEOUT", new XAException(XAException.XA_RBTIMEOUT)); xaCodeMap.put("XA_RBTRANSIENT", new XAException(XAException.XA_RBTRANSIENT)); xaCodeMap.put("XA_RDONLY", new XAException(XAException.XA_RDONLY)); xaCodeMap.put("XA_RETRY", new XAException(XAException.XA_RETRY)); xaCodeMap.put("XAER_ASYNC", new XAException(XAException.XAER_ASYNC)); xaCodeMap.put("XAER_DUPID", new XAException(XAException.XAER_DUPID)); xaCodeMap.put("XAER_INVAL", new XAException(XAException.XAER_INVAL)); xaCodeMap.put("XAER_NOTA", new XAException(XAException.XAER_NOTA)); xaCodeMap.put("XAER_OUTSIDE", new XAException(XAException.XAER_OUTSIDE)); xaCodeMap.put("XAER_PROTO", new XAException(XAException.XAER_PROTO)); xaCodeMap.put("XAER_RMERR", new XAException(XAException.XAER_RMERR)); xaCodeMap.put("XAER_RMFAIL ", new XAException(XAException.XAER_RMFAIL)); } public boolean isXAResource() { return _xaFailureType.isXA() || _xaFailureType.equals(ASFailureType.NONE); } public boolean isSynchronization() { return _xaFailureType.isSynchronization(); } public boolean isPreCommit() { return _xaFailureType.isPreCommit(); } public boolean expectException() { return _xaFailureMode.equals(ASFailureMode.XAEXCEPTION); } }