All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.cdi.transaction.TransactionScopedContextImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2022 Contributors to the Eclipse Foundation
 * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.cdi.transaction;

import jakarta.enterprise.context.ContextNotActiveException;
import jakarta.enterprise.context.spi.Context;
import jakarta.enterprise.context.spi.Contextual;
import jakarta.enterprise.context.spi.CreationalContext;
import jakarta.enterprise.inject.spi.PassivationCapable;
import jakarta.transaction.TransactionScoped;
import jakarta.transaction.TransactionSynchronizationRegistry;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import static jakarta.transaction.Status.STATUS_ACTIVE;
import static jakarta.transaction.Status.STATUS_COMMITTING;
import static jakarta.transaction.Status.STATUS_MARKED_ROLLBACK;
import static jakarta.transaction.Status.STATUS_PREPARED;
import static jakarta.transaction.Status.STATUS_PREPARING;
import static jakarta.transaction.Status.STATUS_ROLLING_BACK;
import static jakarta.transaction.Status.STATUS_UNKNOWN;
import static java.util.Collections.synchronizedSet;
import static org.glassfish.api.naming.SimpleJndiName.JNDI_CTX_JAVA_COMPONENT;

/**
 * The Context implementation for obtaining contextual instances of {@link TransactionScoped} beans.
 *
 * 

* The contextual instances are destroyed when the transaction completes. * *

* Any attempt to call a method on a {@link TransactionScoped} bean when a transaction is not active will result in a * {@Link jakarta.enterprise.context.ContextNotActiveException}. * *

* A CDI Event: @Initialized(TransactionScoped.class) is fired with {@link TransactionScopedCDIEventPayload}, when the * context is initialized for the first time and @Destroyed(TransactionScoped.class) is fired with * {@link TransactionScopedCDIEventPayload}, when the context is destroyed at the end. Currently this payload is empty * i.e. it doesn't contain any information. * * @author JJ Snyder * @author Arjav Desai */ public class TransactionScopedContextImpl implements Context { public static final String TRANSACTION_SYNCHRONIZATION_REGISTRY_JNDI_NAME = JNDI_CTX_JAVA_COMPONENT + "TransactionSynchronizationRegistry"; ConcurrentHashMap>> beansPerTransaction; public TransactionScopedContextImpl() { beansPerTransaction = new ConcurrentHashMap<>(); } public ConcurrentHashMap>> getBeansPerTransaction() { return beansPerTransaction; } @Override public Class getScope() { return TransactionScoped.class; } @Override public T get(Contextual contextual, CreationalContext creationalContext) { TransactionSynchronizationRegistry transactionSynchronizationRegistry = getTransactionSynchronizationRegistry(); Object beanId = getContextualId(contextual); T contextualInstance = getContextualInstance(beanId, transactionSynchronizationRegistry); if (contextualInstance == null) { contextualInstance = createContextualInstance(contextual, beanId, creationalContext, transactionSynchronizationRegistry); } return contextualInstance; } @Override public T get(Contextual contextual) { return getContextualInstance(getContextualId(contextual), getTransactionSynchronizationRegistry()); } /** * Determines if this context object is active. * * @return true if there is a current global transaction and its status is * {@Link jakarta.transaction.Status.STATUS_ACTIVE} false otherwise */ @Override public boolean isActive() { try { //Just calling it but not checking for != null on return value as its already done inside method getTransactionSynchronizationRegistry(); return true; } catch (ContextNotActiveException ignore) { } return false; } private Object getContextualId(Contextual contextual) { if (contextual instanceof PassivationCapable) { PassivationCapable passivationCapable = (PassivationCapable) contextual; return passivationCapable.getId(); } return contextual; } private T createContextualInstance(Contextual contextual, Object contextualId, CreationalContext creationalContext, TransactionSynchronizationRegistry transactionSynchronizationRegistry) { TransactionScopedBean transactionScopedBean = new TransactionScopedBean<>(contextual, creationalContext, this); transactionSynchronizationRegistry.putResource(contextualId, transactionScopedBean); transactionSynchronizationRegistry.registerInterposedSynchronization(transactionScopedBean); // Adding TransactionScopedBean as Set, per transactionSynchronizationRegistry, which is unique per transaction // Setting synchronizedSet so that even is there are multiple transaction for an app its safe Set> transactionScopedBeanSet = beansPerTransaction.get(transactionSynchronizationRegistry); if (transactionScopedBeanSet != null) { transactionScopedBeanSet = synchronizedSet(transactionScopedBeanSet); } else { transactionScopedBeanSet = synchronizedSet(new HashSet<>()); // Fire this event only for the first initialization of context and not for every TransactionScopedBean in a Transaction TransactionScopedCDIUtil.fireEvent(TransactionScopedCDIUtil.INITIALIZED_EVENT); // Adding transactionScopedBeanSet in Map for the first time for this transactionSynchronizationRegistry key beansPerTransaction.put(transactionSynchronizationRegistry, transactionScopedBeanSet); } transactionScopedBeanSet.add(transactionScopedBean); // Not updating entry in main Map with new TransactionScopedBeans as it should happen by reference return transactionScopedBean.getContextualInstance(); } private T getContextualInstance(Object beanId, TransactionSynchronizationRegistry transactionSynchronizationRegistry) { Object obj = transactionSynchronizationRegistry.getResource(beanId); TransactionScopedBean transactionScopedBean = (TransactionScopedBean) obj; if (transactionScopedBean != null) { return transactionScopedBean.getContextualInstance(); } return null; } private TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() { TransactionSynchronizationRegistry transactionSynchronizationRegistry; try { transactionSynchronizationRegistry = (TransactionSynchronizationRegistry) new InitialContext().lookup(TRANSACTION_SYNCHRONIZATION_REGISTRY_JNDI_NAME); } catch (NamingException ne) { throw new ContextNotActiveException("Could not get TransactionSynchronizationRegistry", ne); } int status = transactionSynchronizationRegistry.getTransactionStatus(); if (status == STATUS_ACTIVE || status == STATUS_MARKED_ROLLBACK || status == STATUS_PREPARED || status == STATUS_UNKNOWN || status == STATUS_PREPARING || status == STATUS_COMMITTING || status == STATUS_ROLLING_BACK) { return transactionSynchronizationRegistry; } throw new ContextNotActiveException("TransactionSynchronizationRegistry status is not active."); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy