3131
3232class ClientSessionImpl extends BaseClientSessionImpl implements ClientSession {
3333
34+ private enum TransactionState {
35+ NONE , IN , DONE , ABORTED
36+ }
37+
3438 private final OperationExecutor executor ;
35- private boolean inTransaction ;
39+ private TransactionState transactionState = TransactionState . NONE ;
3640 private boolean messageSent ;
41+ private boolean commitInProgress ;
42+
3743 private TransactionOptions transactionOptions ;
3844
3945 ClientSessionImpl (final ServerSessionPool serverSessionPool , final MongoClient mongoClient , final ClientSessionOptions options ,
@@ -44,7 +50,7 @@ class ClientSessionImpl extends BaseClientSessionImpl implements ClientSession {
4450
4551 @ Override
4652 public boolean hasActiveTransaction () {
47- return inTransaction ;
53+ return transactionState == TransactionState . IN || ( transactionState == TransactionState . DONE && commitInProgress ) ;
4854 }
4955
5056 @ Override
@@ -56,7 +62,7 @@ public boolean notifyMessageSent() {
5662
5763 @ Override
5864 public TransactionOptions getTransactionOptions () {
59- isTrue ("in transaction" , inTransaction );
65+ isTrue ("in transaction" , transactionState == TransactionState . IN || transactionState == TransactionState . DONE );
6066 return transactionOptions ;
6167 }
6268
@@ -68,32 +74,42 @@ public void startTransaction() {
6874 @ Override
6975 public void startTransaction (final TransactionOptions transactionOptions ) {
7076 notNull ("transactionOptions" , transactionOptions );
71- if (inTransaction ) {
77+ if (transactionState == TransactionState . IN ) {
7278 throw new IllegalStateException ("Transaction already in progress" );
7379 }
74- inTransaction = true ;
80+ if (transactionState == TransactionState .DONE ) {
81+ cleanupTransaction (TransactionState .IN );
82+ } else {
83+ transactionState = TransactionState .IN ;
84+ }
85+ getServerSession ().advanceTransactionNumber ();
7586 this .transactionOptions = TransactionOptions .merge (transactionOptions , getOptions ().getDefaultTransactionOptions ());
7687 }
7788
7889 @ Override
7990 public void commitTransaction (final SingleResultCallback <Void > callback ) {
80- if (!canCommitOrAbort ()) {
91+ if (transactionState == TransactionState .ABORTED ) {
92+ throw new IllegalStateException ("Cannot call commitTransaction after calling abortTransaction" );
93+ }
94+ if (transactionState == TransactionState .NONE ) {
8195 throw new IllegalStateException ("There is no transaction started" );
8296 }
8397 if (!messageSent ) {
84- cleanupTransaction ();
98+ cleanupTransaction (TransactionState . DONE );
8599 callback .onResult (null , null );
86100 } else {
87101 ReadConcern readConcern = transactionOptions .getReadConcern ();
88102 if (readConcern == null ) {
89103 throw new MongoInternalException ("Invariant violated. Transaction options read concern can not be null" );
90104 }
105+ commitInProgress = true ;
91106 executor .execute (new CommitTransactionOperation (transactionOptions .getWriteConcern ()),
92107 readConcern , this ,
93108 new SingleResultCallback <Void >() {
94109 @ Override
95110 public void onResult (final Void result , final Throwable t ) {
96- cleanupTransaction ();
111+ commitInProgress = false ;
112+ transactionState = TransactionState .DONE ;
97113 callback .onResult (result , t );
98114 }
99115 });
@@ -102,11 +118,17 @@ public void onResult(final Void result, final Throwable t) {
102118
103119 @ Override
104120 public void abortTransaction (final SingleResultCallback <Void > callback ) {
105- if (!canCommitOrAbort ()) {
121+ if (transactionState == TransactionState .ABORTED ) {
122+ throw new IllegalStateException ("Cannot call abortTransaction twice" );
123+ }
124+ if (transactionState == TransactionState .DONE ) {
125+ throw new IllegalStateException ("Cannot call abortTransaction after calling commitTransaction" );
126+ }
127+ if (transactionState == TransactionState .NONE ) {
106128 throw new IllegalStateException ("There is no transaction started" );
107129 }
108130 if (!messageSent ) {
109- cleanupTransaction ();
131+ cleanupTransaction (TransactionState . ABORTED );
110132 callback .onResult (null , null );
111133 } else {
112134 ReadConcern readConcern = transactionOptions .getReadConcern ();
@@ -118,21 +140,17 @@ public void abortTransaction(final SingleResultCallback<Void> callback) {
118140 new SingleResultCallback <Void >() {
119141 @ Override
120142 public void onResult (final Void result , final Throwable t ) {
121- cleanupTransaction ();
143+ cleanupTransaction (TransactionState . ABORTED );
122144 callback .onResult (null , null );
123145 }
124146 });
125147 }
126148 }
127149
128- private boolean canCommitOrAbort () {
129- return inTransaction ;
130- }
131-
132150 // TODO: should there be a version of this that takes a callback?
133151 @ Override
134152 public void close () {
135- if (inTransaction ) {
153+ if (transactionState == TransactionState . IN ) {
136154 abortTransaction (new SingleResultCallback <Void >() {
137155 @ Override
138156 public void onResult (final Void result , final Throwable t ) {
@@ -144,10 +162,9 @@ public void onResult(final Void result, final Throwable t) {
144162 }
145163 }
146164
147- private void cleanupTransaction () {
148- inTransaction = false ;
165+ private void cleanupTransaction (final TransactionState nextState ) {
149166 messageSent = false ;
150167 transactionOptions = null ;
151- getServerSession (). advanceTransactionNumber () ;
168+ transactionState = nextState ;
152169 }
153170}
0 commit comments