protected Object invalidateAcrossCluster(
      boolean synchronous,
      InvocationContext ctx,
      Object[] keys,
      boolean useFuture,
      final Object retvalForFuture)
      throws Throwable {
    if (!isLocalModeForced(ctx)) {
      // increment invalidations counter if statistics maintained
      incrementInvalidations();
      final InvalidateCommand command = commandsFactory.buildInvalidateCommand(keys);
      if (log.isDebugEnabled())
        log.debug("Cache [" + rpcManager.getTransport().getAddress() + "] replicating " + command);
      // voila, invalidated!
      if (useFuture) {
        NotifyingNotifiableFuture<Object> future = new NotifyingFutureImpl(retvalForFuture);
        rpcManager.broadcastRpcCommandInFuture(command, future);
        return future;
      } else {
        rpcManager.broadcastRpcCommand(command, synchronous, false);
      }
    }

    return retvalForFuture;
  }
 private Cache getCache(String regionName, String typeKey, Properties properties) {
   TypeOverrides regionOverride = typeOverrides.get(regionName);
   if (!definedConfigurations.contains(regionName)) {
     String templateCacheName = null;
     Configuration regionCacheCfg = null;
     if (regionOverride != null) {
       if (log.isDebugEnabled())
         log.debug("Cache region specific configuration exists: " + regionOverride);
       regionOverride = overrideStatisticsIfPresent(regionOverride, properties);
       regionCacheCfg = regionOverride.createInfinispanConfiguration();
       String cacheName = regionOverride.getCacheName();
       if (cacheName != null) // Region specific override with a given cache name
       templateCacheName = cacheName;
       else // Region specific override without cache name, so template cache name is generic for
            // data type.
       templateCacheName = typeOverrides.get(typeKey).getCacheName();
     } else {
       // No region specific overrides, template cache name is generic for data type.
       templateCacheName = typeOverrides.get(typeKey).getCacheName();
       regionCacheCfg = typeOverrides.get(typeKey).createInfinispanConfiguration();
     }
     // Configure transaction manager
     regionCacheCfg = configureTransactionManager(regionCacheCfg, templateCacheName, properties);
     // Apply overrides
     manager.defineConfiguration(regionName, templateCacheName, regionCacheCfg);
     definedConfigurations.add(regionName);
   }
   Cache cache = manager.getCache(regionName);
   if (!cache.getStatus().allowInvocations()) {
     cache.start();
   }
   return createCacheWrapper(cache.getAdvancedCache());
 }
  private void invalidateAcrossCluster(boolean synchronous, Object[] keys, InvocationContext ctx)
      throws Throwable {
    // increment invalidations counter if statistics maintained
    incrementInvalidations();
    final InvalidateCommand invalidateCommand =
        commandsFactory.buildInvalidateCommand(InfinispanCollections.<Flag>emptySet(), keys);
    if (log.isDebugEnabled())
      log.debug("Cache [" + rpcManager.getAddress() + "] replicating " + invalidateCommand);

    ReplicableCommand command = invalidateCommand;
    if (ctx.isInTxScope()) {
      TxInvocationContext txCtx = (TxInvocationContext) ctx;
      // A Prepare command containing the invalidation command in its 'modifications' list is sent
      // to the remote nodes
      // so that the invalidation is executed in the same transaction and locks can be acquired and
      // released properly.
      // This is 1PC on purpose, as an optimisation, even if the current TX is 2PC.
      // If the cache uses 2PC it's possible that the remotes will commit the invalidation and the
      // originator rolls back,
      // but this does not impact consistency and the speed benefit is worth it.
      command =
          commandsFactory.buildPrepareCommand(
              txCtx.getGlobalTransaction(),
              Collections.<WriteCommand>singletonList(invalidateCommand),
              true);
    }
    rpcManager.invokeRemotely(null, command, rpcManager.getDefaultRpcOptions(synchronous));
  }
  private void shutDownGracefully() {
    if (log.isDebugEnabled())
      log.debugf(
          "Wait for on-going transactions to finish for %s.",
          Util.prettyPrintTime(
              configuration.transaction().cacheStopTimeout(), TimeUnit.MILLISECONDS));
    long failTime = currentMillisFromNanotime() + configuration.transaction().cacheStopTimeout();
    boolean txsOnGoing = areTxsOnGoing();
    while (txsOnGoing && currentMillisFromNanotime() < failTime) {
      try {
        Thread.sleep(30);
        txsOnGoing = areTxsOnGoing();
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        if (clustered) {
          log.debugf(
              "Interrupted waiting for on-going transactions to finish. %s local transactions and %s remote transactions",
              localTransactions.size(), remoteTransactions.size());
        } else {
          log.debugf(
              "Interrupted waiting for %s on-going transactions to finish.",
              localTransactions.size());
        }
      }
    }

    if (txsOnGoing) {
      log.unfinishedTransactionsRemain(
          localTransactions == null ? 0 : localTransactions.size(),
          remoteTransactions == null ? 0 : remoteTransactions.size());
    } else {
      log.debug("All transactions terminated");
    }
  }
Beispiel #5
0
  void stop(EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) {
    if (log.isDebugEnabled())
      log.debugf("Stopping cache %s on %s", getName(), getCacheManager().getAddress());

    // Create invocation context to pass flags
    createNonTxInvocationContext(explicitFlags, explicitClassLoader);
    componentRegistry.stop();
  }
Beispiel #6
0
 @ManagedOperation(description = "Starts the cache.")
 @Operation(displayName = "Starts cache.")
 public void start() {
   componentRegistry.start();
   defaultLifespan = config.getExpirationLifespan();
   defaultMaxIdleTime = config.getExpirationMaxIdle();
   if (log.isDebugEnabled())
     log.debugf("Started cache %s on %s", getName(), getCacheManager().getAddress());
 }
 /** {@inheritDoc} */
 public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties)
     throws CacheException {
   if (log.isDebugEnabled()) log.debug("Building timestamps cache region [" + regionName + "]");
   Cache cache = getCache(regionName, TIMESTAMPS_KEY, properties);
   CacheAdapter cacheAdapter = CacheAdapterImpl.newInstance(cache);
   TimestampsRegionImpl region = createTimestampsRegion(cacheAdapter, regionName);
   region.start();
   return region;
 }
    @Override
    @SuppressWarnings("unchecked")
    public synchronized void futureDone(Future<Object> objectFuture) {
      SenderContainer sc = futures.get(objectFuture);
      if (sc.processed) {
        // This can happen - it is a race condition in JGroups' NotifyingFuture.setListener() where
        // a listener
        // could be notified twice.
        if (trace)
          log.tracef(
              "Not processing callback; already processed callback for sender %s", sc.address);
      } else {
        sc.processed = true;
        Address sender = sc.address;
        boolean done = false;
        try {
          if (retval == null) {
            Object response = objectFuture.get();
            if (trace) log.tracef("Received response: %s from %s", response, sender);
            filter.isAcceptable(response, sender);
            if (!filter.needMoreResponses()) {
              retval = new RspList(Collections.singleton(new Rsp(sender, response)));
              done = true;
              // TODO cancel other tasks?
            }
          } else {
            if (trace)
              log.tracef(
                  "Skipping response from %s since a valid response for this request has already been received",
                  sender);
          }
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
        } catch (ExecutionException e) {
          Throwable cause = e.getCause();
          if (cause instanceof org.jgroups.SuspectedException) {
            // Do not set the exception field, RpcException should be thrown if there is no other
            // valid response
            return;
          } else if (cause instanceof org.jgroups.TimeoutException) {
            exception = new TimeoutException("Timeout!", e);
          } else if (cause instanceof Exception) {
            exception = (Exception) cause;
          } else {
            exception = new CacheException("Caught a throwable", cause);
          }

          if (log.isDebugEnabled())
            log.debugf("Caught exception from sender %s: %s", sender, exception);
        } finally {
          expectedResponses--;
          if (expectedResponses == 0 || done) {
            this.notify(); // make sure to awake waiting thread, but avoid unnecessary wakeups!
          }
        }
      }
    }
 /** {@inheritDoc} */
 public EntityRegion buildEntityRegion(
     String regionName, Properties properties, CacheDataDescription metadata)
     throws CacheException {
   if (log.isDebugEnabled()) log.debug("Building entity cache region [" + regionName + "]");
   Cache cache = getCache(regionName, ENTITY_KEY, properties);
   CacheAdapter cacheAdapter = CacheAdapterImpl.newInstance(cache);
   EntityRegionImpl region =
       new EntityRegionImpl(cacheAdapter, regionName, metadata, transactionManager, this);
   region.start();
   return region;
 }
 private static void removeInMemoryData(Cache cache) {
   EmbeddedCacheManager mgr = cache.getCacheManager();
   Address a = mgr.getAddress();
   String str;
   if (a == null) str = "a non-clustered cache manager";
   else str = "a cache manager at address " + a;
   log.debugf("Cleaning data for cache '%s' on %s", cache.getName(), str);
   DataContainer dataContainer = TestingUtil.extractComponent(cache, DataContainer.class);
   if (log.isDebugEnabled())
     log.debugf("removeInMemoryData(): dataContainerBefore == %s", dataContainer.entrySet(null));
   dataContainer.clear();
   if (log.isDebugEnabled())
     log.debugf("removeInMemoryData(): dataContainerAfter == %s", dataContainer.entrySet(null));
   L1GMUContainer l1GMUContainer = TestingUtil.extractComponent(cache, L1GMUContainer.class);
   if (log.isDebugEnabled())
     log.debugf(
         "removeInMemoryData(): l1GMUContainerBefore == %s", l1GMUContainer.chainToString());
   l1GMUContainer.clear();
   if (log.isDebugEnabled())
     log.debugf("removeInMemoryData(): l1GMUContainerAfter == %s", l1GMUContainer.chainToString());
 }
  private void updateTransactionVersion(InvocationContext context) {
    if (!context.isInTxScope() && !context.isOriginLocal()) {
      return;
    }

    if (context instanceof SingleKeyNonTxInvocationContext) {
      if (log.isDebugEnabled()) {
        log.debugf(
            "Received a SingleKeyNonTxInvocationContext... This should be a single read operation");
      }
      return;
    }

    TxInvocationContext txInvocationContext = (TxInvocationContext) context;
    List<EntryVersion> entryVersionList = new LinkedList<EntryVersion>();
    entryVersionList.add(txInvocationContext.getTransactionVersion());

    if (log.isTraceEnabled()) {
      log.tracef(
          "[%s] Keys read in this command: %s",
          txInvocationContext.getGlobalTransaction().prettyPrint(),
          txInvocationContext.getKeysReadInCommand());
    }

    for (InternalGMUCacheEntry internalGMUCacheEntry :
        txInvocationContext.getKeysReadInCommand().values()) {
      Object key = internalGMUCacheEntry.getKey();
      boolean local = cll.localNodeIsOwner(key);
      if (log.isTraceEnabled()) {
        log.tracef(
            "[%s] Analyze entry [%s]: local?=%s",
            txInvocationContext.getGlobalTransaction().prettyPrint(), internalGMUCacheEntry, local);
      }
      if (txInvocationContext.hasModifications() && !internalGMUCacheEntry.isMostRecent()) {
        throw new CacheException("Read-Write transaction read an old value and should rollback");
      }

      if (internalGMUCacheEntry.getMaximumTransactionVersion() != null) {
        entryVersionList.add(internalGMUCacheEntry.getMaximumTransactionVersion());
      }
      txInvocationContext.getCacheTransaction().addReadKey(key);
      if (local) {
        txInvocationContext.setAlreadyReadOnThisNode(true);
        txInvocationContext.addReadFrom(cll.getAddress());
      }
    }

    if (entryVersionList.size() > 1) {
      EntryVersion[] txVersionArray = new EntryVersion[entryVersionList.size()];
      txInvocationContext.setTransactionVersion(
          versionGenerator.mergeAndMax(entryVersionList.toArray(txVersionArray)));
    }
  }
  /**
   * Returns a named sub-element of the current element passed in.
   *
   * <p>None of the parameters should be null - otherwise the method may throw a
   * NullPointerException.
   *
   * @param element - element to search through.
   * @param subElementName - the name of a sub element to look for
   * @return the first matching sub element, if found, or null otherwise.
   */
  public static Element getSubElement(Element element, String subElementName) {
    NodeList nl = element.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (node.getNodeType() == Node.ELEMENT_NODE
          && subElementName.equals(((Element) node).getTagName())) {
        return (Element) node;
      }
    }

    if (log.isDebugEnabled()) log.debugf("getSubElement(): Does not exist for %s", subElementName);
    return null;
  }
Beispiel #13
0
  final void putForExternalRead(
      K key, V value, EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) {
    Transaction ongoingTransaction = null;
    try {
      ongoingTransaction = getOngoingTransaction();
      if (ongoingTransaction != null) transactionManager.suspend();

      EnumSet<Flag> flags =
          EnumSet.of(
              FAIL_SILENTLY,
              FORCE_ASYNCHRONOUS,
              ZERO_LOCK_ACQUISITION_TIMEOUT,
              PUT_FOR_EXTERNAL_READ);
      if (explicitFlags != null && !explicitFlags.isEmpty()) {
        flags.addAll(explicitFlags);
      }

      // if the entry exists then this should be a no-op.
      putIfAbsent(
          key,
          value,
          defaultLifespan,
          TimeUnit.MILLISECONDS,
          defaultMaxIdleTime,
          TimeUnit.MILLISECONDS,
          flags,
          explicitClassLoader);
    } catch (Exception e) {
      if (log.isDebugEnabled()) log.debug("Caught exception while doing putForExternalRead()", e);
    } finally {
      try {
        if (ongoingTransaction != null) transactionManager.resume(ongoingTransaction);
      } catch (Exception e) {
        if (log.isDebugEnabled())
          log.debug("Had problems trying to resume a transaction after putForExternalRead()", e);
      }
    }
  }
  /** {@inheritDoc} */
  public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
      throws CacheException {
    if (log.isDebugEnabled()) log.debug("Building query results cache region [" + regionName + "]");
    String cacheName = typeOverrides.get(QUERY_KEY).getCacheName();
    // If region name is not default one, lookup a cache for that region name
    if (!regionName.equals("org.hibernate.cache.internal.StandardQueryCache"))
      cacheName = regionName;

    Cache cache = getCache(cacheName, QUERY_KEY, properties);
    CacheAdapter cacheAdapter = CacheAdapterImpl.newInstance(cache);
    QueryResultsRegionImpl region =
        new QueryResultsRegionImpl(cacheAdapter, regionName, properties, transactionManager, this);
    region.start();
    return region;
  }
 @Override
 public void endStateTransfer(String sendingSite) {
   if (debug) {
     log.debugf("Ending state transfer from %s", sendingSite);
   }
   String currentSendingSite = this.sendingSite.get();
   if (sendingSite == null || sendingSite.equals(currentSendingSite)) {
     this.sendingSite.set(null);
     commitManager.stopTrack(PUT_FOR_X_SITE_STATE_TRANSFER);
   } else {
     if (log.isDebugEnabled()) {
       log.debugf(
           "Received an end request from a non-sender site. Expects %s but got %s",
           currentSendingSite, sendingSite);
     }
   }
 }
  /**
   * returns the request object list for the {@code member}
   *
   * @param member the destination member
   * @return the request object list. It can be empty if no requests are necessary
   */
  public final synchronized ObjectRequest getObjectRequestForAddress(Address member) {
    int addressIndex = clusterSnapshot.indexOf(member);

    if (addressIndex == -1) {
      log.warnf(
          "Trying to get Object Requests to send to %s but it does not exists in cluster snapshot %s",
          member, clusterSnapshot);
      return new ObjectRequest(null, null);
    }

    ObjectRequest request = accessesByPrimaryOwner[addressIndex].toObjectRequest();

    if (log.isInfoEnabled()) {
      log.debugf(
          "Getting request list for %s. Request is %s",
          member, request.toString(log.isDebugEnabled()));
    }

    return request;
  }
  /**
   * validates the read set and returns the prepare version from the commit queue
   *
   * @param ctx the context
   * @param command the prepare command
   * @throws InterruptedException if interrupted
   */
  protected void performValidation(TxInvocationContext ctx, GMUPrepareCommand command)
      throws InterruptedException {
    boolean hasToUpdateLocalKeys = hasLocalKeysToUpdate(command.getModifications());
    boolean isReadOnly = command.getModifications().length == 0;

    if (!isReadOnly) {
      cll.performReadSetValidation(ctx, command);
      if (hasToUpdateLocalKeys) {
        transactionCommitManager.prepareTransaction(ctx.getCacheTransaction());
      } else {
        transactionCommitManager.prepareReadOnlyTransaction(ctx.getCacheTransaction());
      }
    }

    if (log.isDebugEnabled()) {
      log.debugf(
          "Transaction %s can commit on this node. Prepare Version is %s",
          command.getGlobalTransaction().prettyPrint(), ctx.getTransactionVersion());
    }
  }
    @Override
    public void handleUnmarshallingException(Throwable problem, Class<?> subjectClass) {
      if (log.isDebugEnabled()) {
        StringBuilder builder = new StringBuilder();
        ClassLoader cl = subjectClass.getClassLoader();
        builder.append("classloader hierarchy:");
        ClassLoader parent = cl;
        while (parent != null) {
          if (parent.equals(cl)) {
            builder.append("\n\t\t-> type classloader = ").append(parent);
          } else {
            builder.append("\n\t\t-> parent classloader = ").append(parent);
          }
          URL[] urls = getClassLoaderURLs(parent);

          if (urls != null) {
            for (URL u : urls) builder.append("\n\t\t->...").append(u);
          }

          parent = parent.getParent();
        }
        TraceInformation.addUserInformation(problem, builder.toString());
      }
    }
/**
 * @author Mircea Markus
 * @since 5.2
 */
public class BackupReceiverImpl implements BackupReceiver {

  private static final Log log = LogFactory.getLog(BackupReceiverImpl.class);
  private static final boolean trace = log.isDebugEnabled();
  private final Cache<Object, Object> cache;

  private final BackupCacheUpdater siteUpdater;

  public BackupReceiverImpl(Cache<Object, Object> cache) {
    this.cache = cache;
    siteUpdater = new BackupCacheUpdater(cache);
  }

  @Override
  public Cache getCache() {
    return cache;
  }

  @Override
  public Object handleRemoteCommand(VisitableCommand command) throws Throwable {
    return command.acceptVisitor(null, siteUpdater);
  }

  @Override
  public void handleStateTransferControl(XSiteStateTransferControlCommand command)
      throws Exception {
    XSiteStateTransferControlCommand invokeCommand = command;
    if (!command.getCacheName().equals(cache.getName())) {
      // copy if the cache name is different
      invokeCommand = command.copyForCache(cache.getName());
    }
    invokeCommand.setSiteName(command.getOriginSite());
    invokeRemotelyInLocalSite(invokeCommand);
  }

  @Override
  public void handleStateTransferState(XSiteStatePushCommand cmd) throws Exception {
    // split the state and forward it to the primary owners...
    if (!cache.getStatus().allowInvocations()) {
      throw new CacheException("Cache is stopping or terminated: " + cache.getStatus());
    }
    final ClusteringDependentLogic clusteringDependentLogic =
        cache
            .getAdvancedCache()
            .getComponentRegistry()
            .getComponent(ClusteringDependentLogic.class);
    final Map<Address, List<XSiteState>> primaryOwnersChunks = new HashMap<>();

    if (trace) {
      log.tracef("Received X-Site state transfer '%s'. Splitting by primary owner.", cmd);
    }

    for (XSiteState state : cmd.getChunk()) {
      final Address primaryOwner = clusteringDependentLogic.getPrimaryOwner(state.key());
      List<XSiteState> primaryOwnerList = primaryOwnersChunks.get(primaryOwner);
      if (primaryOwnerList == null) {
        primaryOwnerList = new LinkedList<>();
        primaryOwnersChunks.put(primaryOwner, primaryOwnerList);
      }
      primaryOwnerList.add(state);
    }

    final List<XSiteState> localChunks =
        primaryOwnersChunks.remove(clusteringDependentLogic.getAddress());
    final List<StatePushTask> tasks = new ArrayList<>(primaryOwnersChunks.size());

    for (Map.Entry<Address, List<XSiteState>> entry : primaryOwnersChunks.entrySet()) {
      if (entry.getValue() == null || entry.getValue().isEmpty()) {
        continue;
      }
      if (trace) {
        log.tracef("Node '%s' will apply %s", entry.getKey(), entry.getValue());
      }
      StatePushTask task = new StatePushTask(entry.getValue(), entry.getKey(), cache);
      tasks.add(task);
      task.executeRemote();
    }

    // help gc. this is safe because the chunks was already sent
    primaryOwnersChunks.clear();

    if (trace) {
      log.tracef(
          "Local node '%s' will apply %s",
          cache.getAdvancedCache().getRpcManager().getAddress(), localChunks);
    }

    if (localChunks != null) {
      LocalInvocation.newInstanceFromCache(cache, newStatePushCommand(cache, localChunks)).call();
      // help gc
      localChunks.clear();
    }

    if (trace) {
      log.tracef("Waiting for the remote tasks...");
    }

    while (!tasks.isEmpty()) {
      for (Iterator<StatePushTask> iterator = tasks.iterator(); iterator.hasNext(); ) {
        awaitRemoteTask(cache, iterator.next());
        iterator.remove();
      }
    }
    // the put operation can fail silently. check in the end and it is better to resend the chunk
    // than to lose keys.
    if (!cache.getStatus().allowInvocations()) {
      throw new CacheException("Cache is stopping or terminated: " + cache.getStatus());
    }
  }

  private static void awaitRemoteTask(Cache<?, ?> cache, StatePushTask task) throws Exception {
    try {
      if (trace) {
        log.tracef("Waiting reply from %s", task.address);
      }
      Map<Address, Response> responseMap = task.awaitRemote();
      if (trace) {
        log.tracef("Response received is %s", responseMap);
      }
      if (responseMap.size() > 1 || !responseMap.containsKey(task.address)) {
        throw new IllegalStateException("Shouldn't happen. Map is " + responseMap);
      }
      Response response = responseMap.get(task.address);
      if (response == CacheNotFoundResponse.INSTANCE) {
        if (trace) {
          log.tracef("Cache not found in node '%s'. Retrying locally!", task.address);
        }
        if (!cache.getStatus().allowInvocations()) {
          throw new CacheException("Cache is stopping or terminated: " + cache.getStatus());
        }
        task.executeLocal();
      }
    } catch (Exception e) {
      if (!cache.getStatus().allowInvocations()) {
        throw new CacheException("Cache is stopping or terminated: " + cache.getStatus());
      }
      if (cache.getAdvancedCache().getRpcManager().getMembers().contains(task.address)) {
        if (trace) {
          log.tracef(e, "An exception was sent by %s. Retrying!", task.address);
        }
        task.executeRemote(); // retry!
      } else {
        if (trace) {
          log.tracef(e, "An exception was sent by %s. Retrying locally!", task.address);
        }
        // if the node left the cluster, we apply the missing state. This avoids the site provider
        // to re-send the
        // full chunk.
        task.executeLocal();
      }
    }
  }

  private static XSiteStatePushCommand newStatePushCommand(
      Cache<?, ?> cache, List<XSiteState> stateList) {
    CommandsFactory commandsFactory =
        cache.getAdvancedCache().getComponentRegistry().getCommandsFactory();
    return commandsFactory.buildXSiteStatePushCommand(
        stateList.toArray(new XSiteState[stateList.size()]));
  }

  private Map<Address, Response> invokeRemotelyInLocalSite(CacheRpcCommand command)
      throws Exception {
    final RpcManager rpcManager = cache.getAdvancedCache().getRpcManager();
    final NotifyingNotifiableFuture<Map<Address, Response>> remoteFuture =
        new NotifyingFutureImpl<>();
    final Map<Address, Response> responseMap = new HashMap<>();
    rpcManager.invokeRemotelyInFuture(
        remoteFuture, null, command, rpcManager.getDefaultRpcOptions(true, false));
    responseMap.put(
        rpcManager.getAddress(), LocalInvocation.newInstanceFromCache(cache, command).call());
    //noinspection unchecked
    responseMap.putAll(remoteFuture.get());
    return responseMap;
  }

  public static final class BackupCacheUpdater extends AbstractVisitor {

    private static Log log = LogFactory.getLog(BackupCacheUpdater.class);

    private final ConcurrentMap<GlobalTransaction, GlobalTransaction> remote2localTx;

    private final AdvancedCache<Object, Object> backupCache;

    BackupCacheUpdater(Cache<Object, Object> backup) {
      // ignore return values on the backup
      this.backupCache =
          backup.getAdvancedCache().withFlags(Flag.IGNORE_RETURN_VALUES, Flag.SKIP_XSITE_BACKUP);
      this.remote2localTx = new ConcurrentHashMap<>();
    }

    @Override
    public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command)
        throws Throwable {
      log.tracef("Processing a remote put %s", command);
      if (command.isConditional()) {
        return backupCache.putIfAbsent(command.getKey(), command.getValue(), command.getMetadata());
      }
      return backupCache.put(command.getKey(), command.getValue(), command.getMetadata());
    }

    @Override
    public Object visitRemoveCommand(InvocationContext ctx, RemoveCommand command)
        throws Throwable {
      if (command.isConditional()) {
        return backupCache.remove(command.getKey(), command.getValue());
      }
      return backupCache.remove(command.getKey());
    }

    @Override
    public Object visitReplaceCommand(InvocationContext ctx, ReplaceCommand command)
        throws Throwable {
      if (command.isConditional() && command.getOldValue() != null) {
        return backupCache.replace(
            command.getKey(), command.getOldValue(), command.getNewValue(), command.getMetadata());
      }
      return backupCache.replace(command.getKey(), command.getNewValue(), command.getMetadata());
    }

    @Override
    public Object visitPutMapCommand(InvocationContext ctx, PutMapCommand command)
        throws Throwable {
      Metadata metadata = command.getMetadata();
      backupCache.putAll(
          command.getMap(),
          metadata.lifespan(),
          TimeUnit.MILLISECONDS,
          metadata.maxIdle(),
          TimeUnit.MILLISECONDS);
      return null;
    }

    @Override
    public Object visitClearCommand(InvocationContext ctx, ClearCommand command) throws Throwable {
      backupCache.clear();
      return null;
    }

    @Override
    public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command)
        throws Throwable {
      boolean isTransactional = isTransactional();
      if (isTransactional) {

        // Sanity check -- if the remote tx doesn't have modifications, it never should have been
        // propagated!
        if (!command.hasModifications()) {
          throw new IllegalStateException("TxInvocationContext has no modifications!");
        }

        replayModificationsInTransaction(command, command.isOnePhaseCommit());
      } else {
        replayModifications(command);
      }
      return null;
    }

    private boolean isTransactional() {
      return backupCache.getCacheConfiguration().transaction().transactionMode()
          == TransactionMode.TRANSACTIONAL;
    }

    @Override
    public Object visitCommitCommand(TxInvocationContext ctx, CommitCommand command)
        throws Throwable {
      if (!isTransactional()) {
        log.cannotRespondToCommit(command.getGlobalTransaction(), backupCache.getName());
      } else {
        log.tracef("Committing remote transaction %s", command.getGlobalTransaction());
        completeTransaction(command.getGlobalTransaction(), true);
      }
      return null;
    }

    @Override
    public Object visitRollbackCommand(TxInvocationContext ctx, RollbackCommand command)
        throws Throwable {

      if (!isTransactional()) {
        log.cannotRespondToRollback(command.getGlobalTransaction(), backupCache.getName());
      } else {
        log.tracef("Rolling back remote transaction %s", command.getGlobalTransaction());
        completeTransaction(command.getGlobalTransaction(), false);
      }
      return null;
    }

    private void completeTransaction(GlobalTransaction globalTransaction, boolean commit)
        throws Throwable {
      TransactionTable txTable = txTable();
      GlobalTransaction localTxId = remote2localTx.remove(globalTransaction);
      if (localTxId == null) {
        throw new CacheException(
            "Couldn't find a local transaction corresponding to remote transaction "
                + globalTransaction);
      }
      LocalTransaction localTx = txTable.getLocalTransaction(localTxId);
      if (localTx == null) {
        throw new IllegalStateException("Local tx not found but present in the tx table!");
      }
      TransactionManager txManager = txManager();
      txManager.resume(localTx.getTransaction());
      if (commit) {
        txManager.commit();
      } else {
        txManager.rollback();
      }
    }

    private void replayModificationsInTransaction(PrepareCommand command, boolean onePhaseCommit)
        throws Throwable {
      TransactionManager tm = txManager();
      boolean replaySuccessful = false;
      try {

        tm.begin();
        replayModifications(command);
        replaySuccessful = true;
      } finally {
        LocalTransaction localTx = txTable().getLocalTransaction(tm.getTransaction());
        if (localTx
            != null) { // possible for the tx to be null if we got an exception during applying
                       // modifications
          localTx.setFromRemoteSite(true);

          if (onePhaseCommit) {
            if (replaySuccessful) {
              log.tracef(
                  "Committing remotely originated tx %s as it is 1PC",
                  command.getGlobalTransaction());
              tm.commit();
            } else {
              log.tracef("Rolling back remotely originated tx %s", command.getGlobalTransaction());
              tm.rollback();
            }
          } else { // Wait for a remote commit/rollback.
            remote2localTx.put(command.getGlobalTransaction(), localTx.getGlobalTransaction());
            tm.suspend();
          }
        }
      }
    }

    private TransactionManager txManager() {
      return backupCache.getAdvancedCache().getTransactionManager();
    }

    public TransactionTable txTable() {
      return backupCache.getComponentRegistry().getComponent(TransactionTable.class);
    }

    private void replayModifications(PrepareCommand command) throws Throwable {
      for (WriteCommand c : command.getModifications()) {
        c.acceptVisitor(null, this);
      }
    }
  }

  private static class StatePushTask {
    private final List<XSiteState> chunk;
    private final Address address;
    private final Cache<?, ?> cache;
    private volatile Future<Map<Address, Response>> remoteFuture;

    private StatePushTask(List<XSiteState> chunk, Address address, Cache<?, ?> cache) {
      this.chunk = chunk;
      this.address = address;
      this.cache = cache;
    }

    public void executeRemote() {
      final RpcManager rpcManager = cache.getAdvancedCache().getRpcManager();
      NotifyingNotifiableFuture<Map<Address, Response>> future = new NotifyingFutureImpl<>();
      remoteFuture = future;
      rpcManager.invokeRemotelyInFuture(
          future,
          Collections.singletonList(address),
          newStatePushCommand(cache, chunk),
          rpcManager.getDefaultRpcOptions(true));
    }

    public Response executeLocal() throws Exception {
      return LocalInvocation.newInstanceFromCache(cache, newStatePushCommand(cache, chunk)).call();
    }

    public Map<Address, Response> awaitRemote() throws Exception {
      Future<Map<Address, Response>> future = remoteFuture;
      if (future == null) {
        throw new NullPointerException("Should not happen!");
      }
      return future.get();
    }
  }
}
/**
 * It contains the logic needed to consume the state sent from other site.
 *
 * @author Pedro Ruivo
 * @since 7.0
 */
public class XSiteStateConsumerImpl implements XSiteStateConsumer {

  private static final EnumSet<Flag> STATE_TRANSFER_PUT_FLAGS =
      EnumSet.of(
          PUT_FOR_X_SITE_STATE_TRANSFER,
          IGNORE_RETURN_VALUES,
          SKIP_REMOTE_LOOKUP,
          SKIP_XSITE_BACKUP);
  private static final Log log = LogFactory.getLog(XSiteStateConsumerImpl.class);
  private static final boolean trace = log.isTraceEnabled();
  private static final boolean debug = log.isDebugEnabled();

  private TransactionManager transactionManager;
  private InvocationContextFactory invocationContextFactory;
  private CommandsFactory commandsFactory;
  private InterceptorChain interceptorChain;
  private CommitManager commitManager;
  private AtomicReference<String> sendingSite = new AtomicReference<>(null);

  @Inject
  public void inject(
      TransactionManager transactionManager,
      InvocationContextFactory invocationContextFactory,
      CommandsFactory commandsFactory,
      InterceptorChain interceptorChain,
      CommitManager commitManager) {
    this.transactionManager = transactionManager;
    this.invocationContextFactory = invocationContextFactory;
    this.commandsFactory = commandsFactory;
    this.interceptorChain = interceptorChain;
    this.commitManager = commitManager;
  }

  @Override
  public void startStateTransfer(String sendingSite) {
    if (debug) {
      log.debugf("Starting state transfer. Receiving from %s", sendingSite);
    }
    if (this.sendingSite.compareAndSet(null, sendingSite)) {
      commitManager.startTrack(Flag.PUT_FOR_X_SITE_STATE_TRANSFER);
    } else {
      throw new CacheException("Already receiving state from " + this.sendingSite.get());
    }
  }

  @Override
  public void endStateTransfer(String sendingSite) {
    if (debug) {
      log.debugf("Ending state transfer from %s", sendingSite);
    }
    String currentSendingSite = this.sendingSite.get();
    if (sendingSite == null || sendingSite.equals(currentSendingSite)) {
      this.sendingSite.set(null);
      commitManager.stopTrack(PUT_FOR_X_SITE_STATE_TRANSFER);
    } else {
      if (log.isDebugEnabled()) {
        log.debugf(
            "Received an end request from a non-sender site. Expects %s but got %s",
            currentSendingSite, sendingSite);
      }
    }
  }

  @Override
  public void applyState(XSiteState[] chunk) throws Exception {
    if (debug) {
      log.debugf("Received state: %s keys", chunk.length);
    }
    if (transactionManager != null) {
      applyStateInTransaction(chunk);
    } else {
      applyStateInNonTransaction(chunk);
    }
  }

  @Override
  public String getSendingSiteName() {
    return sendingSite.get();
  }

  private void applyStateInTransaction(XSiteState[] chunk) throws Exception {
    try {
      transactionManager.begin();
      InvocationContext ctx =
          invocationContextFactory.createInvocationContext(
              transactionManager.getTransaction(), true);
      ((TxInvocationContext) ctx)
          .getCacheTransaction()
          .setStateTransferFlag(PUT_FOR_X_SITE_STATE_TRANSFER);
      for (XSiteState siteState : chunk) {
        interceptorChain.invoke(ctx, createPut(siteState));
        if (trace) {
          log.tracef("Successfully applied key'%s'", siteState);
        }
      }
      transactionManager.commit();
      if (debug) {
        log.debugf("Successfully applied state. %s keys inserted", chunk.length);
      }
    } catch (Exception e) {
      log.unableToApplyXSiteState(e);
      safeRollback();
      throw e;
    }
  }

  private void applyStateInNonTransaction(XSiteState[] chunk) {
    SingleKeyNonTxInvocationContext ctx =
        (SingleKeyNonTxInvocationContext)
            invocationContextFactory.createSingleKeyNonTxInvocationContext();

    for (XSiteState siteState : chunk) {
      PutKeyValueCommand command = createPut(siteState);
      ctx.setLockOwner(command.getLockOwner());
      interceptorChain.invoke(ctx, command);
      ctx.resetState(); // re-use same context. Old context is not longer needed
      if (trace) {
        log.tracef("Successfully applied key'%s'", siteState);
      }
    }
    if (debug) {
      log.debugf("Successfully applied state. %s keys inserted", chunk.length);
    }
  }

  private PutKeyValueCommand createPut(XSiteState state) {
    return commandsFactory.buildPutKeyValueCommand(
        state.key(), state.value(), state.metadata(), STATE_TRANSFER_PUT_FLAGS);
  }

  private void safeRollback() {
    try {
      transactionManager.rollback();
    } catch (Exception e) {
      // ignored!
      if (debug) {
        log.debug("Error rollbacking transaction.", e);
      }
    }
  }
}
 /** Try to figure out which TransactionManager to use */
 private static void doLookups() {
   if (lookupFailed) return;
   InitialContext ctx;
   try {
     ctx = new InitialContext();
   } catch (NamingException e) {
     log.error("Failed creating initial JNDI context", e);
     lookupFailed = true;
     return;
   }
   // probe jndi lookups first
   for (String[] knownJNDIManager : knownJNDIManagers) {
     Object jndiObject;
     try {
       if (log.isDebugEnabled())
         log.debug("Trying to lookup TransactionManager for " + knownJNDIManager[1]);
       jndiObject = ctx.lookup(knownJNDIManager[0]);
     } catch (NamingException e) {
       log.debug(
           "Failed to perform a lookup for ["
               + knownJNDIManager[0]
               + " ("
               + knownJNDIManager[1]
               + ")]");
       continue;
     }
     if (jndiObject instanceof TransactionManager) {
       tm = (TransactionManager) jndiObject;
       log.debug("Found TransactionManager for " + knownJNDIManager[1]);
       return;
     }
   }
   // try to find websphere lookups since we came here
   Class clazz;
   try {
     log.debug("Trying WebSphere 5.1: " + WS_FACTORY_CLASS_5_1);
     clazz = Util.loadClassStrict(WS_FACTORY_CLASS_5_1);
     log.debug("Found WebSphere 5.1: " + WS_FACTORY_CLASS_5_1);
   } catch (ClassNotFoundException ex) {
     try {
       log.debug("Trying WebSphere 5.0: " + WS_FACTORY_CLASS_5_0);
       clazz = Util.loadClassStrict(WS_FACTORY_CLASS_5_0);
       log.debug("Found WebSphere 5.0: " + WS_FACTORY_CLASS_5_0);
     } catch (ClassNotFoundException ex2) {
       try {
         log.debug("Trying WebSphere 4: " + WS_FACTORY_CLASS_4);
         clazz = Util.loadClassStrict(WS_FACTORY_CLASS_4);
         log.debug("Found WebSphere 4: " + WS_FACTORY_CLASS_4);
       } catch (ClassNotFoundException ex3) {
         log.debug(
             "Couldn't find any WebSphere TransactionManager factory class, neither for WebSphere version 5.1 nor 5.0 nor 4");
         lookupFailed = true;
         return;
       }
     }
   }
   try {
     Class[] signature = null;
     Object[] args = null;
     Method method = clazz.getMethod("getTransactionManager", signature);
     tm = (TransactionManager) method.invoke(null, args);
   } catch (Exception ex) {
     log.error(
         "Found WebSphere TransactionManager factory class ["
             + clazz.getName()
             + "], but couldn't invoke its static 'getTransactionManager' method",
         ex);
   }
 }
 @Override
 public void handleMarshallingException(Throwable problem, Object subject) {
   if (log.isDebugEnabled()) {
     TraceInformation.addUserInformation(problem, "toString = " + subject.toString());
   }
 }