/**
* Copyright 2014-2016 yangming.liu<bytefox@126.com>.
*
* 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, as published by the Free Software Foundation.
*
* This program 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 distribution; if not, see <http://www.gnu.org/licenses/>.
*/
package org.bytesoft.bytetcc.logging;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.transaction.xa.Xid;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.bytejta.logging.store.VirtualLoggingSystemImpl;
import org.bytesoft.compensable.CompensableBeanFactory;
import org.bytesoft.compensable.archive.CompensableArchive;
import org.bytesoft.compensable.archive.TransactionArchive;
import org.bytesoft.compensable.aware.CompensableBeanFactoryAware;
import org.bytesoft.compensable.aware.CompensableEndpointAware;
import org.bytesoft.compensable.logging.CompensableLogger;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.logging.ArchiveDeserializer;
import org.bytesoft.transaction.logging.LoggingFlushable;
import org.bytesoft.transaction.logging.store.VirtualLoggingListener;
import org.bytesoft.transaction.logging.store.VirtualLoggingRecord;
import org.bytesoft.transaction.logging.store.VirtualLoggingSystem;
import org.bytesoft.transaction.recovery.TransactionRecoveryCallback;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SampleCompensableLogger extends VirtualLoggingSystemImpl
implements CompensableLogger, LoggingFlushable, CompensableBeanFactoryAware, CompensableEndpointAware {
static final Logger logger = LoggerFactory.getLogger(SampleCompensableLogger.class);
private CompensableBeanFactory beanFactory;
private String endpoint;
public void createTransaction(TransactionArchive archive) {
ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
try {
byte[] byteArray = deserializer.serialize((TransactionXid) archive.getXid(), archive);
this.create(archive.getXid(), byteArray);
} catch (RuntimeException rex) {
logger.error("Error occurred while creating transaction-archive.", rex);
}
}
public void updateTransaction(TransactionArchive archive) {
ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
try {
byte[] byteArray = deserializer.serialize((TransactionXid) archive.getXid(), archive);
this.modify(archive.getXid(), byteArray);
} catch (RuntimeException rex) {
logger.error("Error occurred while modifying transaction-archive.", rex);
}
}
public void deleteTransaction(TransactionArchive archive) {
try {
this.delete(archive.getXid());
} catch (RuntimeException rex) {
logger.error("Error occurred while deleting transaction-archive.", rex);
}
}
public void createCoordinator(XAResourceArchive archive) {
ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
try {
byte[] byteArray = deserializer.serialize((TransactionXid) archive.getXid(), archive);
this.create(archive.getXid(), byteArray);
} catch (RuntimeException rex) {
logger.error("Error occurred while modifying resource-archive.", rex);
}
}
public void updateCoordinator(XAResourceArchive archive) {
ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
try {
byte[] byteArray = deserializer.serialize((TransactionXid) archive.getXid(), archive);
this.modify(archive.getXid(), byteArray);
} catch (RuntimeException rex) {
logger.error("Error occurred while modifying resource-archive.", rex);
}
}
public void createCompensable(CompensableArchive archive) {
ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
try {
TransactionXid xid = (TransactionXid) archive.getIdentifier();
byte[] byteArray = deserializer.serialize(xid, archive);
this.create(xid, byteArray);
} catch (RuntimeException rex) {
logger.error("Error occurred while creating compensable-archive.", rex);
}
}
public void updateCompensable(CompensableArchive archive) {
ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
try {
TransactionXid xid = (TransactionXid) archive.getIdentifier();
byte[] byteArray = deserializer.serialize(xid, archive);
this.modify(xid, byteArray);
} catch (RuntimeException rex) {
logger.error("Error occurred while modifying compensable-archive.", rex);
}
}
public void recover(TransactionRecoveryCallback callback) {
final Map<Xid, TransactionArchive> xidMap = new HashMap<Xid, TransactionArchive>();
final ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
final XidFactory xidFactory = this.beanFactory.getCompensableXidFactory();
this.traversal(new VirtualLoggingListener() {
public void recvOperation(VirtualLoggingRecord action) {
Xid xid = action.getIdentifier();
int operator = action.getOperator();
if (VirtualLoggingSystem.OPERATOR_DELETE == operator) {
xidMap.remove(xid);
} else if (xidMap.containsKey(xid) == false) {
xidMap.put(xid, null);
}
}
});
this.traversal(new VirtualLoggingListener() {
public void recvOperation(VirtualLoggingRecord action) {
Xid xid = action.getIdentifier();
if (xidMap.containsKey(xid)) {
this.execOperation(action);
}
}
public void execOperation(VirtualLoggingRecord action) {
Xid identifier = action.getIdentifier();
TransactionXid xid = xidFactory.createGlobalXid(identifier.getGlobalTransactionId());
Object obj = deserializer.deserialize(xid, action.getValue());
if (TransactionArchive.class.isInstance(obj)) {
TransactionArchive archive = (TransactionArchive) obj;
xidMap.put(identifier, archive);
} else if (XAResourceArchive.class.isInstance(obj)) {
TransactionArchive archive = xidMap.get(identifier);
if (archive == null) {
logger.error("Error occurred while recovering resource archive: {}", obj);
return;
}
XAResourceArchive resourceArchive = (XAResourceArchive) obj;
boolean matched = false;
List<XAResourceArchive> remoteResources = archive.getRemoteResources();
for (int i = 0; matched == false && remoteResources != null && i < remoteResources.size(); i++) {
XAResourceArchive element = remoteResources.get(i);
if (resourceArchive.getXid().equals(element.getXid())) {
matched = true;
remoteResources.set(i, resourceArchive);
}
}
if (matched == false) {
remoteResources.add(resourceArchive);
}
} else if (CompensableArchive.class.isInstance(obj)) {
TransactionArchive archive = xidMap.get(identifier);
if (archive == null) {
logger.error("Error occurred while recovering compensable archive: {}", obj);
return;
}
List<CompensableArchive> compensables = archive.getCompensableResourceList();
CompensableArchive resourceArchive = (CompensableArchive) obj;
// if (VirtualLoggingSystem.OPERATOR_CREATE == action.getOperator()) {
// compensables.add(resourceArchive);
// } else {
boolean matched = false;
for (int i = 0; matched == false && compensables != null && i < compensables.size(); i++) {
CompensableArchive element = compensables.get(i);
if (resourceArchive.getIdentifier().equals(element.getIdentifier())) {
matched = true;
compensables.set(i, resourceArchive);
}
}
if (matched == false) {
compensables.add(resourceArchive);
}
// }
}
}
});
for (Iterator<Map.Entry<Xid, TransactionArchive>> itr = xidMap.entrySet().iterator(); itr.hasNext();) {
Map.Entry<Xid, TransactionArchive> entry = itr.next();
TransactionArchive archive = entry.getValue();
if (archive == null) {
continue;
} else {
try {
callback.recover(archive);
} catch (RuntimeException rex) {
logger.error("Error occurred while recovering transaction(xid= {}).", archive.getXid(), rex);
}
}
}
}
public File getDefaultDirectory() {
String address = StringUtils.trimToEmpty(this.endpoint);
File directory = new File(String.format("bytetcc/%s", address.replaceAll("\\:|\\.", "_")));
if (directory.exists() == false) {
try {
directory.mkdirs();
} catch (SecurityException ex) {
logger.error("Error occurred while creating directory {}!", directory.getAbsolutePath(), ex);
}
}
return directory;
}
public String getLoggingFilePrefix() {
return "bytetcc-";
}
public String getLoggingIdentifier() {
return "org.bytesoft.bytetcc.logging.sample";
}
public void setBeanFactory(CompensableBeanFactory tbf) {
this.beanFactory = tbf;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
}