001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2018 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.Collection;
020
021import javax.enterprise.context.ApplicationScoped;
022import javax.enterprise.context.Dependent;
023
024import javax.enterprise.event.Observes;
025
026import javax.enterprise.inject.Any;
027import javax.enterprise.inject.CreationException;
028import javax.enterprise.inject.Default;
029import javax.enterprise.inject.Produces;
030
031import javax.enterprise.inject.spi.AfterBeanDiscovery;
032import javax.enterprise.inject.spi.Bean;
033import javax.enterprise.inject.spi.BeanManager;
034import javax.enterprise.inject.spi.CDI;
035import javax.enterprise.inject.spi.Extension;
036
037import javax.transaction.SystemException;
038import javax.transaction.Transaction;
039import javax.transaction.TransactionManager;
040import javax.transaction.TransactionScoped;
041import javax.transaction.TransactionSynchronizationRegistry;
042import javax.transaction.UserTransaction;
043
044import com.arjuna.ats.jta.common.jtaPropertyManager;
045
046/**
047 * A <a
048 * href="http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#spi">CDI 2.0
049 * portable extension</a> that adapts the <a
050 * href="https://narayana.io/">Narayana transaction engine</a> to a <a
051 * href="http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#part_2">CDI
052 * 2.0 SE environment</a>.
053 *
054 * @author <a href="https://about.me/lairdnelson"
055 * target="_parent">Laird Nelson</a>
056 */
057public final class NarayanaExtension implements Extension {
058
059
060  /*
061   * Constructors.
062   */
063
064
065  /**
066   * Creates a new {@link NarayanaExtension}.
067   */
068  public NarayanaExtension() {
069    super();
070  }
071
072
073  /*
074   * Instance methods.
075   */
076
077
078  /**
079   * Adds a synthetic bean that creates a {@link Transaction} in
080   * {@linkplain TransactionScoped transaction scope}.
081   *
082   * @param event the {@link AfterBeanDiscovery} event fired by the
083   * CDI container; may be {@code null} in which case no action will
084   * be taken
085   */
086  private final void afterBeanDiscovery(@Observes final AfterBeanDiscovery event, final BeanManager beanManager) {
087    if (event != null) {
088
089      // Weld registers a UserTransaction bean well before this
090      // observer method fires.  OpenWebBeans does not.
091      final Collection<? extends Bean<?>> userTransactionBeans = beanManager.getBeans(UserTransaction.class);
092      if (userTransactionBeans == null || userTransactionBeans.isEmpty()) {
093        event.addBean()
094          .types(UserTransaction.class)
095          // see
096          // e.g. https://docs.oracle.com/javaee/6/tutorial/doc/gmgli.html
097          // which reads in part: "Predefined beans are injected with
098          // dependent scope and the predefined default
099          // qualifier @Default."  This scope restriction is not
100          // specified in the CDI specification but seems reasonable
101          // and widely expected.
102          .addQualifiers(Any.Literal.INSTANCE, Default.Literal.INSTANCE) // OpenWebBeans does not add these; Weld does automatically
103          .scope(Dependent.class)
104          .createWith(cc -> com.arjuna.ats.jta.UserTransaction.userTransaction());
105      }
106
107      event.addBean()
108        .id(Transaction.class.getName()) // TODO: is this OK?
109        .addQualifiers(Any.Literal.INSTANCE, Default.Literal.INSTANCE) // OpenWebBeans does not add these
110        .types(Transaction.class)
111        .scope(TransactionScoped.class)
112        .createWith(cc -> {
113            try {
114              return CDI.current().select(TransactionManager.class).get().getTransaction();
115            } catch (final SystemException systemException) {
116              throw new CreationException(systemException.getMessage(), systemException);
117            }
118          });
119
120    }
121  }
122
123}