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.lang.reflect.Field; 020import java.lang.reflect.Modifier; 021 022import javax.enterprise.context.ApplicationScoped; 023 024import javax.enterprise.event.Observes; 025 026import javax.enterprise.inject.CreationException; 027 028import javax.enterprise.inject.spi.AfterBeanDiscovery; 029import javax.enterprise.inject.spi.AfterDeploymentValidation; 030import javax.enterprise.inject.spi.Bean; 031import javax.enterprise.inject.spi.BeanManager; 032import javax.enterprise.inject.spi.CDI; 033import javax.enterprise.inject.spi.Extension; 034import javax.enterprise.inject.spi.ProcessAnnotatedType; 035 036import javax.transaction.SystemException; 037import javax.transaction.Transaction; 038import javax.transaction.TransactionManager; 039import javax.transaction.TransactionScoped; 040import javax.transaction.TransactionSynchronizationRegistry; 041 042import com.arjuna.ats.jta.cdi.TransactionContext; 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 private final void afterBeanDiscovery(@Observes final AfterBeanDiscovery event) { 079 if (event != null) { 080 081 event.addBean() 082 .types(TransactionManager.class) 083 .scope(ApplicationScoped.class) 084 .createWith(cc -> com.arjuna.ats.jta.TransactionManager.transactionManager()); 085 086 event.addBean() 087 .types(Transaction.class) 088 .scope(TransactionScoped.class) 089 .createWith(cc -> { 090 try { 091 return CDI.current().select(TransactionManager.class).get().getTransaction(); 092 } catch (final SystemException systemException) { 093 throw new CreationException(systemException.getMessage(), systemException); 094 } 095 }); 096 097 event.addBean() 098 .types(TransactionSynchronizationRegistry.class) 099 .scope(ApplicationScoped.class) 100 .createWith(cc -> jtaPropertyManager.getJTAEnvironmentBean().getTransactionSynchronizationRegistry()); 101 102 } 103 } 104 105 private final void onTypeDiscovery(@Observes final ProcessAnnotatedType<? extends com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase> event) { 106 if (event != null && 107 event.getAnnotatedType().getBaseType().getTypeName().startsWith("com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptor")) { 108 // Programmatically remove the interceptors installed by the 109 // stock Narayana extension. These will trigger a JNDI 110 // lookup which will fail, since we are not guaranteed the 111 // presence of a JNDI implementation in the microBean 112 // environment. We replace them with similar almost-clones 113 // present in this package that bypass JNDI machinery. 114 // See also: https://github.com/jbosstm/narayana/pull/1344 115 event.veto(); 116 } 117 } 118 119 private final void afterDeploymentValidation(@Observes final AfterDeploymentValidation event, final BeanManager beanManager) throws ReflectiveOperationException { 120 121 if (event != null && beanManager != null) { 122 123 final Bean<?> transactionManagerBean = 124 beanManager.resolve(beanManager.getBeans(TransactionManager.class)); 125 assert transactionManagerBean != null; 126 final TransactionManager transactionManager = 127 (TransactionManager)beanManager.getReference(transactionManagerBean, 128 TransactionManager.class, 129 beanManager.createCreationalContext(transactionManagerBean)); 130 assert transactionManager != null; 131 132 final Bean<?> transactionSynchronizationRegistryBean = beanManager.resolve(beanManager.getBeans(TransactionSynchronizationRegistry.class)); 133 assert transactionSynchronizationRegistryBean != null; 134 final TransactionSynchronizationRegistry transactionSynchronizationRegistry = 135 (TransactionSynchronizationRegistry)beanManager.getReference(transactionSynchronizationRegistryBean, 136 TransactionSynchronizationRegistry.class, 137 beanManager.createCreationalContext(transactionSynchronizationRegistryBean)); 138 assert transactionSynchronizationRegistry != null; 139 140 // Hack the TransactionContext class to not require JNDI. 141 142 Field field = TransactionContext.class.getDeclaredField("transactionManager"); 143 assert field != null; 144 assert Modifier.isStatic(field.getModifiers()); 145 assert Modifier.isPrivate(field.getModifiers()); 146 assert TransactionManager.class.equals(field.getType()); 147 field.setAccessible(true); 148 field.set(null, transactionManager); 149 150 field = TransactionContext.class.getDeclaredField("transactionSynchronizationRegistry"); 151 assert field != null; 152 assert Modifier.isStatic(field.getModifiers()); 153 assert Modifier.isPrivate(field.getModifiers()); 154 assert TransactionSynchronizationRegistry.class.equals(field.getType()); 155 field.setAccessible(true); 156 field.set(null, transactionSynchronizationRegistry); 157 158 } 159 160 } 161 162}