001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2019 microBean.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.microbean.narayana.jta.cdi;
018
019import javax.enterprise.context.ApplicationScoped;
020import javax.enterprise.context.Destroyed;
021import javax.enterprise.context.Initialized;
022
023import javax.enterprise.event.Event;
024
025import javax.inject.Inject;
026
027import javax.transaction.HeuristicMixedException;
028import javax.transaction.HeuristicRollbackException;
029import javax.transaction.NotSupportedException;
030import javax.transaction.RollbackException;
031import javax.transaction.SystemException;
032import javax.transaction.Transaction;
033import javax.transaction.TransactionManager; // for javadoc only
034import javax.transaction.TransactionScoped;
035
036import com.arjuna.ats.jta.common.JTAEnvironmentBean;
037
038/**
039 * A {@link DelegatingTransactionManager} in {@linkplain
040 * ApplicationScoped application scope} that uses the return value
041 * that results from invoking the {@link
042 * JTAEnvironmentBean#getTransactionManager()} method as its backing
043 * implementation.
044 *
045 * @author <a href="https://about.me/lairdnelson"
046 * target="_parent">Laird Nelson</a>
047 *
048 * @see com.arjuna.ats.jta.common.JTAEnvironmentBean#getTransactionManager()
049 */
050@ApplicationScoped
051public class NarayanaTransactionManager extends DelegatingTransactionManager {
052
053  private final Event<Transaction> transactionScopeInitializedBroadcaster;
054
055  private final Event<Object> transactionScopeDestroyedBroadcaster;
056
057  /**
058   * Creates a new, <strong>nonfunctional</strong> {@link
059   * NarayanaTransactionManager}.
060   *
061   * <p>This constructor exists only to conform with section 3.11 of
062   * the CDI specification.</p>
063   *
064   * @deprecated This constructor exists only to conform with section
065   * 3.11 of the CDI specification; please use the {@link
066   * #NarayanaTransactionManager(JTAEnvironmentBean, Event, Event)}
067   * constructor instead.
068   */
069  @Deprecated
070  protected NarayanaTransactionManager() {
071    super(null);
072    this.transactionScopeInitializedBroadcaster = null;
073    this.transactionScopeDestroyedBroadcaster = null;
074  }
075  
076  /**
077   * Creates a new {@link NarayanaTransactionManager}.
078   *
079   * @param jtaEnvironmentBean a {@link JTAEnvironmentBean} used to
080   * acquire this {@link NarayanaTransactionManager}'s delegate; must
081   * not be {@code null}
082   *
083   * @param transactionScopeInitializedBroadcaster an {@link Event}
084   * capable of {@linkplain Event#fire(Object) firing} {@link
085   * Transaction} instances; may be {@code null}
086   *
087   * @param transactionScopeDestroyedBroadcaster an {@link Event}
088   * capable of {@linkplain Event#fire(Object) firing} {@link Object}
089   * instances; may be {@code null}
090   *
091   * @exception NullPointerException if {@code jtaEnvironmentBean} is
092   * {@code null}
093   *
094   * @see #begin()
095   *
096   * @see #commit()
097   *
098   * @see #rollback()
099   */
100  @Inject
101  public NarayanaTransactionManager(final JTAEnvironmentBean jtaEnvironmentBean,
102                                    @Initialized(TransactionScoped.class)
103                                    final Event<Transaction> transactionScopeInitializedBroadcaster,
104                                    @Destroyed(TransactionScoped.class)
105                                    final Event<Object> transactionScopeDestroyedBroadcaster) {
106    super(jtaEnvironmentBean.getTransactionManager());
107    this.transactionScopeInitializedBroadcaster = transactionScopeInitializedBroadcaster;
108    this.transactionScopeDestroyedBroadcaster = transactionScopeDestroyedBroadcaster;
109  }
110
111  /**
112   * Overrides {@link DelegatingTransactionManager#begin()} to
113   * additionally {@linkplain Event#fire(Object) fire} an {@link
114   * Object} representing the {@linkplain Initialized initialization}
115   * of the {@linkplain TransactionScoped transaction scope}.
116   *
117   * @exception NotSupportedException if the thread is already
118   * associated with a transaction and this {@link TransactionManager}
119   * implementation does not support nested transactions
120   *
121   * @exception SystemException if this {@link TransactionManager}
122   * encounters an unexpected error condition
123   *
124   * @see DelegatingTransactionManager#begin()
125   *
126   * @see Event#fire(Object)
127   *
128   * @see Initialized
129   *
130   * @see TransactionScoped
131   */
132  @Override
133  public void begin() throws NotSupportedException, SystemException {
134    super.begin();
135    if (this.transactionScopeInitializedBroadcaster != null) {
136      this.transactionScopeInitializedBroadcaster.fire(this.getTransaction());
137    }
138  }
139
140  /**
141   * Overrides {@link DelegatingTransactionManager#commit()} to
142   * additionally {@linkplain Event#fire(Object) fire} an {@link
143   * Object} representing the {@linkplain Destroyed destruction}
144   * of the {@linkplain TransactionScoped transaction scope}.
145   *
146   * @exception RollbackException if the transaction has been rolled
147   * back rather than committed
148   *
149   * @exception HeuristicMixedException if a heuristic decision was
150   * made and that some relevant updates have been committed while
151   * others have been rolled back
152   *
153   * @exception HeuristicRollbackException if a heuristic decision was
154   * made and all relevant updates have been rolled back
155   *
156   * @exception SecurityException if the thread is not allowed to
157   * commit the transaction
158   *
159   * @exception IllegalStateException if the current thread is not
160   * associated with a transaction
161   *
162   * @exception SystemException if this {@link TransactionManager}
163   * encounters an unexpected error condition
164   *
165   * @see DelegatingTransactionManager#commit()
166   *
167   * @see Event#fire(Object)
168   *
169   * @see Destroyed
170   *
171   * @see TransactionScoped
172   */
173  @Override
174  public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SystemException {
175    try {
176      super.commit();
177    } finally {
178      if (this.transactionScopeDestroyedBroadcaster != null) {
179        this.transactionScopeDestroyedBroadcaster.fire(this.toString());
180      }
181    }
182  }
183
184  /**
185   * Overrides {@link DelegatingTransactionManager#rollback()} to
186   * additionally {@linkplain Event#fire(Object) fire} an {@link
187   * Object} representing the {@linkplain Destroyed destruction}
188   * of the {@linkplain TransactionScoped transaction scope}.
189   *
190   * @exception SecurityException if the thread is not allowed to roll
191   * back the transaction
192   *
193   * @exception IllegalStateException if the current thread is not
194   * associated with a transaction
195   *
196   * @exception SystemException if this {@link TransactionManager}
197   * encounters an unexpected error condition
198   *
199   * @see DelegatingTransactionManager#rollback()
200   *
201   * @see Event#fire(Object)
202   *
203   * @see Destroyed
204   *
205   * @see TransactionScoped
206   */
207  @Override
208  public void rollback() throws SystemException {
209    try {
210      super.rollback();
211    } finally {
212      if (this.transactionScopeDestroyedBroadcaster != null) {
213        this.transactionScopeDestroyedBroadcaster.fire(this.toString());
214      }
215    }
216  }
217
218}