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 java.util.Map; // for javadoc only
020import java.util.Objects;
021
022import javax.transaction.Status; // for javadoc only
023import javax.transaction.Synchronization;
024import javax.transaction.Transaction; // for javadoc only
025import javax.transaction.TransactionManager; // for javadoc only
026import javax.transaction.TransactionSynchronizationRegistry;
027
028/**
029 * An {@code abstract} {@link TransactionSynchronizationRegistry}
030 * implementation that delegates all method invocations to another
031 * {@link TransactionSynchronizationRegistry}.
032 *
033 * @author <a href="https://about.me/lairdnelson"
034 * target="_parent">Laird Nelson</a>
035 */
036public abstract class DelegatingTransactionSynchronizationRegistry implements TransactionSynchronizationRegistry {
037
038  private final TransactionSynchronizationRegistry delegate;
039
040  /**
041   * Creates a new {@link DelegatingTransactionSynchronizationRegistry}.
042   *
043   * @param delegate the {@link TransactionSynchronizationRegistry} to
044   * which all method invocations will be delegated; must not be
045   * {@code null}
046   *
047   * @exception NullPointerException if {@code delegate} is {@code
048   * null}
049   */
050  protected DelegatingTransactionSynchronizationRegistry(final TransactionSynchronizationRegistry delegate) {
051    super();
052    this.delegate = Objects.requireNonNull(delegate);
053  }
054
055  /**
056   * Return an opaque object to represent the transaction bound to the
057   * current thread at the time this method is called.
058   *
059   * <p>This method may return {@code null}.</p>
060   *
061   * <p>This object overrides {@link Object#hashCode()} and {@link
062   * Object#equals(Object)} to allow its use as the key in a {@link
063   * Map} for use by the caller. If there is no transaction currently
064   * active, this method will return {@code null}.</p>
065   *
066   * <p>The {@link Object} returned will return the same hashCode and
067   * compare equal to all other objects returned by calling this
068   * method from any component executing in the same transaction
069   * context in the same application server.</p>
070   *
071   * <p>The {@link Object#toString()} method returns a {@link String}
072   * that might be usable by a human reader to usefully understand the
073   * transaction context. The {@link Object#toString()} result is
074   * otherwise not defined. Specifically, there is no forward or
075   * backward compatibility guarantee of the results of the returned
076   * {@link Object}'s {@link Object#toString()} override.</p>
077   * 
078   * <p>The object is not necessarily serializable, and has no defined
079   * behavior outside the virtual machine whence it was obtained.</p>
080   *
081   * @return an opaque object representing the transaction bound to
082   * the current thread at the time this method is called, or {@code
083   * null}
084   */
085  @Override
086  public Object getTransactionKey() {
087    return this.delegate.getTransactionKey();
088  }
089
090  /**
091   * Adds or replaces an object in the {@link Map} of resources being
092   * managed for the transaction bound to the current thread at the
093   * time this method is called.
094   *
095   * <p>The supplied key should be of an caller-defined class so as
096   * not to conflict with other users. The class of the key must
097   * guarantee that the {@link Object#hashCode() hashCode()} and
098   * {@link Object#equals(Object) equals(Object)} methods are suitable
099   * for use as keys in a {@link Map}. The key and value are not
100   * examined or used by the implementation. The general contract of
101   * this method is that of {@link Map#put(Object, Object)} for a
102   * {@link Map} that supports non-{@code null} keys and null
103   * values. For example, if there is already an value associated with
104   * the key, it is replaced by the {@code value} parameter.</p>
105   *
106   * @param key the key for the {@link Map} entry; must not be {@code null}
107   *
108   * @param value the value for the {@link Map} entry
109   *
110   * @exception IllegalStateException if no transaction is active
111   *
112   * @exception NullPointerException if the parameter {@code key} is
113   * {@code null}
114  */
115  @Override
116  public void putResource(final Object key, final Object value) {
117    this.delegate.putResource(key, value);
118  }
119
120  /**
121   * Gest an object from the {@link Map} of resources being managed
122   * for the transaction bound to the current thread at the time this
123   * method is called.
124   *
125   * <p>The key should have been supplied earlier by a call to {@link
126   * #putResource(Object, Object)} in the same transaction. If the key
127   * cannot be found in the current resource {@link Map}, {@code null}
128   * is returned. The general contract of this method is that of
129   * {@link Map#get(Object)} for a {@link Map} that supports
130   * non-{@code null} keys and null values. For example, the returned
131   * value is null if there is no entry for the parameter {@code key}
132   * or if the value associated with the key is actually {@code
133   * null}.</p>
134   *
135   * @param key the key for the {@link Map} entry
136   *
137   * @return the value associated with the supplied {@code key}; may
138   * be {@code null}
139   *
140   * @exception IllegalStateException if no transaction is active
141   *
142   * @exception NullPointerException if the parameter {@code key} is
143   * {@code null}
144   */
145  @Override
146  public Object getResource(final Object key) {
147    return this.delegate.getResource(key);
148  }
149
150  /**
151   * Registers a {@link Synchronization} instance with special ordering semantics.
152   *
153   * <p>The supplied {@link Synchronization}'s {@link
154   * Synchronization#beforeCompletion()} method will be called after
155   * all {@code SessionSynchronization#beforeCompletion()} callbacks
156   * and callbacks registered directly with the {@link Transaction},
157   * but before the 2-phase commit process starts. Similarly, the
158   * {@link Synchronization#afterCompletion(int)} callback will be
159   * called after 2-phase commit completes but before any {@code
160   * SessionSynchronization} and {@link Transaction} {@code
161   * afterCompletion(int)} callbacks.</p>
162   *
163   * <p>The {@link Synchronization#beforeCompletion()} callback will
164   * be invoked in the transaction context of the transaction bound to
165   * the current thread at the time this method is called. Allowable
166   * methods include access to resources, e.g. connectors. No access
167   * is allowed to "user components" (e.g. timer services or bean
168   * methods), as these might change the state of data being managed
169   * by the caller, and might change the state of data that has
170   * already been flushed by another caller of {@link
171   * #registerInterposedSynchronization(Synchronization)}. The general
172   * context is the component context of the caller of {@link
173   * #registerInterposedSynchronization(Synchronization)}.</p>
174   *
175   * <p>The {@link Synchronization#afterCompletion(int)} callback will
176   * be invoked in an undefined context. No access is permitted to
177   * "user components" as defined above. Resources can be closed but
178   * no transactional work can be performed with them.</p>
179   *
180   * <p>If this method is invoked without an active transaction
181   * context, an {@link IllegalStateException} is thrown.</p>
182   *
183   * <p>If this method is invoked after the two-phase commit
184   * processing has started, an {@link IllegalStateException} is
185   * thrown.</p>
186   *
187   * @param synchronization the {@link Synchronization} to register;
188   * must not be {@code null}
189   *
190   * @exception IllegalStateException if no transaction is active or
191   * two-phase commit processing has started
192   *
193   * @see Synchronization
194   *
195   * @see Synchronization#beforeCompletion()
196   *
197   * @see Synchronization#afterCompletion(int)
198   */
199  @Override
200  public void registerInterposedSynchronization(final Synchronization synchronization) {
201    this.delegate.registerInterposedSynchronization(synchronization);
202  }
203
204  /**
205   * Return the status of the transaction bound to the current thread
206   * at the time this method is called.
207   *
208   * <p>This is the result of executing {@link
209   * TransactionManager#getStatus()} in the context of the transaction
210   * bound to the current thread at the time this method is
211   * called.</p>
212   *
213   * @return the status of the transaction bound to the current thread
214   * at the time this method is called; will be equal the value of one
215   * of the constants defined in the {@link Status} class
216   *
217   * @see TransactionManager#getStatus()
218   *
219   * @see Status
220   */
221  @Override
222  public int getTransactionStatus() {
223    return this.delegate.getTransactionStatus();
224  }
225
226  /**
227   * Sets the {@code rollbackOnly} status of the transaction bound to
228   * the current thread at the time this method is called.
229   *
230   * @exception IllegalStateException if no transaction is active
231   */
232  @Override
233  public void setRollbackOnly() {
234    this.delegate.setRollbackOnly();
235  }
236
237  /**
238   * Get the {@code rollbackOnly} status of the transaction bound to
239   * the current thread at the time this method is called.
240   *
241   * @return the {@code rollbackOnly} status
242   *
243   * @exception IllegalStateException if no transaction is active
244   */
245  @Override
246  public boolean getRollbackOnly() {
247    return this.delegate.getRollbackOnly();
248  }
249
250}