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