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 {@link NarayanaTransactionManager}.
059   *
060   * @param jtaEnvironmentBean a {@link JTAEnvironmentBean} used to
061   * acquire this {@link NarayanaTransactionManager}'s delegate; must
062   * not be {@code null}
063   *
064   * @param transactionScopeInitializedBroadcaster an {@link Event}
065   * capable of {@linkplain Event#fire(Object) firing} {@link
066   * Transaction} instances; may be {@code null}
067   *
068   * @param transactionScopeDestroyedBroadcaster an {@link Event}
069   * capable of {@linkplain Event#fire(Object) firing} {@link Object}
070   * instances; may be {@code null}
071   *
072   * @exception NullPointerException if {@code jtaEnvironmentBean} is
073   * {@code null}
074   *
075   * @see #begin()
076   *
077   * @see #commit()
078   *
079   * @see #rollback()
080   */
081  @Inject
082  public NarayanaTransactionManager(final JTAEnvironmentBean jtaEnvironmentBean,
083                                    @Initialized(TransactionScoped.class)
084                                    final Event<Transaction> transactionScopeInitializedBroadcaster,
085                                    @Destroyed(TransactionScoped.class)
086                                    final Event<Object> transactionScopeDestroyedBroadcaster) {
087    super(jtaEnvironmentBean.getTransactionManager());
088    this.transactionScopeInitializedBroadcaster = transactionScopeInitializedBroadcaster;
089    this.transactionScopeDestroyedBroadcaster = transactionScopeDestroyedBroadcaster;
090  }
091
092  /**
093   * Overrides {@link DelegatingTransactionManager#begin()} to
094   * additionally {@linkplain Event#fire(Object) fire} an {@link
095   * Object} representing the {@linkplain Initialized initialization}
096   * of the {@linkplain TransactionScoped transaction scope}.
097   *
098   * @exception NotSupportedException if the thread is already
099   * associated with a transaction and this {@link TransactionManager}
100   * implementation does not support nested transactions
101   *
102   * @exception SystemException if this {@link TransactionManager}
103   * encounters an unexpected error condition
104   *
105   * @see DelegatingTransactionManager#begin()
106   *
107   * @see Event#fire(Object)
108   *
109   * @see Initialized
110   *
111   * @see TransactionScoped
112   */
113  @Override
114  public void begin() throws NotSupportedException, SystemException {
115    super.begin();
116    if (this.transactionScopeInitializedBroadcaster != null) {
117      this.transactionScopeInitializedBroadcaster.fire(this.getTransaction());
118    }
119  }
120
121  /**
122   * Overrides {@link DelegatingTransactionManager#commit()} to
123   * additionally {@linkplain Event#fire(Object) fire} an {@link
124   * Object} representing the {@linkplain Destroyed destruction}
125   * of the {@linkplain TransactionScoped transaction scope}.
126   *
127   * @exception RollbackException if the transaction has been rolled
128   * back rather than committed
129   *
130   * @exception HeuristicMixedException if a heuristic decision was
131   * made and that some relevant updates have been committed while
132   * others have been rolled back
133   *
134   * @exception HeuristicRollbackException if a heuristic decision was
135   * made and all relevant updates have been rolled back
136   *
137   * @exception SecurityException if the thread is not allowed to
138   * commit the transaction
139   *
140   * @exception IllegalStateException if the current thread is not
141   * associated with a transaction
142   *
143   * @exception SystemException if this {@link TransactionManager}
144   * encounters an unexpected error condition
145   *
146   * @see DelegatingTransactionManager#commit()
147   *
148   * @see Event#fire(Object)
149   *
150   * @see Destroyed
151   *
152   * @see TransactionScoped
153   */
154  @Override
155  public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SystemException {
156    try {
157      super.commit();
158    } finally {
159      if (this.transactionScopeDestroyedBroadcaster != null) {
160        this.transactionScopeDestroyedBroadcaster.fire(this.toString());
161      }
162    }
163  }
164
165  /**
166   * Overrides {@link DelegatingTransactionManager#rollback()} to
167   * additionally {@linkplain Event#fire(Object) fire} an {@link
168   * Object} representing the {@linkplain Destroyed destruction}
169   * of the {@linkplain TransactionScoped transaction scope}.
170   *
171   * @exception SecurityException if the thread is not allowed to roll
172   * back the transaction
173   *
174   * @exception IllegalStateException if the current thread is not
175   * associated with a transaction
176   *
177   * @exception SystemException if this {@link TransactionManager}
178   * encounters an unexpected error condition
179   *
180   * @see DelegatingTransactionManager#rollback()
181   *
182   * @see Event#fire(Object)
183   *
184   * @see Destroyed
185   *
186   * @see TransactionScoped
187   */
188  @Override
189  public void rollback() throws SystemException {
190    try {
191      super.rollback();
192    } finally {
193      if (this.transactionScopeDestroyedBroadcaster != null) {
194        this.transactionScopeDestroyedBroadcaster.fire(this.toString());
195      }
196    }
197  }
198
199}