/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.jpa.container;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.jboss.as.jpa.messages.JpaLogger;
/**
* For tracking of SFSB call stack on a per thread basis.
* When a SFSB with an extended persistence context (XPC) is injected, the SFSB call stack is searched for
* a XPC that can be inherited from.
*
* @author Scott Marlow
*/
public class SFSBCallStack {
private static final ThreadLocal<SFSBCallStackThreadData> CURRENT = new ThreadLocal<SFSBCallStackThreadData>() {
@Override
protected SFSBCallStackThreadData initialValue() {
return new SFSBCallStackThreadData();
}
};
public static int getSFSBCreationBeanNestingLevel() {
return CURRENT.get().creationBeanNestingLevel;
}
/**
* called from SFSBPreCreateInterceptor, before bean creation
*/
public static void beginSfsbCreation() {
SFSBCallStackThreadData data = CURRENT.get();
int no = data.creationBeanNestingLevel;
if (no == 0) {
data.creationTimeXPCRegistration = new HashMap<String, ExtendedEntityManager>();
// create new tracking structure (passing in parent levels tracking structure or null if toplevel)
data.creationTimeInjectedXPCs = new SFSBInjectedXPCs(data.creationTimeInjectedXPCs, null);
}
else {
// create new tracking structure (passing in parent levels tracking structure or null if toplevel)
SFSBInjectedXPCs parent = data.creationTimeInjectedXPCs;
data.creationTimeInjectedXPCs = new SFSBInjectedXPCs(parent, parent.getTopLevel());
}
data.creationBeanNestingLevel++;
}
/**
* called from SFSBPreCreateInterceptor, after bean creation
*/
public static void endSfsbCreation() {
SFSBCallStackThreadData data = CURRENT.get();
int no = data.creationBeanNestingLevel;
no--;
data.creationBeanNestingLevel = no;
if (no == 0) {
// Completed creating top level bean, remove 'xpc creation tracking' thread local
data.creationTimeXPCRegistration = null;
data.creationTimeInjectedXPCs = null;
}
else {
// finished creating a sub-bean, switch to parent level 'xpc creation tracking'
data.creationTimeInjectedXPCs = data.creationTimeInjectedXPCs.getParent();
}
}
static SFSBInjectedXPCs getSFSBCreationTimeInjectedXPCs(final String puScopedName) {
SFSBInjectedXPCs result = CURRENT.get().creationTimeInjectedXPCs;
if (result == null) {
throw JpaLogger.ROOT_LOGGER.xpcOnlyFromSFSB(puScopedName);
}
return result;
}
/**
* Return the current entity manager call stack
*
* @return call stack (may be empty but never null)
*/
public static ArrayList<Map<String, ExtendedEntityManager>> currentSFSBCallStack() {
return CURRENT.get().invocationStack;
}
/**
* return for just the current entity manager invocation
*
* @return
*/
public static Map<String, ExtendedEntityManager> currentSFSBCallStackInvocation() {
ArrayList<Map<String, ExtendedEntityManager>> stack = CURRENT.get().invocationStack;
if ( stack != null && stack.size() > 0) {
return stack.get(stack.size() - 1);
}
return null;
}
/**
* Push the passed SFSB context handle onto the invocation call stack
*
* @param entityManagers the entity manager map
*/
public static void pushCall(Map<String, ExtendedEntityManager> entityManagers) {
currentSFSBCallStack().add(entityManagers);
if (entityManagers != null) {
/**
* JPA 2.0 spec section 7.9.1 Container Responsibilities:
* "When a business method of the stateful session bean is invoked,
* if the stateful session bean uses container managed transaction demarcation,
* and the entity manager is not already associated with the current JTA transaction,
* the container associates the entity manager with the current JTA transaction and
* calls EntityManager.joinTransaction.
* "
*/
for(ExtendedEntityManager extendedEntityManager: entityManagers.values()) {
extendedEntityManager.internalAssociateWithJtaTx();
}
}
}
/**
* Pops the current SFSB invocation off the invocation call stack
*
* @return the entity manager map
*/
public static Map<String, ExtendedEntityManager> popCall() {
ArrayList<Map<String, ExtendedEntityManager>> stack = currentSFSBCallStack();
Map<String, ExtendedEntityManager> result = stack.remove(stack.size() - 1);
stack.trimToSize();
return result;
}
/**
* gets the current SFSB invocation off the invocation call stack
*
* @return the entity manager map
*/
static Map<String, ExtendedEntityManager> getCurrentCall() {
ArrayList<Map<String, ExtendedEntityManager>> stack = currentSFSBCallStack();
Map<String, ExtendedEntityManager> result = null;
if (stack != null) {
result = stack.get(stack.size() - 1);
}
return result;
}
private static class SFSBCallStackThreadData {
/**
* Each thread will have its own list of SFSB invocations in progress.
*/
private ArrayList<Map<String, ExtendedEntityManager>> invocationStack = new ArrayList<Map<String, ExtendedEntityManager>>();
/**
* During SFSB creation, track the injected extended persistence contexts
*/
private Map<String, ExtendedEntityManager> creationTimeXPCRegistration = null;
private SFSBInjectedXPCs creationTimeInjectedXPCs;
/**
* Track the SFSB bean injection nesting level. Zero indicates the top level bean, one is the first level of SFSBs injected,
* two is the second level of SFSBs injected...
*/
private int creationBeanNestingLevel = 0;
}
}