/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ignite.internal.processors.cache.transactions; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.UUID; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteClientDisconnectedException; import org.apache.ignite.IgniteException; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; import org.apache.ignite.internal.util.future.IgniteFinishedFutureImpl; import org.apache.ignite.internal.util.future.IgniteFutureImpl; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.CX1; import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteAsyncSupport; import org.apache.ignite.lang.IgniteFuture; import org.apache.ignite.lang.IgniteUuid; import org.apache.ignite.transactions.Transaction; import org.apache.ignite.transactions.TransactionConcurrency; import org.apache.ignite.transactions.TransactionIsolation; import org.apache.ignite.transactions.TransactionState; /** * Cache transaction proxy. */ @SuppressWarnings("unchecked") public class TransactionProxyImpl<K, V> implements TransactionProxy, Externalizable { /** */ private static final long serialVersionUID = 0L; /** Wrapped transaction. */ @GridToStringInclude private GridNearTxLocal tx; /** Gateway. */ @GridToStringExclude private GridCacheSharedContext<K, V> cctx; /** Async flag. */ private boolean async; /** Async call result. */ private IgniteFuture asyncRes; /** * Empty constructor required for {@link Externalizable}. */ public TransactionProxyImpl() { // No-op. } /** * @param tx Transaction. * @param cctx Shared context. * @param async Async flag. */ public TransactionProxyImpl(GridNearTxLocal tx, GridCacheSharedContext<K, V> cctx, boolean async) { assert tx != null; assert cctx != null; this.tx = tx; this.cctx = cctx; this.async = async; } /** * @return Transaction. */ public GridNearTxLocal tx() { return tx; } /** * Enters a call. */ private void enter() { if (cctx.deploymentEnabled()) cctx.deploy().onEnter(); try { cctx.kernalContext().gateway().readLock(); } catch (IllegalStateException | IgniteClientDisconnectedException e) { throw e; } catch (RuntimeException | Error e) { cctx.kernalContext().gateway().readUnlock(); throw e; } } /** * Leaves a call. */ private void leave() { try { CU.unwindEvicts(cctx); } finally { cctx.kernalContext().gateway().readUnlock(); } } /** {@inheritDoc} */ @Override public IgniteUuid xid() { return tx.xid(); } /** {@inheritDoc} */ @Override public UUID nodeId() { if (async) save(tx.nodeId()); return tx.nodeId(); } /** {@inheritDoc} */ @Override public long threadId() { if (async) save(tx.threadId()); return tx.threadId(); } /** {@inheritDoc} */ @Override public long startTime() { if (async) save(tx.startTime()); return tx.startTime(); } /** {@inheritDoc} */ @Override public TransactionIsolation isolation() { if (async) save(tx.isolation()); return tx.isolation(); } /** {@inheritDoc} */ @Override public TransactionConcurrency concurrency() { if (async) save(tx.concurrency()); return tx.concurrency(); } /** {@inheritDoc} */ @Override public boolean isInvalidate() { if (async) save(tx.isInvalidate()); return tx.isInvalidate(); } /** {@inheritDoc} */ @Override public boolean implicit() { if (async) save(tx.implicit()); return tx.implicit(); } /** {@inheritDoc} */ @Override public long timeout() { if (async) save(tx.timeout()); return tx.timeout(); } /** {@inheritDoc} */ @Override public TransactionState state() { if (async) save(tx.state()); return tx.state(); } /** {@inheritDoc} */ @Override public long timeout(long timeout) { return tx.timeout(timeout); } /** {@inheritDoc} */ @Override public IgniteAsyncSupport withAsync() { return new TransactionProxyImpl<>(tx, cctx, true); } /** {@inheritDoc} */ @Override public boolean isAsync() { return async; } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <R> IgniteFuture<R> future() { return asyncRes; } /** {@inheritDoc} */ @Override public boolean setRollbackOnly() { enter(); try { return tx.setRollbackOnly(); } finally { leave(); } } /** {@inheritDoc} */ @Override public boolean isRollbackOnly() { enter(); try { if (async) save(tx.isRollbackOnly()); return tx.isRollbackOnly(); } finally { leave(); } } /** {@inheritDoc} */ @Override public void commit() { enter(); try { IgniteInternalFuture<IgniteInternalTx> commitFut = cctx.commitTxAsync(tx); if (async) saveFuture(commitFut); else commitFut.get(); } catch (IgniteCheckedException e) { throw U.convertException(e); } finally { leave(); } } /** {@inheritDoc} */ @Override public IgniteFuture<Void> commitAsync() throws IgniteException { enter(); try { return (IgniteFuture<Void>)createFuture(cctx.commitTxAsync(tx)); } finally { leave(); } } /** {@inheritDoc} */ @Override public void close() { enter(); try { cctx.endTx(tx); } catch (IgniteCheckedException e) { throw U.convertException(e); } finally { leave(); } } /** {@inheritDoc} */ @Override public void rollback() { enter(); try { IgniteInternalFuture rollbackFut = cctx.rollbackTxAsync(tx); if (async) asyncRes = new IgniteFutureImpl(rollbackFut); else rollbackFut.get(); } catch (IgniteCheckedException e) { throw U.convertException(e); } finally { leave(); } } /** {@inheritDoc} */ @Override public IgniteFuture<Void> rollbackAsync() throws IgniteException { enter(); try { return (IgniteFuture<Void>)(new IgniteFutureImpl(cctx.rollbackTxAsync(tx))); } catch (IgniteCheckedException e) { throw U.convertException(e); } finally { leave(); } } /** * @param res Result to convert to finished future. */ private void save(Object res) { asyncRes = new IgniteFinishedFutureImpl<>(res); } /** * @param fut Internal future. */ private void saveFuture(IgniteInternalFuture<IgniteInternalTx> fut) { asyncRes = createFuture(fut); } /** * @param fut Internal future. * @return User future. */ private IgniteFuture<?> createFuture(IgniteInternalFuture<IgniteInternalTx> fut) { IgniteInternalFuture<Transaction> fut0 = fut.chain(new CX1<IgniteInternalFuture<IgniteInternalTx>, Transaction>() { @Override public Transaction applyx(IgniteInternalFuture<IgniteInternalTx> fut) throws IgniteCheckedException { fut.get(); return TransactionProxyImpl.this; } }); return new IgniteFutureImpl(fut0); } /** {@inheritDoc} */ @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(tx); } /** {@inheritDoc} */ @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { tx = (GridNearTxLocal)in.readObject(); } /** {@inheritDoc} */ @Override public String toString() { return S.toString(TransactionProxyImpl.class, this); } }