public void timedOut(Instance instance) {
      BeanContext beanContext = instance.beanContext;

      ThreadContext threadContext =
          new ThreadContext(beanContext, instance.primaryKey, Operation.PRE_DESTROY);
      threadContext.setCurrentAllowedStates(null);
      ThreadContext oldContext = ThreadContext.enter(threadContext);
      try {
        Method remove =
            instance.bean instanceof SessionBean ? SessionBean.class.getMethod("ejbRemove") : null;

        List<InterceptorData> callbackInterceptors = beanContext.getCallbackInterceptors();
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean,
                remove,
                Operation.PRE_DESTROY,
                callbackInterceptors,
                instance.interceptors);

        interceptorStack.invoke();
      } catch (Throwable e) {
        logger.error(
            "An unexpected exception occured while invoking the ejbRemove method on the timed-out Stateful SessionBean instance",
            e);
      } finally {
        logger.info("Removing the timed-out stateful session bean instance " + instance.primaryKey);
        ThreadContext.exit(oldContext);
      }
    }
    public void afterLoad(Instance instance) throws SystemException, ApplicationException {
      BeanContext beanContext = instance.beanContext;

      ThreadContext threadContext =
          new ThreadContext(instance.beanContext, instance.primaryKey, Operation.ACTIVATE);
      ThreadContext oldContext = ThreadContext.enter(threadContext);
      try {
        Method remove =
            instance.bean instanceof SessionBean
                ? SessionBean.class.getMethod("ejbActivate")
                : null;

        List<InterceptorData> callbackInterceptors = beanContext.getCallbackInterceptors();
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean,
                remove,
                Operation.ACTIVATE,
                callbackInterceptors,
                instance.interceptors);

        interceptorStack.invoke();
      } catch (Throwable callbackException) {
        discardInstance(threadContext);
        handleSystemException(
            threadContext.getTransactionPolicy(), callbackException, threadContext);
      } finally {
        ThreadContext.exit(oldContext);
      }
    }
    public void beforeStore(Instance instance) {
      BeanContext beanContext = instance.beanContext;

      ThreadContext threadContext =
          new ThreadContext(beanContext, instance.primaryKey, Operation.PASSIVATE);
      ThreadContext oldContext = ThreadContext.enter(threadContext);
      try {
        Method passivate =
            instance.bean instanceof SessionBean
                ? SessionBean.class.getMethod("ejbPassivate")
                : null;

        List<InterceptorData> callbackInterceptors = beanContext.getCallbackInterceptors();
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean,
                passivate,
                Operation.PASSIVATE,
                callbackInterceptors,
                instance.interceptors);

        interceptorStack.invoke();

      } catch (Throwable e) {
        logger.error(
            "An unexpected exception occured while invoking the ejbPassivate method on the Stateful SessionBean instance",
            e);
      } finally {
        ThreadContext.exit(oldContext);
      }
    }
    public void afterCompletion(Status status) {
      Throwable firstException = null;
      for (Synchronization synchronization : registry.values()) {

        Instance instance = synchronization.instance;

        ThreadContext callContext =
            new ThreadContext(
                instance.beanContext, instance.primaryKey, Operation.AFTER_COMPLETION);
        callContext.setCurrentAllowedStates(null);
        ThreadContext oldCallContext = ThreadContext.enter(callContext);
        try {
          instance.setInUse(true);
          if (synchronization.isCallSessionSynchronization()) {

            BeanContext beanContext = instance.beanContext;
            List<InterceptorData> interceptors = beanContext.getCallbackInterceptors();
            InterceptorStack interceptorStack =
                new InterceptorStack(
                    instance.bean,
                    null,
                    Operation.AFTER_COMPLETION,
                    interceptors,
                    instance.interceptors);
            interceptorStack.invoke(status == Status.COMMITTED);
          }
          instance.setTransaction(null);
          releaseInstance(instance);
        } catch (InvalidateReferenceException inv) {
          // exception has alredy been handled
        } catch (Throwable e) {
          String message =
              "An unexpected system exception occured while invoking the afterCompletion method on the SessionSynchronization object";

          // [1] Log the exception or error
          logger.error(message, e);

          // Transaction is complete so can not be rolled back

          // [3] Discard the instance
          discardInstance(callContext);

          // [4] throw throw first exception to the client
          if (firstException == null) firstException = e;
        } finally {
          ThreadContext.exit(oldCallContext);
        }
      }

      if (firstException != null) {
        throw new RuntimeException(
            "An unexpected system exception occured while invoking the afterCompletion method on the SessionSynchronization object",
            firstException);
      }
    }
    public void beforeCompletion() {
      for (Synchronization synchronization : registry.values()) {

        Instance instance = synchronization.instance;

        // don't call beforeCompletion when transaction is marked rollback only
        if (txPolicy.isRollbackOnly()) return;

        // only call beforeCompletion on beans with session synchronization
        if (!synchronization.isCallSessionSynchronization()) continue;

        // Invoke beforeCompletion
        ThreadContext callContext =
            new ThreadContext(
                instance.beanContext, instance.primaryKey, Operation.BEFORE_COMPLETION);
        callContext.setCurrentAllowedStates(null);
        ThreadContext oldCallContext = ThreadContext.enter(callContext);
        try {
          instance.setInUse(true);

          BeanContext beanContext = instance.beanContext;
          List<InterceptorData> interceptors = beanContext.getCallbackInterceptors();
          InterceptorStack interceptorStack =
              new InterceptorStack(
                  instance.bean,
                  null,
                  Operation.BEFORE_COMPLETION,
                  interceptors,
                  instance.interceptors);
          interceptorStack.invoke();

          instance.setInUse(false);
        } catch (InvalidateReferenceException e) {
          // exception has alredy been handled
        } catch (Exception e) {
          String message =
              "An unexpected system exception occured while invoking the beforeCompletion method on the SessionSynchronization object";

          // [1] Log the exception or error
          logger.error(message, e);

          // [2] Mark the transaction for rollback.
          txPolicy.setRollbackOnly(e);

          // [3] Discard the instance
          discardInstance(callContext);

          // [4] throw the java.rmi.RemoteException to the client
          throw new RuntimeException(message, e);
        } finally {
          ThreadContext.exit(oldCallContext);
        }
      }
    }
  private Instance createInstance(ThreadContext callContext, BeanContext beanContext)
      throws ApplicationException {
    try {
      initializeDependencies(beanContext);

      final InstanceContext context = beanContext.newInstance();

      if (context.getBean() instanceof SessionBean) {

        final Operation originalOperation = callContext.getCurrentOperation();
        try {
          callContext.setCurrentOperation(Operation.CREATE);
          final Method create = beanContext.getCreateMethod();
          final InterceptorStack ejbCreate =
              new InterceptorStack(
                  context.getBean(),
                  create,
                  Operation.CREATE,
                  new ArrayList<InterceptorData>(),
                  new HashMap());
          ejbCreate.invoke();
        } finally {
          callContext.setCurrentOperation(originalOperation);
        }
      }

      ReadWriteLock lock;
      if (beanContext.isBeanManagedConcurrency()) {
        // Bean-Managed Concurrency
        lock = new BeanManagedLock();
      } else {
        // Container-Managed Concurrency
        lock = new ReentrantReadWriteLock();
      }

      return new Instance(
          context.getBean(), context.getInterceptors(), context.getCreationalContext(), lock);
    } catch (Throwable e) {
      if (e instanceof java.lang.reflect.InvocationTargetException) {
        e = ((java.lang.reflect.InvocationTargetException) e).getTargetException();
      }
      String t =
          "The bean instance " + beanContext.getDeploymentID() + " threw a system exception:" + e;
      logger.error(t, e);
      throw new ApplicationException(
          new NoSuchEJBException("Singleton failed to initialize").initCause(e));
    }
  }
    private void registerSessionSynchronization(
        Instance instance, BeanContext beanContext, Object primaryKey, boolean synchronize) {

      Synchronization synchronization = registry.get(primaryKey);

      if (synchronization == null) {
        synchronization = new Synchronization(instance);
        registry.put(primaryKey, synchronization);
      }

      boolean wasSynchronized = synchronization.setCallSessionSynchronization(synchronize);

      // check if afterBegin has already been invoked or if this is not a session synchronization
      // bean
      if (wasSynchronized || !synchronize) {
        return;
      }

      // Invoke afterBegin
      ThreadContext callContext =
          new ThreadContext(instance.beanContext, instance.primaryKey, Operation.AFTER_BEGIN);
      callContext.setCurrentAllowedStates(null);
      ThreadContext oldCallContext = ThreadContext.enter(callContext);
      try {

        List<InterceptorData> interceptors = beanContext.getCallbackInterceptors();
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean, null, Operation.AFTER_BEGIN, interceptors, instance.interceptors);
        interceptorStack.invoke();

      } catch (Exception e) {
        String message =
            "An unexpected system exception occured while invoking the afterBegin method on the SessionSynchronization object";

        // [1] Log the exception or error
        logger.error(message, e);

        // Caller handles transaction rollback and discardInstance

        // [4] throw the java.rmi.RemoteException to the client
        throw new RuntimeException(message, e);
      } finally {
        ThreadContext.exit(oldCallContext);
      }
    }
  private Object invokeWebService(
      Object[] args,
      BeanContext beanContext,
      Method runMethod,
      Instance instance,
      Object returnValue)
      throws Exception {
    if (args.length < 2) {
      throw new IllegalArgumentException(
          "WebService calls must follow format {messageContext, interceptor, [arg...]}.");
    }

    Object messageContext = args[0];

    // This object will be used as an interceptor in the stack and will be responsible
    // for unmarshalling the soap message parts into an argument list that will be
    // used for the actual method invocation.
    //
    // We just need to make it an interceptor in the OpenEJB sense and tack it on the end
    // of our stack.
    Object interceptor = args[1];

    //  Add the webservice interceptor to the list of interceptor instances
    Map<String, Object> interceptors = new HashMap<String, Object>(instance.interceptors);
    {
      interceptors.put(interceptor.getClass().getName(), interceptor);
    }

    //  Create an InterceptorData for the webservice interceptor to the list of interceptorDatas for
    // this method
    List<InterceptorData> interceptorDatas = new ArrayList<InterceptorData>();
    {
      InterceptorData providerData = new InterceptorData(interceptor.getClass());
      ClassFinder finder = new ClassFinder(interceptor.getClass());
      providerData.getAroundInvoke().addAll(finder.findAnnotatedMethods(AroundInvoke.class));
      //            interceptorDatas.add(providerData);
      interceptorDatas.add(0, providerData);
      interceptorDatas.addAll(beanContext.getMethodInterceptors(runMethod));
    }

    InterceptorStack interceptorStack =
        new InterceptorStack(
            instance.bean, runMethod, Operation.BUSINESS_WS, interceptorDatas, interceptors);
    Object[] params = new Object[runMethod.getParameterTypes().length];
    if (messageContext instanceof javax.xml.rpc.handler.MessageContext) {
      ThreadContext.getThreadContext()
          .set(
              javax.xml.rpc.handler.MessageContext.class,
              (javax.xml.rpc.handler.MessageContext) messageContext);
      returnValue =
          interceptorStack.invoke((javax.xml.rpc.handler.MessageContext) messageContext, params);
    } else if (messageContext instanceof javax.xml.ws.handler.MessageContext) {
      AddressingSupport wsaSupport = NoAddressingSupport.INSTANCE;
      for (int i = 2; i < args.length; i++) {
        if (args[i] instanceof AddressingSupport) {
          wsaSupport = (AddressingSupport) args[i];
        }
      }
      ThreadContext.getThreadContext().set(AddressingSupport.class, wsaSupport);
      ThreadContext.getThreadContext()
          .set(
              javax.xml.ws.handler.MessageContext.class,
              (javax.xml.ws.handler.MessageContext) messageContext);
      returnValue =
          interceptorStack.invoke((javax.xml.ws.handler.MessageContext) messageContext, params);
    }
    return returnValue;
  }
  protected Object _invoke(
      Method callMethod,
      Method runMethod,
      Object[] args,
      Instance instance,
      ThreadContext callContext,
      InterfaceType type)
      throws OpenEJBException {

    BeanContext beanContext = callContext.getBeanContext();

    TransactionPolicy txPolicy =
        createTransactionPolicy(beanContext.getTransactionType(callMethod, type), callContext);

    Object returnValue = null;
    try {
      if (type == InterfaceType.SERVICE_ENDPOINT) {
        callContext.setCurrentOperation(Operation.BUSINESS_WS);
        returnValue = invokeWebService(args, beanContext, runMethod, instance, returnValue);
      } else {
        List<InterceptorData> interceptors = beanContext.getMethodInterceptors(runMethod);
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean,
                runMethod,
                type == InterfaceType.TIMEOUT ? Operation.TIMEOUT : Operation.BUSINESS,
                interceptors,
                instance.interceptors);
        returnValue = interceptorStack.invoke(args);
      }
    } catch (Throwable re) { // handle reflection exception
      ExceptionType exceptionType = beanContext.getExceptionType(re);
      if (exceptionType == ExceptionType.SYSTEM) {
        /* System Exception ****************************/

        // The bean instance is not put into the pool via instanceManager.poolInstance
        // and therefore the instance will be garbage collected and destroyed.
        // In case of StrictPooling flag being set to true we also release the semaphore
        // in the discardInstance method of the instanceManager.
        callContext.setDiscardInstance(true);
        handleSystemException(txPolicy, re, callContext);
      } else {
        /* Application Exception ***********************/

        handleApplicationException(
            txPolicy, re, exceptionType == ExceptionType.APPLICATION_ROLLBACK);
      }
    } finally {
      try {
        afterInvoke(txPolicy, callContext);
      } catch (SystemException e) {
        callContext.setDiscardInstance(true);
        throw e;
      } catch (ApplicationException e) {
        throw e;
      } catch (RuntimeException e) {
        callContext.setDiscardInstance(true);
        throw e;
      }
    }

    return returnValue;
  }
  protected Object businessMethod(
      BeanContext beanContext,
      Object primKey,
      Class callInterface,
      Method callMethod,
      Object[] args,
      InterfaceType interfaceType)
      throws OpenEJBException {
    ThreadContext callContext = new ThreadContext(beanContext, primKey);
    ThreadContext oldCallContext = ThreadContext.enter(callContext);
    try {
      // Security check
      checkAuthorization(callMethod, interfaceType);

      // Start transaction
      TransactionPolicy txPolicy =
          createTransactionPolicy(
              callContext.getBeanContext().getTransactionType(callMethod, interfaceType),
              callContext);

      Object returnValue = null;
      Instance instance = null;
      try {
        // Obtain instance
        instance = obtainInstance(primKey, callContext);

        // Resume previous Bean transaction if there was one
        if (txPolicy instanceof BeanTransactionPolicy) {
          SuspendedTransaction suspendedTransaction = instance.getBeanTransaction();
          if (suspendedTransaction != null) {
            instance.setBeanTransaction(null);
            BeanTransactionPolicy beanTxEnv = (BeanTransactionPolicy) txPolicy;
            beanTxEnv.resumeUserTransaction(suspendedTransaction);
          }
        }

        // Register the entity managers
        registerEntityManagers(instance, callContext);
        // Register for synchronization callbacks
        registerSessionSynchronization(instance, callContext);

        // Setup for business invocation
        callContext.setCurrentOperation(Operation.BUSINESS);
        callContext.setCurrentAllowedStates(null);
        callContext.setInvokedInterface(callInterface);
        Method runMethod = beanContext.getMatchingBeanMethod(callMethod);
        callContext.set(Method.class, runMethod);

        // Initialize interceptor stack
        List<InterceptorData> interceptors = beanContext.getMethodInterceptors(runMethod);
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean, runMethod, Operation.BUSINESS, interceptors, instance.interceptors);

        // Invoke
        returnValue = interceptorStack.invoke(args);
      } catch (Throwable e) {
        handleException(callContext, txPolicy, e);
      } finally {
        // Commit transaction
        afterInvoke(callContext, txPolicy, instance);
      }
      return returnValue;
    } finally {
      ThreadContext.exit(oldCallContext);
    }
  }
  protected Object removeEJBObject(
      BeanContext beanContext,
      Object primKey,
      Class callInterface,
      Method callMethod,
      Object[] args,
      InterfaceType interfaceType)
      throws OpenEJBException {
    if (primKey == null) throw new NullPointerException("primKey is null");

    ThreadContext callContext = new ThreadContext(beanContext, primKey);
    ThreadContext oldCallContext = ThreadContext.enter(callContext);
    try {
      // Security check
      checkAuthorization(callMethod, interfaceType);

      // If a bean managed transaction is active, the bean can not be removed
      if (interfaceType.isComponent()) {
        Instance instance = checkedOutInstances.get(primKey);

        /**
         * According to EJB 3.0 "4.4.4 Restrictions for Transactions" any remove methods from home
         * or component interfaces must not be allowed if the bean instance is in a transaction.
         * Unfortunately, the Java EE 5 TCK has tests that ignore the restrictions in 4.4.4 and
         * expect beans in transactions can be removed via their home or component interface. The
         * test to see if the bean instance implements javax.ejb.SessionBean is a workaround for
         * passing the TCK while the tests in question can be challenged or the spec can be
         * changed/updated.
         */
        if (instance != null && instance.bean instanceof javax.ejb.SessionBean) {
          throw new ApplicationException(
              new RemoveException("A stateful EJB enrolled in a transaction can not be removed"));
        }
      }

      // Start transaction
      TransactionPolicy txPolicy =
          createTransactionPolicy(
              callContext.getBeanContext().getTransactionType(callMethod, interfaceType),
              callContext);

      Object returnValue = null;
      boolean retain = false;
      Instance instance = null;
      Method runMethod = null;
      try {
        // Obtain instance
        instance = obtainInstance(primKey, callContext);

        // Resume previous Bean transaction if there was one
        if (txPolicy instanceof BeanTransactionPolicy) {
          // Resume previous Bean transaction if there was one
          SuspendedTransaction suspendedTransaction = instance.getBeanTransaction();
          if (suspendedTransaction != null) {
            instance.setBeanTransaction(null);
            BeanTransactionPolicy beanTxEnv = (BeanTransactionPolicy) txPolicy;
            beanTxEnv.resumeUserTransaction(suspendedTransaction);
          }
        }

        // Register the entity managers
        registerEntityManagers(instance, callContext);

        // Register for synchronization callbacks
        registerSessionSynchronization(instance, callContext);

        // Setup for remove invocation
        callContext.setCurrentOperation(Operation.REMOVE);
        callContext.setCurrentAllowedStates(null);
        callContext.setInvokedInterface(callInterface);
        runMethod = beanContext.getMatchingBeanMethod(callMethod);
        callContext.set(Method.class, runMethod);

        // Do not pass arguments on home.remove(remote) calls
        Class<?> declaringClass = callMethod.getDeclaringClass();
        if (declaringClass.equals(EJBHome.class) || declaringClass.equals(EJBLocalHome.class)) {
          args = new Object[] {};
        }

        // Initialize interceptor stack
        List<InterceptorData> interceptors = beanContext.getMethodInterceptors(runMethod);
        InterceptorStack interceptorStack =
            new InterceptorStack(
                instance.bean, runMethod, Operation.REMOVE, interceptors, instance.interceptors);

        // Invoke
        if (args == null) {
          returnValue = interceptorStack.invoke();
        } else {
          returnValue = interceptorStack.invoke(args);
        }
      } catch (InvalidateReferenceException e) {
        throw e;
      } catch (Throwable e) {
        if (interfaceType.isBusiness()) {
          retain = beanContext.retainIfExeption(runMethod);
          handleException(callContext, txPolicy, e);
        } else {
          try {
            handleException(callContext, txPolicy, e);
          } catch (ApplicationException ae) {
            // Don't throw application exceptions for non-business interface removes
          }
        }
      } finally {
        if (!retain) {
          try {
            callContext.setCurrentOperation(Operation.PRE_DESTROY);
            List<InterceptorData> callbackInterceptors = beanContext.getCallbackInterceptors();
            InterceptorStack interceptorStack =
                new InterceptorStack(
                    instance.bean,
                    null,
                    Operation.PRE_DESTROY,
                    callbackInterceptors,
                    instance.interceptors);
            interceptorStack.invoke();
          } catch (Throwable callbackException) {
            String logMessage =
                "An unexpected exception occured while invoking the preDestroy method on the removed Stateful SessionBean instance; "
                    + callbackException.getClass().getName()
                    + " "
                    + callbackException.getMessage();

            /* [1] Log the exception or error */
            logger.error(logMessage);

          } finally {
            callContext.setCurrentOperation(Operation.REMOVE);
          }

          // todo destroy extended persistence contexts
          discardInstance(callContext);
        }

        // Commit transaction
        afterInvoke(callContext, txPolicy, instance);
      }

      return returnValue;
    } finally {
      ThreadContext.exit(oldCallContext);
    }
  }
  protected ProxyInfo createEJBObject(
      BeanContext beanContext, Method callMethod, Object[] args, InterfaceType interfaceType)
      throws OpenEJBException {
    // generate a new primary key
    Object primaryKey = newPrimaryKey();

    ThreadContext createContext = new ThreadContext(beanContext, primaryKey);
    ThreadContext oldCallContext = ThreadContext.enter(createContext);
    try {
      // Security check
      checkAuthorization(callMethod, interfaceType);

      // Create the extended entity managers for this instance
      Index<EntityManagerFactory, JtaEntityManagerRegistry.EntityManagerTracker> entityManagers =
          createEntityManagers(beanContext);

      // Register the newly created entity managers
      if (entityManagers != null) {
        try {
          entityManagerRegistry.addEntityManagers(
              (String) beanContext.getDeploymentID(), primaryKey, entityManagers);
        } catch (EntityManagerAlreadyRegisteredException e) {
          throw new EJBException(e);
        }
      }

      createContext.setCurrentOperation(Operation.CREATE);
      createContext.setCurrentAllowedStates(null);

      // Start transaction
      TransactionPolicy txPolicy =
          createTransactionPolicy(
              createContext.getBeanContext().getTransactionType(callMethod, interfaceType),
              createContext);

      Instance instance = null;
      try {
        // Create new instance

        try {
          final InstanceContext context = beanContext.newInstance();

          // Wrap-up everthing into a object
          instance =
              new Instance(
                  beanContext,
                  primaryKey,
                  context.getBean(),
                  context.getInterceptors(),
                  context.getCreationalContext(),
                  entityManagers);

        } catch (Throwable throwable) {
          ThreadContext callContext = ThreadContext.getThreadContext();
          handleSystemException(callContext.getTransactionPolicy(), throwable, callContext);
          throw new IllegalStateException(throwable); // should never be reached
        }

        // add to cache
        cache.add(primaryKey, instance);

        // instance starts checked-out
        checkedOutInstances.put(primaryKey, instance);

        // Register for synchronization callbacks
        registerSessionSynchronization(instance, createContext);

        // Invoke create for legacy beans
        if (!callMethod.getDeclaringClass().equals(BeanContext.BusinessLocalHome.class)
            && !callMethod.getDeclaringClass().equals(BeanContext.BusinessRemoteHome.class)
            && !callMethod.getDeclaringClass().equals(BeanContext.BusinessLocalBeanHome.class)) {

          // Setup for business invocation
          Method createOrInit = beanContext.getMatchingBeanMethod(callMethod);
          createContext.set(Method.class, createOrInit);

          // Initialize interceptor stack
          InterceptorStack interceptorStack =
              new InterceptorStack(
                  instance.bean,
                  createOrInit,
                  Operation.CREATE,
                  new ArrayList<InterceptorData>(),
                  new HashMap<String, Object>());

          // Invoke
          if (args == null) {
            interceptorStack.invoke();
          } else {
            interceptorStack.invoke(args);
          }
        }
      } catch (Throwable e) {
        handleException(createContext, txPolicy, e);
      } finally {
        afterInvoke(createContext, txPolicy, instance);
      }

      return new ProxyInfo(beanContext, primaryKey);
    } finally {
      ThreadContext.exit(oldCallContext);
    }
  }
  public void freeInstance(ThreadContext callContext) {
    BeanContext beanContext = callContext.getBeanContext();
    Data data = (Data) beanContext.getContainerData();
    Future<Instance> instanceFuture = data.singleton.get();

    // Possible the instance was never created
    if (instanceFuture == null) return;

    Instance instance;
    try {
      instance = instanceFuture.get();
    } catch (InterruptedException e) {
      Thread.interrupted();
      logger.error(
          "Singleton shutdown failed because the thread was interrupted: "
              + beanContext.getDeploymentID(),
          e);
      return;
    } catch (ExecutionException e) {
      // Instance was never initialized
      return;
    }

    try {
      callContext.setCurrentOperation(Operation.PRE_DESTROY);
      callContext.setCurrentAllowedStates(null);

      Method remove = instance.bean instanceof SessionBean ? beanContext.getCreateMethod() : null;

      List<InterceptorData> callbackInterceptors = beanContext.getCallbackInterceptors();
      InterceptorStack interceptorStack =
          new InterceptorStack(
              instance.bean,
              remove,
              Operation.PRE_DESTROY,
              callbackInterceptors,
              instance.interceptors);

      // Transaction Demarcation for Singleton PostConstruct method
      TransactionType transactionType;

      if (beanContext.getComponentType() == BeanType.SINGLETON) {
        Set<Method> callbacks =
            callbackInterceptors.get(callbackInterceptors.size() - 1).getPreDestroy();
        if (callbacks.isEmpty()) {
          transactionType = TransactionType.RequiresNew;
        } else {
          transactionType = beanContext.getTransactionType(callbacks.iterator().next());
          if (transactionType == TransactionType.Required) {
            transactionType = TransactionType.RequiresNew;
          }
        }
      } else {
        transactionType =
            beanContext.isBeanManagedTransaction()
                ? TransactionType.BeanManaged
                : TransactionType.NotSupported;
      }
      TransactionPolicy transactionPolicy =
          EjbTransactionUtil.createTransactionPolicy(transactionType, callContext);
      try {
        // Call the chain
        interceptorStack.invoke();
        if (instance.creationalContext != null) {
          instance.creationalContext.release();
        }
      } catch (Throwable e) {
        // RollBack Transaction
        EjbTransactionUtil.handleSystemException(transactionPolicy, e, callContext);
      } finally {
        EjbTransactionUtil.afterInvoke(transactionPolicy, callContext);
      }

    } catch (Throwable re) {
      logger.error("Singleton shutdown failed: " + beanContext.getDeploymentID(), re);
    }
  }
  protected Object _invoke(
      Method callMethod,
      Method runMethod,
      Object[] args,
      Instance instance,
      ThreadContext callContext,
      InterfaceType callType)
      throws OpenEJBException {
    BeanContext beanContext = callContext.getBeanContext();

    Duration accessTimeout = getAccessTimeout(beanContext, runMethod);
    boolean read = beanContext.getConcurrencyAttribute(runMethod) == javax.ejb.LockType.READ;

    final Lock lock = aquireLock(read, accessTimeout, instance, runMethod);

    Object returnValue;
    try {
      TransactionPolicy txPolicy =
          createTransactionPolicy(
              beanContext.getTransactionType(callMethod, callType), callContext);

      returnValue = null;
      try {
        if (callType == InterfaceType.SERVICE_ENDPOINT) {
          callContext.setCurrentOperation(Operation.BUSINESS_WS);
          returnValue = invokeWebService(args, beanContext, runMethod, instance);
        } else {
          List<InterceptorData> interceptors = beanContext.getMethodInterceptors(runMethod);
          InterceptorStack interceptorStack =
              new InterceptorStack(
                  instance.bean,
                  runMethod,
                  callType == InterfaceType.TIMEOUT ? Operation.TIMEOUT : Operation.BUSINESS,
                  interceptors,
                  instance.interceptors);
          returnValue = interceptorStack.invoke(args);
        }
      } catch (Throwable e) { // handle reflection exception
        ExceptionType type = beanContext.getExceptionType(e);
        if (type == ExceptionType.SYSTEM) {
          /* System Exception ****************************/

          // The bean instance is not put into the pool via instanceManager.poolInstance
          // and therefore the instance will be garbage collected and destroyed.
          // For this reason the discardInstance method of the StatelessInstanceManager
          // does nothing.
          handleSystemException(txPolicy, e, callContext);
        } else {
          /* Application Exception ***********************/

          handleApplicationException(txPolicy, e, type == ExceptionType.APPLICATION_ROLLBACK);
        }
      } finally {
        afterInvoke(txPolicy, callContext);
      }
    } finally {
      lock.unlock();
    }

    return returnValue;
  }