/* * JBoss, Home of Professional Open Source. * Copyright (c) 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.ejb3.component.stateful; import java.io.ObjectStreamException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import javax.ejb.EJBException; import javax.transaction.Transaction; import org.jboss.as.ee.component.Component; import org.jboss.as.ee.component.ComponentInstance; import org.jboss.as.ejb3.cache.Contextual; import org.jboss.as.ejb3.cache.Identifiable; import org.jboss.as.ejb3.component.InvokeMethodOnTargetInterceptor; import org.jboss.as.ejb3.component.session.SessionBeanComponentInstance; import org.jboss.as.ejb3.tx.OwnableReentrantLock; import org.jboss.as.naming.ManagedReference; import org.jboss.ejb.client.SessionID; import org.jboss.invocation.Interceptor; import org.jboss.invocation.InterceptorContext; import org.wildfly.transaction.client.AbstractTransaction; import org.wildfly.transaction.client.ContextTransactionManager; /** * @author <a href="mailto:cdewolf@redhat.com">Carlo de Wolf</a> */ public class StatefulSessionComponentInstance extends SessionBeanComponentInstance implements Identifiable<SessionID>, Contextual<Object> { private static final long serialVersionUID = 3803978357389448971L; private final SessionID id; private final Interceptor afterBegin; private final Interceptor afterCompletion; private final Interceptor beforeCompletion; private final Interceptor prePassivate; private final Interceptor postActivate; private final Interceptor ejb2XRemoveInterceptor; /** * If this is a BMT bean this stores the active transaction */ private volatile Transaction transaction; /** * The transaction lock for the stateful bean */ private final OwnableReentrantLock lock = new OwnableReentrantLock(); /** * true if this bean has been enrolled in a transaction */ private boolean synchronizationRegistered = false; /** * The thread based lock for the stateful bean */ private final Object threadLock = new Object(); /** * State that is used to defer afterCompletion invocation if an invocation is currently in progress. * * States: * 0 = No invocation in progress * 1 = Invocation in progress * 2 = Invocation in progress, afterCompletion delayed * */ private final AtomicInteger invocationSynchState = new AtomicInteger(); public static final int SYNC_STATE_NO_INVOCATION = 0; public static final int SYNC_STATE_INVOCATION_IN_PROGRESS = 1; public static final int SYNC_STATE_AFTER_COMPLETE_DELAYED_NO_COMMIT = 2; public static final int SYNC_STATE_AFTER_COMPLETE_DELAYED_COMMITTED = 3; private boolean removed = false; boolean isSynchronizationRegistered() { return synchronizationRegistered; } void setSynchronizationRegistered(boolean synchronizationRegistered) { this.synchronizationRegistered = synchronizationRegistered; } Object getThreadLock() { return threadLock; } OwnableReentrantLock getLock() { return lock; } AtomicInteger getInvocationSynchState() { return invocationSynchState; } /** * Construct a new instance. * * @param component the component */ protected StatefulSessionComponentInstance(final StatefulSessionComponent component, final Interceptor preDestroyInterceptor, final Map<Method, Interceptor> methodInterceptors, final Map<Object, Object> context) { super(component, preDestroyInterceptor, methodInterceptors); final SessionID existingSession = (SessionID) context.get(SessionID.class); this.id = (existingSession != null) ? existingSession : component.getCache().createIdentifier(); this.afterBegin = component.getAfterBegin(); this.afterCompletion = component.getAfterCompletion(); this.beforeCompletion = component.getBeforeCompletion(); this.prePassivate = component.getPrePassivate(); this.postActivate = component.getPostActivate(); this.ejb2XRemoveInterceptor = component.getEjb2XRemoveMethod(); } protected void afterBegin() { CurrentSynchronizationCallback.set(CurrentSynchronizationCallback.CallbackType.AFTER_BEGIN); try { execute(afterBegin, getComponent().getAfterBeginMethod()); } finally { CurrentSynchronizationCallback.clear(); } } protected void afterCompletion(boolean committed) { CurrentSynchronizationCallback.set(CurrentSynchronizationCallback.CallbackType.AFTER_COMPLETION); try { execute(afterCompletion, getComponent().getAfterCompletionMethod(), committed); } finally { CurrentSynchronizationCallback.clear(); } } protected void beforeCompletion() { CurrentSynchronizationCallback.set(CurrentSynchronizationCallback.CallbackType.BEFORE_COMPLETION); try { execute(beforeCompletion, getComponent().getBeforeCompletionMethod()); } finally { CurrentSynchronizationCallback.clear(); } } protected void prePassivate() { this.execute(prePassivate, null); } protected void postActivate() { this.execute(postActivate, null); } @Override public void discard() { if (!isDiscarded()) { super.discard(); getComponent().getCache().discard(this); } } private Object execute(final Interceptor interceptor, final Method method, final Object... parameters) { if (interceptor == null) return null; final InterceptorContext interceptorContext = new InterceptorContext(); //we need the method so this does not count as a lifecycle invocation interceptorContext.setMethod(method); interceptorContext.putPrivateData(Component.class, getComponent()); interceptorContext.putPrivateData(ComponentInstance.class, this); interceptorContext.putPrivateData(InvokeMethodOnTargetInterceptor.PARAMETERS_KEY, parameters); interceptorContext.setContextData(new HashMap<String, Object>()); interceptorContext.setTarget(getInstance()); final AbstractTransaction transaction = ContextTransactionManager.getInstance().getTransaction(); interceptorContext.setTransactionSupplier(() -> transaction); try { return interceptor.processInvocation(interceptorContext); } catch (Error e) { throw e; } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new EJBException(e); } } @Override public StatefulSessionComponent getComponent() { return (StatefulSessionComponent) super.getComponent(); } @Override public SessionID getId() { return id; } public Interceptor getEjb2XRemoveInterceptor() { return ejb2XRemoveInterceptor; } @Override public String toString() { return " Instance of " + getComponent().getComponentName() + " {" + id + "}"; } public Object writeReplace() throws ObjectStreamException { Set<Object> keys = getComponent().getSerialiableInterceptorContextKeys(); final Map<Object, Object> serializableInterceptors = new HashMap<Object, Object>(); for(Object key : keys) { serializableInterceptors.put(key, getInstanceData(key)); } return new SerializedStatefulSessionComponent((ManagedReference)getInstanceData(INSTANCE_KEY), id, getComponent().getCreateServiceName().getCanonicalName(), serializableInterceptors); } public Transaction getTransaction() { return transaction; } public void setTransaction(Transaction transaction) { this.transaction = transaction; } void setRemoved(boolean removed) { this.removed = removed; } boolean isRemoved() { return removed; } @Override public Object getCacheContext() { return this.getInstanceData(Contextual.class); } @Override public void setCacheContext(Object context) { this.setInstanceData(Contextual.class, context); } }