<%#
 Copyright 2013-2021 the original author or authors from the JHipster project.

 This file is part of the JHipster project, see https://www.jhipster.tech/
 for more information.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      https://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-%>
package <%= packageName %>.config;

<%_ if (cacheProviderEhCache) { _%>
import java.time.Duration;

import org.ehcache.config.builders.*;
import org.ehcache.jsr107.Eh107Configuration;

  <%_ if (enableHibernateCache) { _%>
import org.hibernate.cache.jcache.ConfigSettings;
  <%_ } _%>
import tech.jhipster.config.JHipsterProperties;

<%_ } _%>
<%_ if (cacheProviderCaffeine) { _%>
import com.github.benmanes.caffeine.jcache.configuration.CaffeineConfiguration;
import java.util.OptionalLong;
import java.util.concurrent.TimeUnit;

  <%_ if (enableHibernateCache) { _%>
import org.hibernate.cache.jcache.ConfigSettings;
  <%_ } _%>
import tech.jhipster.config.JHipsterProperties;

<%_ } _%>
<%_ if (cacheProviderHazelcast) { _%>
import tech.jhipster.config.JHipsterConstants;
import tech.jhipster.config.JHipsterProperties;

import com.hazelcast.config.*;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.Hazelcast;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

<%_ } _%>
<%_ if (cacheProviderEhCache || cacheProviderCaffeine) { _%>
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
  <%_ if (enableHibernateCache) { _%>
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
  <%_ } _%>
<%_ } _%>
<%_ if (cacheProviderHazelcast) { _%>
  <%_ if (serviceDiscoveryAny) { _%>
import org.springframework.boot.autoconfigure.web.ServerProperties;
  <%_ } _%>

import org.springframework.cache.CacheManager;

<%_ } _%>
<%_ if (cacheProviderMemcached) { _%>
  <%_ if (!skipUserManagement || (authenticationTypeOauth2 && !databaseTypeNo)) { _%>
import <%= packageName %>.repository.UserRepository;
  <%_ } _%>
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.CacheManager;
<%_ } _%>
<%_ if (!cacheProviderNo) { _%>
import org.springframework.boot.info.BuildProperties;
import org.springframework.boot.info.GitProperties;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import tech.jhipster.config.cache.PrefixedKeyGenerator;
<%_ } _%>
import org.springframework.cache.annotation.EnableCaching;
<%_ if (!cacheProviderMemcached) { _%>
  <%_ if (serviceDiscoveryAny) { _%>
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.serviceregistry.Registration;
  <%_ } _%>
<%_ } _%>
<%_ if (cacheProviderMemcached) { _%>
import org.springframework.cache.support.NoOpCacheManager;
<%_ } _%>
import org.springframework.context.annotation.*;
<%_ if (cacheProviderHazelcast) { _%>
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;

import javax.annotation.PreDestroy;
<%_ } _%>
<%_ if (cacheProviderInfinispan) { _%>
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.jboss.marshalling.core.JBossUserMarshaller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.jhipster.config.JHipsterProperties;
import java.util.concurrent.TimeUnit;
import org.hibernate.cfg.AvailableSettings;
import org.infinispan.spring.starter.embedded.InfinispanCacheConfigurer;
import org.infinispan.spring.starter.embedded.InfinispanGlobalConfigurer;
import org.infinispan.transaction.TransactionMode;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import java.util.stream.Stream;
  <%_ if (serviceDiscoveryAny) { _%>
import org.infinispan.remoting.transport.jgroups.JGroupsTransport;
import org.jgroups.Channel;
import org.jgroups.JChannel;
import org.jgroups.PhysicalAddress;
import org.jgroups.protocols.*;
import org.jgroups.protocols.pbcast.GMS;
import org.jgroups.protocols.pbcast.NAKACK2;
import org.jgroups.protocols.pbcast.STABLE;
import org.jgroups.stack.IpAddress;
import org.jgroups.stack.ProtocolStack;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import org.springframework.beans.factory.BeanInitializationException;
import java.util.ArrayList;
import java.util.List;
  <%_ } _%>
<%_ } _%>
<%_ if (cacheProviderMemcached) { _%>
import java.util.HashMap;
import java.util.Map;
import java.net.InetSocketAddress;
import net.rubyeye.xmemcached.auth.AuthInfo;
import net.rubyeye.xmemcached.utils.AddrUtil;
import com.google.code.ssm.providers.xmemcached.XMemcachedConfiguration;
import com.google.code.ssm.CacheFactory;
import com.google.code.ssm.config.DefaultAddressProvider;
import com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl;
import com.google.code.ssm.spring.SSMCache;
import com.google.code.ssm.spring.SSMCacheManager;

import tech.jhipster.config.JHipsterProperties;
<%_ } _%>
<%_ if (cacheProviderRedis) { _%>
import java.net.URI;
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.SingleServerConfig;
import org.redisson.jcache.configuration.RedissonConfiguration;
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
  <%_ if (enableHibernateCache) { _%>
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.hibernate.cache.jcache.ConfigSettings;
  <%_ } _%>

import java.util.concurrent.TimeUnit;

import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;

import tech.jhipster.config.JHipsterProperties;
<%_ } _%>

@Configuration
@EnableCaching
public class CacheConfiguration {
<%_ if (!cacheProviderNo) { _%>
    private GitProperties gitProperties;
    private BuildProperties buildProperties;
<%_ } _%>
<%_ if (cacheProviderEhCache || cacheProviderCaffeine) { _%>
    private final javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration;

    public CacheConfiguration(JHipsterProperties jHipsterProperties) {
  <%_ if (cacheProviderEhCache) { _%>
        JHipsterProperties.Cache.Ehcache ehcache = jHipsterProperties.getCache().getEhcache();

        jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration(
            CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class,
                ResourcePoolsBuilder.heap(ehcache.getMaxEntries()))
                .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ehcache.getTimeToLiveSeconds())))
                .build());
  <%_ } else { _%>
        JHipsterProperties.Cache.Caffeine caffeine = jHipsterProperties.getCache().getCaffeine();

        CaffeineConfiguration<Object, Object> caffeineConfiguration = new CaffeineConfiguration<>();
        caffeineConfiguration.setMaximumSize(OptionalLong.of(caffeine.getMaxEntries()));
        caffeineConfiguration.setExpireAfterWrite(OptionalLong.of(TimeUnit.SECONDS.toNanos(caffeine.getTimeToLiveSeconds())));
        caffeineConfiguration.setStatisticsEnabled(true);
        jcacheConfiguration = caffeineConfiguration;
  <%_ } _%>
    }

  <%_ if (enableHibernateCache) { _%>
    @Bean
    public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(javax.cache.CacheManager cacheManager) {
        return hibernateProperties -> hibernateProperties.put(ConfigSettings.CACHE_MANAGER, cacheManager);
    }
  <%_ } _%>

    @Bean
    public JCacheManagerCustomizer cacheManagerCustomizer() {
        return cm -> {
  <%_ if (authenticationTypeOauth2 && applicationTypeMicroservice) { _%>
            createCache(cm, "oAuth2Authentication");
  <%_ } _%>
  <%_ if (!skipUserManagement || (authenticationTypeOauth2 && !databaseTypeNo)) { _%>
            createCache(cm, <%= packageName %>.repository.UserRepository.USERS_BY_LOGIN_CACHE);
            createCache(cm, <%= packageName %>.repository.UserRepository.USERS_BY_EMAIL_CACHE);
    <%_ if (enableHibernateCache) { _%>
            createCache(cm, <%= packageName %>.domain.<%= asEntity('User') %>.class.getName());
            createCache(cm, <%= packageName %>.domain.Authority.class.getName());
            createCache(cm, <%= packageName %>.domain.<%= asEntity('User') %>.class.getName() + ".authorities");
      <%_ if (authenticationTypeSession) { _%>
            createCache(cm, <%= packageName %>.domain.PersistentToken.class.getName());
            createCache(cm, <%= packageName %>.domain.<%= asEntity('User') %>.class.getName() + ".persistentTokens");
      <%_ } _%>
    <%_ } _%>
  <%_ } _%>
  <%_ if (cacheProviderEhCache) { _%>
            // jhipster-needle-ehcache-add-entry
  <%_ } _%>
  <%_ if (cacheProviderCaffeine) { _%>
            // jhipster-needle-caffeine-add-entry
  <%_ } _%>
        };
    }

    private void createCache(javax.cache.CacheManager cm, String cacheName) {
        javax.cache.Cache<Object, Object> cache = cm.getCache(cacheName);
        if (cache != null) {
            cache.clear();
        } else {
            cm.createCache(cacheName, jcacheConfiguration);
        }
    }
<%_ } _%>
<%_ if (cacheProviderHazelcast) { _%>

    private final Logger log = LoggerFactory.getLogger(CacheConfiguration.class);

    private final Environment env;
  <%_ if (serviceDiscoveryAny) { _%>

    private final ServerProperties serverProperties;

    private final DiscoveryClient discoveryClient;

    private Registration registration;
  <%_ } _%>

    public CacheConfiguration(<% if (cacheProviderHazelcast) { %>Environment env<% if (serviceDiscoveryAny) { %>, ServerProperties serverProperties, DiscoveryClient discoveryClient<% } } %>) {
        this.env = env;
  <%_ if (serviceDiscoveryAny) { _%>
        this.serverProperties = serverProperties;
        this.discoveryClient = discoveryClient;
  <%_ } _%>
    }
  <%_ if (serviceDiscoveryAny) { _%>

    @Autowired(required = false)
    public void setRegistration(Registration registration) {
        this.registration = registration;
    }
  <%_ } _%>

    @PreDestroy
    public void destroy() {
        log.info("Closing Cache Manager");
        Hazelcast.shutdownAll();
    }

    @Bean
    public CacheManager cacheManager(HazelcastInstance hazelcastInstance) {
        log.debug("Starting HazelcastCacheManager");
        return new com.hazelcast.spring.cache.HazelcastCacheManager(hazelcastInstance);
    }

    @Bean
    public HazelcastInstance hazelcastInstance(JHipsterProperties jHipsterProperties) {
        log.debug("Configuring Hazelcast");
        HazelcastInstance hazelCastInstance = Hazelcast.getHazelcastInstanceByName("<%= baseName %>");
        if (hazelCastInstance != null) {
            log.debug("Hazelcast already initialized");
            return hazelCastInstance;
        }
        Config config = new Config();
        config.setInstanceName("<%= baseName %>");
  <%_ if (serviceDiscoveryAny) { _%>
        config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
        if (this.registration == null) {
            log.warn("No discovery service is set up, Hazelcast cannot create a cluster.");
        } else {
            // The serviceId is by default the application's name,
            // see the "spring.application.name" standard Spring property
            String serviceId = registration.getServiceId();
            log.debug("Configuring Hazelcast clustering for instanceId: {}", serviceId);
            // In development, everything goes through 127.0.0.1, with a different port
            if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) {
                log.debug("Application is running with the \"dev\" profile, Hazelcast " +
                          "cluster will only work with localhost instances");

                config.getNetworkConfig().setPort(serverProperties.getPort() + 5701);
                config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true);
                for (ServiceInstance instance : discoveryClient.getInstances(serviceId)) {
                    String clusterMember = "127.0.0.1:" + (instance.getPort() + 5701);
                    log.debug("Adding Hazelcast (dev) cluster member {}", clusterMember);
                    config.getNetworkConfig().getJoin().getTcpIpConfig().addMember(clusterMember);
                }
            } else { // Production configuration, one host per instance all using port 5701
                config.getNetworkConfig().setPort(5701);
                config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true);
                for (ServiceInstance instance : discoveryClient.getInstances(serviceId)) {
                    String clusterMember = instance.getHost() + ":5701";
                    log.debug("Adding Hazelcast (prod) cluster member {}", clusterMember);
                    config.getNetworkConfig().getJoin().getTcpIpConfig().addMember(clusterMember);
                }
            }
        }
  <%_ } else { _%>
        config.getNetworkConfig().setPort(5701);
        config.getNetworkConfig().setPortAutoIncrement(true);

        // In development, remove multicast auto-configuration
        if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) {
            System.setProperty("hazelcast.local.localAddress", "127.0.0.1");

            config.getNetworkConfig().getJoin().getAwsConfig().setEnabled(false);
            config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
            config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(false);
        }
        <%_ } _%>
        config.setManagementCenterConfig(new ManagementCenterConfig());
        config.addMapConfig(initializeDefaultMapConfig(jHipsterProperties));
    <%_ if (enableHibernateCache) { _%>
        config.addMapConfig(initializeDomainMapConfig(jHipsterProperties));
    <%_ } _%>
        return Hazelcast.newHazelcastInstance(config);
    }

    private MapConfig initializeDefaultMapConfig(JHipsterProperties jHipsterProperties) {
        MapConfig mapConfig = new MapConfig("default");

        /*
        Number of backups. If 1 is set as the backup-count for example,
        then all entries of the map will be copied to another JVM for
        fail-safety. Valid numbers are 0 (no backup), 1, 2, 3.
        */
        mapConfig.setBackupCount(jHipsterProperties.getCache().getHazelcast().getBackupCount());

        /*
        Valid values are:
        NONE (no eviction),
        LRU (Least Recently Used),
        LFU (Least Frequently Used).
        NONE is the default.
        */
        mapConfig.getEvictionConfig().setEvictionPolicy(EvictionPolicy.LRU);

        /*
        Maximum size of the map. When max size is reached,
        map is evicted based on the policy defined.
        Any integer between 0 and Integer.MAX_VALUE. 0 means
        Integer.MAX_VALUE. Default is 0.
        */
        mapConfig.getEvictionConfig().setMaxSizePolicy(MaxSizePolicy.USED_HEAP_SIZE);

        return mapConfig;
    }

    private MapConfig initializeDomainMapConfig(JHipsterProperties jHipsterProperties) {
        MapConfig mapConfig = new MapConfig("<%= packageName %>.domain.*");
        mapConfig.setTimeToLiveSeconds(jHipsterProperties.getCache().getHazelcast().getTimeToLiveSeconds());
        return mapConfig;
    }
<%_ } _%>
<%_ if (cacheProviderInfinispan) { _%>

    private final Logger log = LoggerFactory.getLogger(CacheConfiguration.class);

  <%_ if (serviceDiscoveryAny) { _%>

    private DiscoveryClient discoveryClient;

    private Registration registration;

    @Autowired(required = false)
    public void setRegistration(Registration registration) {
        this.registration = registration;
    }

    @Autowired(required = false)
    public void setDiscoveryClient(DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }
  <%_ } _%>

    /**
     * Inject a {@link org.infinispan.configuration.global.GlobalConfiguration GlobalConfiguration} for Infinispan cache.
     * <p>
     * If a service discovery solution is enabled (JHipster Registry or Consul),
     * then the host list will be populated from the service discovery.
     *
     * <p>
     * If the service discovery is not enabled, host discovery will be based on
     * the default transport settings defined in the 'config-file' packaged within
     * the Jar. The 'config-file' can be overridden using the application property
     * <i>jhipster.cache.infinispan.config-file</i>
     *
     * <p>
     * If no service discovery is defined, you have the choice of 'config-file'
     * based on the underlying platform for hosts discovery. Infinispan
     * supports discovery natively for most of the platforms like Kubernetes/OpenShift,
     * AWS, Azure and Google.
     *
     * @param jHipsterProperties the jhipster properties to configure from.
     * @return the infinispan global configurer.
     */
    @Bean
    public InfinispanGlobalConfigurer globalConfiguration(JHipsterProperties jHipsterProperties) {
        log.info("Defining Infinispan Global Configuration");
  <%_ if (serviceDiscoveryAny) { _%>
            if (this.registration == null) { // if registry is not defined, use native discovery
                log.warn("No discovery service is set up, Infinispan will use default discovery for cluster formation");
                return () -> GlobalConfigurationBuilder
                    .defaultClusteredBuilder().transport().defaultTransport()
                    .addProperty("configurationFile", jHipsterProperties.getCache().getInfinispan().getConfigFile())
                    .clusterName("infinispan-<%= baseName %>-cluster")
                    .jmx().enabled(jHipsterProperties.getCache().getInfinispan().isStatsEnabled())
                    .serialization().marshaller(new JBossUserMarshaller())
                    .build();
            } else if (discoveryClient.getInstances(registration.getServiceId()).size() == 0) {
                return () -> GlobalConfigurationBuilder
                    .defaultClusteredBuilder().transport()
                    .transport(new JGroupsTransport())
                    .clusterName("infinispan-<%= baseName %>-cluster")
                    .jmx().enabled(jHipsterProperties.getCache().getInfinispan().isStatsEnabled())
                    .serialization().marshaller(new JBossUserMarshaller())
                    .build();
            } else {
                return () -> GlobalConfigurationBuilder
                    .defaultClusteredBuilder().transport()
                    .transport(new JGroupsTransport(getTransportChannel()))
                    .clusterName("infinispan-<%= baseName %>-cluster")
                    .jmx().enabled(jHipsterProperties.getCache().getInfinispan().isStatsEnabled())
                    .serialization().marshaller(new JBossUserMarshaller())
                    .build();
            }
  <%_ } else { _%>
            return () -> GlobalConfigurationBuilder
                    .defaultClusteredBuilder().transport().defaultTransport()
                    .addProperty("configurationFile", jHipsterProperties.getCache().getInfinispan().getConfigFile())
                    .clusterName("infinispan-<%= baseName %>-cluster")
                    .jmx().enabled(jHipsterProperties.getCache().getInfinispan().isStatsEnabled())
                    .serialization().marshaller(new JBossUserMarshaller())
                    .build();
  <%_ } _%>
    }

    /**
     * Initialize cache configuration for Hibernate L2 cache and Spring Cache.
     * <p>
     * There are three different modes: local, distributed and replicated, and L2 cache options are pre-configured.
     *
     * <p>
     * It supports both jCache and Spring cache abstractions.
     * <p>
     * Usage:
     *  <ol>
     *      <li>
     *          jCache:
     *          <pre class="code">@CacheResult(cacheName = "dist-app-data") </pre>
     *              - for creating a distributed cache. In a similar way other cache names and options can be used
     *      </li>
     *      <li>
     *          Spring Cache:
     *          <pre class="code">@Cacheable(value = "repl-app-data") </pre>
     *              - for creating a replicated cache. In a similar way other cache names and options can be used
     *      </li>
     *      <li>
     *          Cache manager can also be injected through DI/CDI and data can be manipulated using Infinispan APIs,
     *          <pre class="code">
     *          &#064;Autowired (or) &#064;Inject
     *          private EmbeddedCacheManager cacheManager;
     *
     *          void cacheSample() {
     *              cacheManager.getCache("dist-app-data").put("hi", "there");
     *          }
     *          </pre>
     *      </li>
     *  </ol>
     *
     * @param jHipsterProperties the jhipster properties to configure from.
     * @return the infinispan cache configurer.
     */
    @Bean
    public InfinispanCacheConfigurer cacheConfigurer(JHipsterProperties jHipsterProperties) {
        log.info("Defining {} configuration", "app-data for local, replicated and distributed modes");
        JHipsterProperties.Cache.Infinispan cacheInfo = jHipsterProperties.getCache().getInfinispan();

        return manager -> {
            // initialize application cache
            manager.defineConfiguration("local-app-data", new ConfigurationBuilder()
                .clustering().cacheMode(CacheMode.LOCAL)
                .statistics().enabled(cacheInfo.isStatsEnabled())
                .memory().maxCount(cacheInfo.getLocal().getMaxEntries()).expiration()
                .lifespan(cacheInfo.getLocal().getTimeToLiveSeconds(), TimeUnit.SECONDS).build());
            manager.defineConfiguration("dist-app-data", new ConfigurationBuilder()
                .clustering().cacheMode(CacheMode.DIST_SYNC).hash().numOwners(cacheInfo.getDistributed().getInstanceCount())
                .statistics().enabled(cacheInfo.isStatsEnabled())
                .memory().maxCount(cacheInfo.getDistributed().getMaxEntries()).expiration()
                .lifespan(cacheInfo.getDistributed().getTimeToLiveSeconds(), TimeUnit.SECONDS).build());
            manager.defineConfiguration("repl-app-data", new ConfigurationBuilder()
                .clustering().cacheMode(CacheMode.REPL_SYNC)
                .statistics().enabled(cacheInfo.isStatsEnabled())
                .memory().maxCount(cacheInfo.getReplicated().getMaxEntries()).expiration()
                .lifespan(cacheInfo.getReplicated().getTimeToLiveSeconds(), TimeUnit.SECONDS).build());

            // initialize Hibernate L2 cache configuration templates
            manager.defineConfiguration("entity", new ConfigurationBuilder().clustering().cacheMode(CacheMode.INVALIDATION_SYNC)
                .statistics().enabled(cacheInfo.isStatsEnabled())
                .locking().concurrencyLevel(1000).lockAcquisitionTimeout(15000).template(true).build());
            manager.defineConfiguration("replicated-entity", new ConfigurationBuilder().clustering().cacheMode(CacheMode.REPL_SYNC)
                .statistics().enabled(cacheInfo.isStatsEnabled())
                .locking().concurrencyLevel(1000).lockAcquisitionTimeout(15000).template(true).build());
            manager.defineConfiguration("local-query", new ConfigurationBuilder().clustering().cacheMode(CacheMode.LOCAL)
                .statistics().enabled(cacheInfo.isStatsEnabled())
                .locking().concurrencyLevel(1000).lockAcquisitionTimeout(15000).template(true).build());
            manager.defineConfiguration("replicated-query", new ConfigurationBuilder().clustering().cacheMode(CacheMode.REPL_ASYNC)
                .statistics().enabled(cacheInfo.isStatsEnabled())
                .locking().concurrencyLevel(1000).lockAcquisitionTimeout(15000).template(true).build());
            manager.defineConfiguration("timestamps", new ConfigurationBuilder().clustering().cacheMode(CacheMode.REPL_ASYNC)
                .statistics().enabled(cacheInfo.isStatsEnabled())
                .locking().concurrencyLevel(1000).lockAcquisitionTimeout(15000).template(true).build());
            manager.defineConfiguration("pending-puts", new ConfigurationBuilder().clustering().cacheMode(CacheMode.LOCAL)
                .statistics().enabled(cacheInfo.isStatsEnabled())
                .simpleCache(true).transaction().transactionMode(TransactionMode.NON_TRANSACTIONAL).expiration().maxIdle(60000).template(true).build());

  <%_ if (!skipUserManagement || (authenticationTypeOauth2 && !databaseTypeNo)) { _%>
            Stream.of(<%= packageName %>.repository.UserRepository.USERS_BY_LOGIN_CACHE, <%= packageName %>.repository.UserRepository.USERS_BY_EMAIL_CACHE)
                .forEach(cacheName -> manager.defineConfiguration(cacheName, new ConfigurationBuilder().clustering()
                    .cacheMode(CacheMode.INVALIDATION_SYNC)
                    .statistics()
                    .enabled(cacheInfo.isStatsEnabled())
                    .locking()
                    .concurrencyLevel(1000)
                    .lockAcquisitionTimeout(15000)
                    .build()));
  <%_ } _%>
  <%_ if (authenticationTypeOauth2 && applicationTypeMicroservice) { _%>
            manager.defineConfiguration("oAuth2Authentication", new ConfigurationBuilder()
                .clustering().cacheMode(CacheMode.LOCAL)
                .statistics().enabled(cacheInfo.isStatsEnabled())
                .memory().maxCount(cacheInfo.getLocal().getMaxEntries()).expiration()
                .lifespan(cacheInfo.getLocal().getTimeToLiveSeconds(), TimeUnit.SECONDS).build());
  <%_ } _%>

        };
    }
  <%_ if (enableHibernateCache) { _%>
    /**
    * Hibernate properties customizer.
    * This component ensures that Hibernate L2 cache region factory uses, as cache manager, the
    * same instance already configured in Spring Context with {@link CacheConfiguration}
    *
    * @param cacheFactoryConfiguration custom infinispan region factory
    * @return hibernate properties with custom cache region factory
    */
    @Bean
    public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(CacheFactoryConfiguration cacheFactoryConfiguration) {
        return hibernateProperties -> hibernateProperties.put(AvailableSettings.CACHE_REGION_FACTORY, cacheFactoryConfiguration);
    }
  <%_ } _%>

  <%_ if (serviceDiscoveryAny) { _%>
    /**
     * TCP channel with the host details populated from the service discovery
     * (JHipster Registry or Consul).
     * <p>
     * MPING multicast is replaced with TCPPING with the host details discovered
     * from registry and sends only unicast messages to the host list.
     */
    private JChannel getTransportChannel() {
        JChannel channel;
        List<InetSocketAddress> initialHosts = new ArrayList<>();
        try {
            for (ServiceInstance instance : discoveryClient.getInstances(registration.getServiceId())) {
                log.debug("Adding Infinispan cluster member {}:{}", instance.getHost(), 7800);
                initialHosts.add(new InetSocketAddress(instance.getHost(), 7800));
            }
            TCP tcp = new TCP();
            tcp.setBindAddress(InetAddress.getLocalHost());
            tcp.setBindPort(7800);
            tcp.setThreadPoolMinThreads(2);
            tcp.setThreadPoolMaxThreads(30);
            tcp.setThreadPoolKeepAliveTime(60000);

            TCPPING tcpping = new TCPPING();
            initialHosts.add(new InetSocketAddress(InetAddress.getLocalHost(), 7800));
            tcpping.setInitialHosts(initialHosts);
            tcpping.setErgonomics(false);
            tcpping.setPortRange(10);
            tcpping.sendCacheInformation();

            NAKACK2 nakack = new NAKACK2();
            nakack.setUseMcastXmit(false);
            nakack.setDiscardDeliveredMsgs(false);

            MERGE3 merge = new MERGE3();
            merge.setMinInterval(10000);
            merge.setMaxInterval(30000);

            FD_ALL fd = new FD_ALL();
            fd.setTimeout(60000);
            fd.setInterval(15000);
            fd.setTimeoutCheckInterval(5000);

            ProtocolStack stack = new ProtocolStack();
            // Order shouldn't be changed
            stack
                .addProtocol(tcp)
                .addProtocol(tcpping)
                .addProtocol(merge)
                .addProtocol(new FD_SOCK())
                .addProtocol(fd)
                .addProtocol(new VERIFY_SUSPECT())
                .addProtocol(nakack)
                .addProtocol(new UNICAST3())
                .addProtocol(new STABLE())
                .addProtocol(new GMS())
                .addProtocol(new MFC())
                .addProtocol(new FRAG2());
            channel = new JChannel(stack);
            stack.init();
        } catch (Exception e) {
            throw new BeanInitializationException("Cache (Infinispan protocol stack) configuration failed", e);
        }
        return channel;
    }
  <%_ } _%>
<%_ } _%>
<%_ if (cacheProviderMemcached) { _%>

    private final Logger log = LoggerFactory.getLogger(CacheConfiguration.class);

    @Bean
    public CacheManager memcachedCacheManager(JHipsterProperties jHipsterProperties, List<CacheFactory> caches)
        throws Exception {

        if (!jHipsterProperties.getCache().getMemcached().isEnabled()) {
            // Note that Memcached cannot work with Spring Boot devtools
            // So it should be disabled in development mode
            log.debug("Memcached is disabled");
            return new NoOpCacheManager();
        }
        log.debug("Starting Memcached configuration");
        SSMCacheManager cacheManager = new SSMCacheManager();
        List<SSMCache> ssmCaches = new ArrayList<>();
        for (CacheFactory cache : caches) {
            SSMCache ssmCache =
                new SSMCache(cache.getObject(), jHipsterProperties.getCache().getMemcached().getExpiration(),
                    false);

            ssmCaches.add(ssmCache);
        }
        cacheManager.setCaches(ssmCaches);
        return cacheManager;
    }

    @Bean
    CacheFactory defaultCacheFactory(JHipsterProperties jHipsterProperties) {
        return createCache("default", jHipsterProperties);
    }
  <%_ if (!skipUserManagement || (authenticationTypeOauth2 && !databaseTypeNo)) { _%>

    @Bean
    public CacheFactory usersByLoginCache(JHipsterProperties jHipsterProperties) {
        return this.createCache(UserRepository.USERS_BY_LOGIN_CACHE, jHipsterProperties);
    }

    @Bean
    public CacheFactory usersByEmailCache(JHipsterProperties jHipsterProperties) {
        return this.createCache(UserRepository.USERS_BY_EMAIL_CACHE, jHipsterProperties);
    }
  <%_ } _%>

    private CacheFactory createCache(String cacheName, JHipsterProperties jHipsterProperties) {
        if (!jHipsterProperties.getCache().getMemcached().isEnabled()) {
            // Note that Memcached cannot work with Spring Boot devtools
            // So it should be disabled in development mode
            return null;
        }
        CacheFactory defaultCache = new CacheFactory();
        defaultCache.setCacheName(cacheName);
        defaultCache.setCacheClientFactory(new MemcacheClientFactoryImpl());

        DefaultAddressProvider addressProvider = new DefaultAddressProvider();
        addressProvider.setAddress(jHipsterProperties.getCache().getMemcached().getServers());
        defaultCache.setAddressProvider(addressProvider);

        Map<InetSocketAddress, AuthInfo> authInfoMap = new HashMap<>();

        if (jHipsterProperties.getCache().getMemcached().getAuthentication().isEnabled()) {
            InetSocketAddress memcachedServers = AddrUtil.getOneAddress(jHipsterProperties.getCache().getMemcached().getServers());
            AuthInfo authInfo =
                AuthInfo.plain(
                    jHipsterProperties.getCache().getMemcached().getAuthentication().getUsername(),
                    jHipsterProperties.getCache().getMemcached().getAuthentication().getPassword());

            authInfoMap.put(memcachedServers, authInfo);
        }
        XMemcachedConfiguration cacheConfiguration = new XMemcachedConfiguration();
        if (!authInfoMap.isEmpty()) {
            cacheConfiguration.setAuthInfoMap(authInfoMap);
        }
        cacheConfiguration.setConsistentHashing(true);
        cacheConfiguration.setUseBinaryProtocol(jHipsterProperties.getCache().getMemcached().isUseBinaryProtocol());
        defaultCache.setConfiguration(cacheConfiguration);

        return defaultCache;
    }
<%_ } _%>

<%_ if (cacheProviderRedis) { _%>
    @Bean
    public javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration(JHipsterProperties jHipsterProperties) {
        MutableConfiguration<Object, Object> jcacheConfig = new MutableConfiguration<>();

        URI redisUri = URI.create(jHipsterProperties.getCache().getRedis().getServer()[0]);

        Config config = new Config();
        if (jHipsterProperties.getCache().getRedis().isCluster()) {
            ClusterServersConfig clusterServersConfig = config
                .useClusterServers()
                .setMasterConnectionPoolSize(jHipsterProperties.getCache().getRedis().getConnectionPoolSize())
                .setMasterConnectionMinimumIdleSize(jHipsterProperties.getCache().getRedis().getConnectionMinimumIdleSize())
                .setSubscriptionConnectionPoolSize(jHipsterProperties.getCache().getRedis().getSubscriptionConnectionPoolSize())
                .addNodeAddress(jHipsterProperties.getCache().getRedis().getServer());

            if (redisUri.getUserInfo() != null) {
                clusterServersConfig.setPassword(redisUri.getUserInfo().substring(redisUri.getUserInfo().indexOf(':') + 1));
            }
        } else {
            SingleServerConfig singleServerConfig = config
                .useSingleServer()
                .setConnectionPoolSize(jHipsterProperties.getCache().getRedis().getConnectionPoolSize())
                .setConnectionMinimumIdleSize(jHipsterProperties.getCache().getRedis().getConnectionMinimumIdleSize())
                .setSubscriptionConnectionPoolSize(jHipsterProperties.getCache().getRedis().getSubscriptionConnectionPoolSize())
                .setAddress(jHipsterProperties.getCache().getRedis().getServer()[0]);

            if (redisUri.getUserInfo() != null) {
                singleServerConfig.setPassword(redisUri.getUserInfo().substring(redisUri.getUserInfo().indexOf(':') + 1));
            }
        }
        jcacheConfig.setStatisticsEnabled(true);
        jcacheConfig.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS, jHipsterProperties.getCache().getRedis().getExpiration())));
        return RedissonConfiguration.fromInstance(Redisson.create(config), jcacheConfig);
    }

  <%_ if (enableHibernateCache) { _%>
    @Bean
    public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(javax.cache.CacheManager cm) {
        return hibernateProperties -> hibernateProperties.put(ConfigSettings.CACHE_MANAGER, cm);
    }
  <%_ } _%>

    @Bean
    public JCacheManagerCustomizer cacheManagerCustomizer(javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration) {
        return cm -> {
  <%_ if (!skipUserManagement || (authenticationTypeOauth2 && !databaseTypeNo)) { _%>
            createCache(cm, <%= packageName %>.repository.UserRepository.USERS_BY_LOGIN_CACHE, jcacheConfiguration);
            createCache(cm, <%= packageName %>.repository.UserRepository.USERS_BY_EMAIL_CACHE, jcacheConfiguration);
    <%_ if (enableHibernateCache) { _%>
            createCache(cm, <%= packageName %>.domain.<%= asEntity('User') %>.class.getName(), jcacheConfiguration);
            createCache(cm, <%= packageName %>.domain.Authority.class.getName(), jcacheConfiguration);
            createCache(cm, <%= packageName %>.domain.<%= asEntity('User') %>.class.getName() + ".authorities", jcacheConfiguration);
      <%_ if (authenticationTypeSession) { _%>
            createCache(cm, <%= packageName %>.domain.PersistentToken.class.getName(), jcacheConfiguration);
            createCache(cm, <%= packageName %>.domain.<%= asEntity('User') %>.class.getName() + ".persistentTokens", jcacheConfiguration);
      <%_ } _%>
    <%_ } _%>
  <%_ } _%>
            // jhipster-needle-redis-add-entry
        };
    }

    private void createCache(javax.cache.CacheManager cm, String cacheName, javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration) {
        javax.cache.Cache<Object, Object> cache = cm.getCache(cacheName);
        if (cache != null) {
            cache.clear();
        } else {
            cm.createCache(cacheName, jcacheConfiguration);
        }
    }

<%_ } _%>
<%_ if (!cacheProviderNo) { _%>
    @Autowired(required = false)
    public void setGitProperties(GitProperties gitProperties) {
        this.gitProperties = gitProperties;
    }

    @Autowired(required = false)
    public void setBuildProperties(BuildProperties buildProperties) {
        this.buildProperties = buildProperties;
    }

    @Bean
    public KeyGenerator keyGenerator() {
        return new PrefixedKeyGenerator(this.gitProperties, this.buildProperties);
    }
<%_ } _%>
}
