/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.openejb.cdi.transactional; import org.apache.geronimo.transaction.manager.GeronimoTransactionManager; import org.apache.geronimo.transaction.manager.TransactionImpl; import org.apache.openejb.OpenEJB; import org.apache.openejb.loader.SystemInstance; import org.apache.webbeans.context.AbstractContext; import org.apache.webbeans.context.creational.BeanInstanceBag; import javax.enterprise.context.ContextNotActiveException; import javax.enterprise.context.spi.Contextual; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import javax.transaction.TransactionScoped; import javax.transaction.TransactionSynchronizationRegistry; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; public class TransactionContext extends AbstractContext implements Synchronization { private static final Class<?>[] MAP = new Class<?>[]{Map.class}; private final TransactionManager transactionManager = OpenEJB.getTransactionManager(); private final boolean geronimoTxMgr = GeronimoTransactionManager.class.isInstance(OpenEJB.getTransactionManager()); public TransactionContext() { super(TransactionScoped.class); } @Override public boolean isActive() { try { final int status = transactionManager.getTransaction().getStatus(); return status == Status.STATUS_ACTIVE || status == Status.STATUS_MARKED_ROLLBACK || status == Status.STATUS_PREPARED || status == Status.STATUS_PREPARING || status == Status.STATUS_COMMITTING || status == Status.STATUS_ROLLING_BACK || status == Status.STATUS_UNKNOWN; } catch (final Throwable e) { return false; } } @Override protected void checkActive() { if (!isActive()) { throw new ContextNotActiveException("Context with scope annotation @" + getScope().getName() + " is not active"); } } @Override protected void setComponentInstanceMap() { // TODO: think if not using a proxy could be more clever ;) componentInstanceMap = Map.class.cast(Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), MAP, new TransactionalMapHandler( this, SystemInstance.get().getComponent(TransactionSynchronizationRegistry.class)))); } @Override public void beforeCompletion() { if (!geronimoTxMgr) { // too early but we don't know how to do otherwise ATM destroy(); } } @Override public void afterCompletion(final int status) { if (geronimoTxMgr) { destroy(); } } private static class TransactionalMapHandler implements InvocationHandler { private static final String KEY = "@Transactional#OpenEJB.map"; private final TransactionSynchronizationRegistry registry; private final TransactionContext context; public TransactionalMapHandler(final TransactionContext transactionContext, final TransactionSynchronizationRegistry registry) { this.context = transactionContext; this.registry = registry; } @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { try { return method.invoke(findMap(), args); } catch (final InvocationTargetException ite) { throw ite.getCause(); } } private Map<Contextual<?>, BeanInstanceBag<?>> findMap() { final Object resource; try { // we can't call registry.getResource(KEY) in afterCompletion resource = context.geronimoTxMgr ? TransactionImpl.class.cast(context.transactionManager.getTransaction()).getResource(KEY): registry.getResource(KEY); } catch (final SystemException e) { throw new IllegalStateException(e); } if (resource == null) { final Map<Contextual<?>, BeanInstanceBag<?>> map = new HashMap<>(); registry.putResource(KEY, map); registry.registerInterposedSynchronization(context); return map; } return Map.class.cast(resource); } } }