/* * JBoss, Home of Professional Open Source * Copyright 2006, Red Hat Middleware LLC, 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) 2005-2006, * @author JBoss Inc. */ package com.arjuna.ats.jta.distributed; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import com.arjuna.ats.arjuna.common.Uid; import com.arjuna.ats.jta.distributed.server.CompletionCounter; public class TestResource implements XAResource { private Xid xid; protected int timeout = 0; private boolean readonly = false; private File file; private String serverId; private CompletionCounter completionCounter; private boolean scanning; private transient boolean fatalCommit; public TestResource(String serverId, boolean readonly) { this.completionCounter = CompletionCounter.getInstance(); this.serverId = serverId; this.readonly = readonly; } public TestResource(String serverId, File file) throws IOException { this.completionCounter = CompletionCounter.getInstance(); this.serverId = serverId; this.file = file; DataInputStream fis = new DataInputStream(new FileInputStream(file)); final int formatId = fis.readInt(); final int gtrid_length = fis.readInt(); final byte[] gtrid = new byte[gtrid_length]; fis.read(gtrid, 0, gtrid_length); final int bqual_length = fis.readInt(); final byte[] bqual = new byte[bqual_length]; fis.read(bqual, 0, bqual_length); this.xid = new Xid() { @Override public byte[] getGlobalTransactionId() { return gtrid; } @Override public int getFormatId() { return formatId; } @Override public byte[] getBranchQualifier() { return bqual; } public String toString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("< formatId="); stringBuilder.append(formatId); stringBuilder.append(", gtrid_length="); stringBuilder.append(gtrid_length); stringBuilder.append(", bqual_length="); stringBuilder.append(bqual_length); stringBuilder.append(", tx_uid="); stringBuilder.append(new Uid(gtrid).stringForm()); stringBuilder.append(", node_name="); stringBuilder.append(new String(Arrays.copyOfRange(gtrid, Uid.UID_SIZE, gtrid_length))); stringBuilder.append(", branch_uid="); stringBuilder.append(new Uid(bqual));; stringBuilder.append(", subordinatenodename="); int offset = Uid.UID_SIZE + 4; int length = (bqual[offset++] << 24) + ((bqual[offset++] & 0xFF) << 16) + ((bqual[offset++] & 0xFF) << 8) + (bqual[offset++] & 0xFF); if (length > 0) stringBuilder.append(new String(Arrays.copyOfRange(bqual, offset, offset+length))); stringBuilder.append(", eis_name=unknown"); stringBuilder.append(" >"); return stringBuilder.toString(); } }; fis.close(); } public TestResource(String nodeName, boolean b, boolean fatalCommit) { this(nodeName, b); this.fatalCommit = fatalCommit; } /** * This class declares that it throws an Error *purely for byteman* so that * we can crash the resource during this method: * https://issues.jboss.org/browse/BYTEMAN-156 * https://issues.jboss.org/browse/BYTEMAN-175 */ public synchronized int prepare(Xid xid) throws XAException, Error { System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ") XA_PREPARE [" + xid + "]"); if (readonly) return XA_RDONLY; else { File dir = new File(System.getProperty("user.dir") + "/distributedjta-tests/TestResource/" + serverId + "/"); dir.mkdirs(); file = new File(dir, new Uid().fileStringForm() + "_"); try { file.createNewFile(); final int formatId = xid.getFormatId(); final byte[] gtrid = xid.getGlobalTransactionId(); final int gtrid_length = gtrid.length; final byte[] bqual = xid.getBranchQualifier(); final int bqual_length = bqual.length; DataOutputStream fos = new DataOutputStream(new FileOutputStream(file)); fos.writeInt(formatId); fos.writeInt(gtrid_length); fos.write(gtrid, 0, gtrid_length); fos.writeInt(bqual_length); fos.write(bqual, 0, bqual_length); fos.flush(); fos.close(); } catch (IOException e) { throw new XAException(XAException.XAER_RMERR); } return XA_OK; } } public synchronized void commit(Xid id, boolean onePhase) throws XAException { System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ") XA_COMMIT [" + id + "]"); completionCounter.incrementCommit(serverId); if (file != null) { if (!file.delete()) { throw new XAException(XAException.XA_RETRY); } } this.xid = null; if (fatalCommit) { throw new Error(); } } public synchronized void rollback(Xid xid) throws XAException { System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ") XA_ROLLBACK[" + xid + "]"); completionCounter.incrementRollback(serverId); if (file != null) { if (!file.delete()) { throw new XAException(XAException.XA_RETRY); } } this.xid = null; } public void start(Xid xid, int flags) throws XAException { System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ") XA_START [" + xid + "] Flags=" + flags); } public void end(Xid xid, int flags) throws XAException { System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ") XA_END [" + xid + "] Flags=" + flags); } public void forget(Xid xid) throws XAException { System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ") XA_FORGET[" + xid + "]"); } public int getTransactionTimeout() throws XAException { return (timeout); } public boolean isSameRM(XAResource xares) throws XAException { if (xares instanceof TestResource) { TestResource other = (TestResource) xares; if ((this.xid != null && other.xid != null)) { if (this.xid.getFormatId() == other.xid.getFormatId()) { if (Arrays.equals(this.xid.getGlobalTransactionId(), other.xid.getGlobalTransactionId())) { if (Arrays.equals(this.xid.getBranchQualifier(), other.xid.getBranchQualifier())) { return true; } } } } } return false; } public Xid[] recover(int flag) throws XAException { Xid toReturn = null; if ((flag & XAResource.TMSTARTRSCAN) == XAResource.TMSTARTRSCAN) { synchronized(this) { if (scanning) { try { this.wait(); } catch (InterruptedException e) { throw new XAException("Could not wait for in progress scanner"); } } else { scanning = true; } } System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ") RECOVER[XAResource.TMSTARTRSCAN]: " + serverId); if (xid != null) { toReturn = xid; System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ") RECOVERED: " + toReturn); } } if ((flag & XAResource.TMENDRSCAN) == XAResource.TMENDRSCAN) { System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ") RECOVER[XAResource.TMENDRSCAN]: " + serverId); synchronized(this) { if (scanning) { scanning = false; this.notify(); } } } if (flag == XAResource.TMNOFLAGS) { System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ") RECOVER[XAResource.TMENDRSCAN]: " + serverId); } return new Xid[] { toReturn }; } public boolean setTransactionTimeout(int seconds) throws XAException { timeout = seconds; return (true); } }