/**
   * This starts the components in the cache, connecting to channels, starting service threads, etc.
   * If the cache is not in the {@link org.infinispan.lifecycle.ComponentStatus#INITIALIZING} state,
   * it will be initialized first.
   */
  public void start() {

    if (!state.startAllowed()) {
      if (state.needToDestroyFailedCache()) destroy(); // this will take us back to TERMINATED

      if (state.needToInitializeBeforeStart()) {
        rewire();
      } else return;
    }

    state = ComponentStatus.INITIALIZING;
    try {
      internalStart();
    } catch (Throwable t) {
      handleLifecycleTransitionFailure(t);
    }
  }
  /**
   * Blocks until the current cache instance is in its {@link
   * org.infinispan.lifecycle.ComponentStatus#RUNNING started} phase. Blocks for up to {@link
   * Configuration#getStateRetrievalTimeout()} milliseconds, throwing an IllegalStateException if
   * the cache doesn't reach this state even after this maximum wait time.
   *
   * @throws InterruptedException if interrupted while waiting
   * @throws IllegalStateException if even after waiting the cache has not started.
   */
  private void blockUntilCacheStarts() throws InterruptedException, IllegalStateException {
    int pollFrequencyMS = 100;
    long startupWaitTime = getConfiguration().getStateRetrievalTimeout();
    long giveUpTime = System.currentTimeMillis() + startupWaitTime;

    while (System.currentTimeMillis() < giveUpTime) {
      if (state.allowInvocations()) break;
      Thread.sleep(pollFrequencyMS);
    }

    // check if we have started.
    if (!state.allowInvocations())
      throw new IllegalStateException(
          "Cache not in STARTED state, even after waiting "
              + getConfiguration().getStateRetrievalTimeout()
              + " millis.");
  }
  /**
   * Stops the cache and sets the cache status to {@link
   * org.infinispan.lifecycle.ComponentStatus#TERMINATED} once it is done. If the cache is not in
   * the {@link org.infinispan.lifecycle.ComponentStatus#RUNNING} state, this is a no-op.
   */
  public void stop() {
    if (!state.stopAllowed()) {
      return;
    }

    // Trying to stop() from FAILED is valid, but may not work
    boolean failed = state == ComponentStatus.FAILED;

    try {
      internalStop();
    } catch (Throwable t) {
      if (failed) {
        getLog().failedToCallStopAfterFailure(t);
      }
      failed = true;
      handleLifecycleTransitionFailure(t);
    } finally {
      if (!failed) state = ComponentStatus.TERMINATED;
    }
  }
  /**
   * Asserts whether invocations are allowed on the cache or not. Returns <tt>true</tt> if
   * invocations are to be allowed, <tt>false</tt> otherwise. If the origin of the call is remote
   * and the cache status is {@link org.infinispan.lifecycle.ComponentStatus#INITIALIZING}, this
   * method will block for up to {@link Configuration#getStateRetrievalTimeout()} millis, checking
   * for a valid state.
   *
   * @param originLocal true if the call originates locally (i.e., from the {@link
   *     org.infinispan.CacheImpl} or false if it originates remotely, i.e., from the {@link
   *     org.infinispan.remoting.InboundInvocationHandler}.
   * @return true if invocations are allowed, false otherwise.
   */
  public boolean invocationsAllowed(boolean originLocal) {
    getLog().trace("Testing if invocations are allowed.");
    if (state.allowInvocations()) return true;

    // if this is a locally originating call and the cache is not in a valid state, return false.
    if (originLocal) return false;

    getLog().trace("Is remotely originating.");

    // else if this is a remote call and the status is STARTING, wait until the cache starts.
    if (state == ComponentStatus.INITIALIZING) {
      getLog().trace("Cache is initializing; block.");
      try {
        blockUntilCacheStarts();
        return true;
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
    } else {
      getLog().cacheNotStarted();
    }
    return false;
  }
 /**
  * If the cache is STOPPING, non-transaction invocations, or transactional invocations for
  * transaction others than the ongoing ones, are no allowed. This method returns true if under
  * this circumstances meet. Otherwise, it returns false.
  */
 private boolean stoppingAndNotAllowed(ComponentStatus status, InvocationContext ctx)
     throws Exception {
   return status.isStopping() && (!ctx.isInTxScope() || !isOngoingTransaction(ctx));
 }
  private Object handleAll(InvocationContext ctx, VisitableCommand command) throws Throwable {
    boolean suppressExceptions = false;
    try {
      ComponentStatus status = componentRegistry.getStatus();
      if (command.ignoreCommandOnStatus(status)) {
        log.debugf("Status: %s : Ignoring %s command", status, command);
        return null;
      }

      if (status.isTerminated()) {
        throw new IllegalStateException(
            String.format(
                "%s is in 'TERMINATED' state and so it does not accept new invocations. "
                    + "Either restart it or recreate the cache container.",
                getCacheNamePrefix()));
      } else if (stoppingAndNotAllowed(status, ctx)) {
        throw new IllegalStateException(
            String.format(
                "%s is in 'STOPPING' state and this is an invocation not belonging to an on-going transaction, so it does not accept new invocations. "
                    + "Either restart it or recreate the cache container.",
                getCacheNamePrefix()));
      }

      LogFactory.pushNDC(componentRegistry.getCacheName(), trace);

      try {
        if (trace) log.tracef("Invoked with command %s and InvocationContext [%s]", command, ctx);
        if (ctx == null) throw new IllegalStateException("Null context not allowed!!");

        if (ctx.hasFlag(Flag.FAIL_SILENTLY)) {
          suppressExceptions = true;
        }

        try {
          return invokeNextInterceptor(ctx, command);
        } catch (Throwable th) {
          // If we are shutting down there is every possibility that the invocation fails.
          suppressExceptions = suppressExceptions || shuttingDown;
          if (suppressExceptions) {
            if (shuttingDown)
              log.trace(
                  "Exception while executing code, but we're shutting down so failing silently.");
            else log.trace("Exception while executing code, failing silently...", th);
            return null;
          } else {
            // HACK: There are way too many StateTransferInProgressExceptions on remote nodes during
            // state transfer
            // and they not really exceptional (the originator will retry the command)
            boolean logAsError =
                !(th instanceof StateTransferInProgressException && !ctx.isOriginLocal());
            if (logAsError) {
              log.executionError(th);
            } else {
              log.trace("Exception while executing code", th);
            }
            if (ctx.isInTxScope() && ctx.isOriginLocal()) {
              if (trace) log.trace("Transaction marked for rollback as exception was received.");
              markTxForRollbackAndRethrow(ctx, th);
              throw new IllegalStateException("This should not be reached");
            }
            throw th;
          }
        } finally {
          ctx.reset();
        }
      } finally {
        LogFactory.popNDC(trace);
      }
    } finally {
      invocationContextContainer.clearThreadLocal();
    }
  }
  private Object handleAll(InvocationContext ctx, VisitableCommand command) throws Throwable {
    try {
      ComponentStatus status = componentRegistry.getStatus();
      if (command.ignoreCommandOnStatus(status)) {
        log.debugf("Status: %s : Ignoring %s command", status, command);
        return null;
      }

      if (status.isTerminated()) {
        throw log.cacheIsTerminated(getCacheNamePrefix());
      } else if (stoppingAndNotAllowed(status, ctx)) {
        throw log.cacheIsStopping(getCacheNamePrefix());
      }

      LogFactory.pushNDC(componentRegistry.getCacheName(), trace);

      invocationContextContainer.setThreadLocal(ctx);
      try {
        if (trace) log.tracef("Invoked with command %s and InvocationContext [%s]", command, ctx);
        if (ctx == null) throw new IllegalStateException("Null context not allowed!!");

        try {
          return invokeNextInterceptor(ctx, command);
        } catch (InvalidCacheUsageException ex) {
          throw ex; // Propagate back client usage errors regardless of flag
        } catch (Throwable th) {
          // Only check for fail silently if there's a failure :)
          boolean suppressExceptions =
              (command instanceof FlagAffectedCommand)
                  && ((FlagAffectedCommand) command).hasFlag(Flag.FAIL_SILENTLY);
          // If we are shutting down there is every possibility that the invocation fails.
          suppressExceptions = suppressExceptions || shuttingDown;
          if (suppressExceptions) {
            if (shuttingDown)
              log.trace(
                  "Exception while executing code, but we're shutting down so failing silently.",
                  th);
            else log.trace("Exception while executing code, failing silently...", th);
            return null;
          } else {
            if (th instanceof WriteSkewException) {
              // We log this as DEBUG rather than ERROR - see ISPN-2076
              log.debug("Exception executing call", th);
            } else if (th instanceof OutdatedTopologyException) {
              log.outdatedTopology(th);
            } else {
              Collection<Object> affectedKeys = extractWrittenKeys(ctx, command);
              log.executionError(command.getClass().getSimpleName(), affectedKeys, th);
            }
            if (ctx.isInTxScope() && ctx.isOriginLocal()) {
              if (trace) log.trace("Transaction marked for rollback as exception was received.");
              markTxForRollbackAndRethrow(ctx, th);
              throw new IllegalStateException("This should not be reached");
            }
            throw th;
          }
        }
      } finally {
        LogFactory.popNDC(trace);
      }
    } finally {
      invocationContextContainer.clearThreadLocal();
    }
  }