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}