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}