All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.infinispan.registry.impl.ClusterRegistryImpl Maven / Gradle / Ivy

There is a newer version: 9.1.7.Final
Show newest version
package org.infinispan.registry.impl;

import net.jcip.annotations.ThreadSafe;

import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.commons.CacheException;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.context.Flag;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.annotations.Stop;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.filter.KeyFilter;
import org.infinispan.registry.ClusterRegistry;
import org.infinispan.registry.InternalCacheRegistry;
import org.infinispan.registry.ScopedKey;
import org.infinispan.remoting.transport.jgroups.SuspectException;
import org.infinispan.transaction.TransactionMode;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;

import javax.transaction.InvalidTransactionException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Default implementation of the ClusterRegistry. Stores all the information in a replicated cache that is lazily
 * instantiated on the first access. This means that if the EmbeddedCacheManager doesn't use the metadata the
 * underlying cache never gets instantiated.
 *
 * @author Mircea Markus
 * @since 6.0
 * @deprecated functionality replaced by {@link InternalCacheRegistry}
 */
@ThreadSafe
@Deprecated
public class ClusterRegistryImpl implements ClusterRegistry {

   public static final String GLOBAL_REGISTRY_CACHE_NAME = "__cluster_registry_cache__";

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

   private EmbeddedCacheManager cacheManager;
   private InternalCacheRegistry internalCacheRegistry;
   private volatile AdvancedCache, V> clusterRegistryCache;
   private volatile TransactionManager transactionManager;

   @Inject
   public void init(EmbeddedCacheManager cacheManager, InternalCacheRegistry internalCacheRegistry) {
      this.cacheManager = cacheManager;
      this.internalCacheRegistry = internalCacheRegistry;
   }

   @Stop(priority=1)
   public void stop() {
      clusterRegistryCache = null;
   }

   protected void runCommand(Runnable runnable) {
      while (true) {
         try {
            runnable.run();
            break;
         } catch (CacheException e) {
            if (SuspectException.isSuspectExceptionInChain(e)) {
               // Retry the command
               log.trace("Ignoring suspect exception and retrying operation for ClusterRegistry.");
            } else {
               throw e;
            }
         }
      }
   }

   @Override
   public void put(final S scope, final K key, final V value) {
      if (value == null) throw new IllegalArgumentException("Null values are not allowed");
      startRegistryCache();
      Transaction tx = suspendTx();
      try {
         runCommand(new Runnable() {

            @Override
            public void run() {
               clusterRegistryCache.put(new ScopedKey(scope, key), value);
            }
         });
      } finally {
         resumeTx(tx);
      }
   }

   @Override
   public void put(final S scope, final K key, final V value, final long lifespan, final TimeUnit unit) {
      if (value == null) throw new IllegalArgumentException("Null values are not allowed");
      startRegistryCache();
      Transaction tx = suspendTx();
      try {
         runCommand(new Runnable() {

            @Override
            public void run() {
               clusterRegistryCache.put(new ScopedKey(scope, key), value, lifespan, unit);
            }
         });
      } finally {
         resumeTx(tx);
      }
   }

   @Override
   public void remove(final S scope, final K key) {
      startRegistryCache();
      Transaction tx = suspendTx();
      try {
         runCommand(new Runnable() {

            @Override
            public void run() {
               clusterRegistryCache.remove(new ScopedKey(scope, key));
            }
         });
      } finally {
         resumeTx(tx);
      }
   }

   @Override
   public V get(S scope, K key) {
      startRegistryCache();
      // We don't want repeatable read semantics for the cluster registry, so we need to suspend for reads as well
      Transaction tx = suspendTx();
      try {
         return clusterRegistryCache.get(new ScopedKey(scope, key));
      } finally {
         resumeTx(tx);
      }
   }

   @Override
   public boolean containsKey(S scope, K key) {
      startRegistryCache();
      Transaction tx = suspendTx();
      try {
         return clusterRegistryCache.containsKey(new ScopedKey(scope, key));
      } finally {
         resumeTx(tx);
      }
   }

   @Override
   public Set keys(S scope) {
      startRegistryCache();
      Set result = new HashSet();
      Transaction tx = suspendTx();
      try {
         for (ScopedKey key : clusterRegistryCache.keySet()) {
            if (key.hasScope(scope)) {
               result.add(key.getKey());
            }
         }
         return result;
      } finally {
         resumeTx(tx);
      }
   }

   @Override
   public void clear(S scope) {
      startRegistryCache();
      Transaction tx = suspendTx();
      try {
         for (final ScopedKey key : clusterRegistryCache.keySet()) {
            if (key.hasScope(scope)) {
               runCommand(new Runnable() {

                  @Override
                  public void run() {
                     clusterRegistryCache.remove(key);
                  }
               });
            }
         }
      } finally {
         resumeTx(tx);
      }
   }

   @Override
   public void clearAll() {
      startRegistryCache();
      Transaction tx = suspendTx();
      try {
         runCommand(new Runnable() {

            @Override
            public void run() {
               clusterRegistryCache.clear();
            }
         });
      } finally {
         resumeTx(tx);
      }
   }

   @Override
   public void addListener(final S scope, final Object listener) {
      startRegistryCache();
      clusterRegistryCache.addListener(listener, new KeyFilter>() {
         @Override
         public boolean accept(ScopedKey key) {
            return key.hasScope(scope);
         }
      });
   }

   @Override
   public void addListener(final S scope, final KeyFilter keyFilter, final Object listener) {
      startRegistryCache();
      clusterRegistryCache.addListener(listener, new KeyFilter>() {
         @Override
         public boolean accept(ScopedKey key) {
            return key.hasScope(scope) && keyFilter.accept(key.getKey());
         }
      });
   }

   @Override
   public void removeListener(Object listener) {
      if (clusterRegistryCache != null) {
         clusterRegistryCache.removeListener(listener);
      }
   }

   /**
    * Start the cache lazily: if the cluster registry is not needed at all then this won't ever be started.
    */
   private void startRegistryCache() {
      if (clusterRegistryCache == null) {
         synchronized (this) {
            if (clusterRegistryCache != null) return;
            internalCacheRegistry.registerInternalCache(GLOBAL_REGISTRY_CACHE_NAME, getRegistryCacheConfig());
            Cache, V> clusterRegistryCache = SecurityActions.getRegistryCache(cacheManager);
            this.clusterRegistryCache = clusterRegistryCache.getAdvancedCache().withFlags(Flag.IGNORE_RETURN_VALUES);
            transactionManager = this.clusterRegistryCache.getTransactionManager();
         }
      }
   }

   private Configuration getRegistryCacheConfig() {
      ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();

      //allow the registry to work for local caches as well as clustered caches
      CacheMode cacheMode = isClustered() ? CacheMode.REPL_SYNC : CacheMode.LOCAL;
      configurationBuilder.clustering().cacheMode(cacheMode);

      // use invocation batching (cache-only transactions) for high consistency as writes are expected to be rare in this cache
      configurationBuilder.transaction().transactionMode(TransactionMode.TRANSACTIONAL)
            .transactionManagerLookup(null).invocationBatching().enable();

      //fetch the state (redundant as state transfer this is enabled by default, keep it here to document the intention)
      configurationBuilder.clustering().stateTransfer().fetchInMemoryState(cacheMode.needsStateTransfer());

      configurationBuilder.security().authorization().disable();
      return configurationBuilder.build();
   }

   private boolean isClustered() {
      return cacheManager.getGlobalComponentRegistry().getGlobalConfiguration().isClustered();
   }

   /**
    * Suspend any ongoing transaction, so that the cluster registry writes are committed immediately.
    */
   private Transaction suspendTx() {
      try {
         if (transactionManager == null) {
            return null;
         }
         return transactionManager.suspend();
      } catch (SystemException e) {
         throw new CacheException("Unable to suspend ongoing transaction", e);
      }
   }

   private void resumeTx(Transaction tx) {
      try {
         if (tx != null) {
            transactionManager.resume(tx);
         }
      } catch (InvalidTransactionException | SystemException e) {
         throw new CacheException("Unable to resume ongoing transaction", e);
      }
   }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy