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}