public Object invokeHome(Invocation mi) throws Exception {
    EntityEnterpriseContext ctx = (EntityEnterpriseContext) mi.getEnterpriseContext();
    Transaction tx = mi.getTransaction();

    Object rtn = getNext().invokeHome(mi);

    // An anonymous context was sent in, so if it has an id it is a real instance now
    if (ctx.getId() != null) {

      // it doesn't need to be read, but it might have been changed from the db already.
      ctx.setValid(true);

      if (tx != null) {
        BeanLock lock = container.getLockManager().getLock(ctx.getCacheKey());
        try {
          lock.schedule(mi);
          register(ctx, tx); // Set tx
          lock.endInvocation(mi);
        } finally {
          container.getLockManager().removeLockRef(lock.getId());
        }
      }
    }
    return rtn;
  }
 /** Do some validation of the incoming parameters */
 protected void validateArguments(Invocation mi) {
   if (mi.getType() == InvocationType.REMOTE) {
     Object[] params = mi.getArguments();
     for (int i = 0; i < params.length; i++) {
       Object obj = params[i];
       if (obj instanceof TimerHandle)
         throw new IllegalArgumentException("Cannot pass TimerHandle through remote interface");
     }
   }
 }
 /** Do some validation of the return value */
 protected Object validateReturnValue(Invocation mi, Object retValue) {
   if (mi.getType() == InvocationType.REMOTE) {
     if (retValue instanceof TimerHandle)
       throw new IllegalArgumentException("Cannot return TimerHandle from remote interface");
   }
   return retValue;
 }
  public Object invoke(Invocation mi) throws Exception {
    // We are going to work with the context a lot
    EntityEnterpriseContext ctx = (EntityEnterpriseContext) mi.getEnterpriseContext();

    // The Tx coming as part of the Method Invocation
    Transaction tx = mi.getTransaction();

    if (log.isTraceEnabled()) log.trace("invoke called for ctx " + ctx + ", tx=" + tx);

    if (!ctx.isValid()) {
      container.getPersistenceManager().loadEntity(ctx);
      ctx.setValid(true);
    }

    // mark the context as read only if this is a readonly method and the context
    // was not already readonly
    boolean didSetReadOnly = false;
    if (!ctx.isReadOnly()
        && (container.isReadOnly()
            || container.getBeanMetaData().isMethodReadOnly(mi.getMethod()))) {
      ctx.setReadOnly(true);
      didSetReadOnly = true;
    }

    // So we can go on with the invocation

    // Invocation with a running Transaction
    try {
      if (tx != null && tx.getStatus() != Status.STATUS_NO_TRANSACTION) {
        // readonly does not synchronize, lock or belong with transaction.
        boolean isReadOnly = container.isReadOnly();
        if (isReadOnly == false) {
          Method method = mi.getMethod();
          if (method != null)
            isReadOnly = container.getBeanMetaData().isMethodReadOnly(method.getName());
        }
        try {
          if (isReadOnly == false) {
            // register the wrapper with the transaction monitor (but only
            // register once). The transaction demarcation will trigger the
            // storage operations
            register(ctx, tx);
          }

          // Invoke down the chain
          Object retVal = getNext().invoke(mi);

          // Register again as a finder in the middle of a method
          // will de-register this entity, and then the rest of the method can
          // change fields which will never be stored
          if (isReadOnly == false) {
            // register the wrapper with the transaction monitor (but only
            // register once). The transaction demarcation will trigger the
            // storage operations
            register(ctx, tx);
          }

          // return the return value
          return retVal;
        } finally {
          // We were read-only and the context wasn't already synchronized, tidyup the cache
          if (isReadOnly && ctx.hasTxSynchronization() == false) {
            switch (commitOption) {
                // Keep instance active, but invalidate state
              case ConfigurationMetaData.B_COMMIT_OPTION:
                // Invalidate state (there might be other points of entry)
                ctx.setValid(false);
                break;

                // Invalidate everything AND Passivate instance
              case ConfigurationMetaData.C_COMMIT_OPTION:
                try {
                  // FIXME: We cannot passivate here, because previous
                  // interceptors work with the context, in particular
                  // the re-entrance interceptor is doing lock counting
                  // Just remove it from the cache
                  if (ctx.getId() != null) container.getInstanceCache().remove(ctx.getId());
                } catch (Exception e) {
                  log.debug("Exception releasing context", e);
                }
                break;
            }
          }
        }
      } else {
        // No tx
        try {
          Object result = getNext().invoke(mi);

          // Store after each invocation -- not on exception though, or removal
          // And skip reads too ("get" methods)
          if (ctx.getId() != null && !container.isReadOnly()) {
            container.invokeEjbStore(ctx);
            container.storeEntity(ctx);
          }

          return result;
        } catch (Exception e) {
          // Exception - force reload on next call
          ctx.setValid(false);
          throw e;
        } finally {
          switch (commitOption) {
              // Keep instance active, but invalidate state
            case ConfigurationMetaData.B_COMMIT_OPTION:
              // Invalidate state (there might be other points of entry)
              ctx.setValid(false);
              break;

              // Invalidate everything AND Passivate instance
            case ConfigurationMetaData.C_COMMIT_OPTION:
              try {
                // Do not call release if getId() is null.  This means that
                // the entity has been removed from cache.
                // release will schedule a passivation and this removed ctx
                // could be put back into the cache!
                // This is necessary because we have no lock, we
                // don't want to return an instance to the pool that is
                // being used
                if (ctx.getId() != null) container.getInstanceCache().remove(ctx.getId());
              } catch (Exception e) {
                log.debug("Exception releasing context", e);
              }
              break;
          }
        }
      }
    } finally {
      // if we marked the context as read only we need to reset it
      if (didSetReadOnly) {
        ctx.setReadOnly(false);
      }
    }
  }
  /**
   * Implementation of the server invoker handler interface. Will take the invocation request and
   * invoke down the interceptor chain.
   *
   * @param invocationReq
   * @return response of the invocation
   * @throws Throwable
   */
  public Object invoke(InvocationRequest invocationReq) throws Throwable {
    Invocation invocation = (Invocation) invocationReq.getParameter();
    Thread currentThread = Thread.currentThread();
    ClassLoader oldCl = currentThread.getContextClassLoader();
    ObjectName mbean = null;
    try {
      mbean = (ObjectName) Registry.lookup(invocation.getObjectName());

      /** Clustering * */
      long clientViewId = ((Long) invocation.getValue("CLUSTER_VIEW_ID")).longValue();
      HATarget target = (HATarget) beanMap.get(invocation.getObjectName());
      if (target == null) {
        // We could throw IllegalStateException but we have a race condition that could occur:
        // when we undeploy a bean, the cluster takes some time to converge
        // and to recalculate a new viewId and list of replicant for each HATarget.
        // Consequently, a client could own an up-to-date list of the replicants
        // (before the cluster has converged) and try to perform an invocation
        // on this node where the HATarget no more exist, thus receiving a
        // wrong exception and no failover is performed with an IllegalStateException
        //
        throw new GenericClusteringException(
            GenericClusteringException.COMPLETED_NO,
            "target is not/no more registered on this node");
      }

      if (!target.invocationsAllowed()) {
        throw new GenericClusteringException(
            GenericClusteringException.COMPLETED_NO,
            "invocations are currently not allowed on this target");
      }
      /** End Clustering * */

      // The cl on the thread should be set in another interceptor
      Object obj =
          getServer()
              .invoke(mbean, "invoke", new Object[] {invocation}, Invocation.INVOKE_SIGNATURE);

      /** Clustering * */
      HARMIResponse haResponse = new HARMIResponse();

      if (clientViewId != target.getCurrentViewId()) {
        haResponse.newReplicants = new ArrayList(target.getReplicants());
        haResponse.currentViewId = target.getCurrentViewId();
      }
      haResponse.response = obj;

      /** End Clustering * */
      return new MarshalledObject(haResponse);
    } catch (Exception e) {
      Throwable th = JMXExceptionDecoder.decode(e);
      if (log.isTraceEnabled()) {
        log.trace("Failed to invoke on mbean: " + mbean, th);
      }

      if (th instanceof Exception) {
        e = (Exception) th;
      }

      throw e;
    } finally {
      currentThread.setContextClassLoader(oldCl);
      Thread.interrupted(); // clear interruption because this thread may be pooled.
    }
  }