package org.infinispan.functional.impl; import java.util.concurrent.CompletableFuture; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import org.infinispan.batch.BatchContainer; import org.infinispan.commands.VisitableCommand; import org.infinispan.commons.CacheException; import org.infinispan.commons.api.functional.FunctionalMap; import org.infinispan.commons.api.functional.Status; import org.infinispan.util.concurrent.CompletableFutures; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import org.infinispan.configuration.cache.Configuration; import org.infinispan.context.InvocationContext; import org.infinispan.context.impl.TxInvocationContext; /** * Abstract functional map, providing implementations for some of the shared methods. * * @since 8.0 */ abstract class AbstractFunctionalMap<K, V> implements FunctionalMap<K, V> { private static final Log log = LogFactory.getLog(FunctionalMap.class); protected final FunctionalMapImpl<K, V> fmap; protected final Params params; private final boolean transactional; private final boolean autoCommit; private final BatchContainer batchContainer; private final TransactionManager transactionManager; protected AbstractFunctionalMap(Params params, FunctionalMapImpl<K, V> fmap) { this.fmap = fmap; Configuration config = fmap.cache.getCacheConfiguration(); transactional = config.transaction().transactionMode().isTransactional(); autoCommit = config.transaction().autoCommit(); transactionManager = transactional ? fmap.cache.getTransactionManager() : null; batchContainer = transactional && config.invocationBatching().enabled() ? fmap.cache.getBatchContainer() : null; this.params = params; } @Override public String getName() { return ""; } @Override public Status getStatus() { return fmap.getStatus(); } @Override public void close() throws Exception { fmap.close(); } protected InvocationContext getInvocationContext(boolean isWrite, int keyCount) { InvocationContext invocationContext; boolean txInjected = false; if (transactional) { Transaction transaction = getOngoingTransaction(); if (transaction == null && autoCommit && transactionManager != null) { try { transactionManager.begin(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new CacheException("Unable to begin implicit transaction.", e); } transaction = getOngoingTransaction(); txInjected = true; } invocationContext = fmap.invCtxFactory.createInvocationContext(transaction, txInjected); } else { invocationContext = fmap.invCtxFactory.createInvocationContext(isWrite, keyCount); } return invocationContext; } private Transaction getOngoingTransaction() { try { Transaction transaction = null; if (transactionManager != null) { transaction = transactionManager.getTransaction(); if (transaction == null && batchContainer != null) { transaction = batchContainer.getBatchTransaction(); } } return transaction; } catch (SystemException e) { throw new CacheException("Unable to get transaction", e); } } protected <T> CompletableFuture<T> invokeAsync(InvocationContext ctx, VisitableCommand cmd) { CompletableFuture<T> cf; boolean isImplicitTx = ctx.isInTxScope() && ((TxInvocationContext) ctx).isImplicitTransaction(); final Transaction implicitTransaction; try { // interceptors must not access thread-local transaction anyway if (isImplicitTx) { implicitTransaction = transactionManager.suspend(); assert implicitTransaction != null; } else { implicitTransaction = null; } cf = (CompletableFuture<T>) fmap.chain.invokeAsync(ctx, cmd); } catch (SystemException e) { throw new CacheException("Cannot suspend implicit transaction", e); } catch (Throwable t) { if (isImplicitTx) { try { if (transactionManager != null) transactionManager.rollback(); } catch (Throwable t2) { log.trace("Could not rollback", t2);//best effort t.addSuppressed(t2); } } throw t; } if (isImplicitTx) { return cf.handle((result, throwable) -> { if (throwable != null) { try { implicitTransaction.rollback(); } catch (SystemException e) { log.trace("Could not rollback", e); throwable.addSuppressed(e); } throw CompletableFutures.asCompletionException(throwable); } try { implicitTransaction.commit(); } catch (Exception e) { log.couldNotCompleteInjectedTransaction(e); throw CompletableFutures.asCompletionException(e); } return result; }); } else { return cf; } } }