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

org.apereo.cas.config.Ehcache3TicketRegistryConfiguration Maven / Gradle / Ivy

There is a newer version: 6.6.15.2
Show newest version
package org.apereo.cas.config;

import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.ticket.Ticket;
import org.apereo.cas.ticket.TicketCatalog;
import org.apereo.cas.ticket.TicketDefinition;
import org.apereo.cas.ticket.registry.EhCache3TicketRegistry;
import org.apereo.cas.ticket.registry.TicketRegistry;
import org.apereo.cas.util.CoreTicketUtils;
import org.apereo.cas.util.model.Capacity;

import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.lang3.StringUtils;
import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder;
import org.ehcache.clustered.client.config.builders.ClusteredStoreConfigurationBuilder;
import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder;
import org.ehcache.clustered.client.config.builders.TimeoutsBuilder;
import org.ehcache.clustered.common.Consistency;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheEventListenerConfigurationBuilder;
import org.ehcache.config.builders.ExpiryPolicyBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.core.config.DefaultConfiguration;
import org.ehcache.event.CacheEvent;
import org.ehcache.event.CacheEventListener;
import org.ehcache.event.EventType;
import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration;
import org.ehcache.jsr107.Eh107Configuration;
import org.ehcache.jsr107.EhcacheCachingProvider;
import org.ehcache.jsr107.config.ConfigurationElementState;
import org.ehcache.jsr107.config.Jsr107Configuration;
import org.ehcache.spi.service.ServiceCreationConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.cache.Caching;

import java.io.File;
import java.net.URI;
import java.time.Duration;
import java.util.HashMap;

/**
 * This is {@link Ehcache3TicketRegistryConfiguration}.
 *
 * @author Hal Deadman
 * @since 6.2.0
 */
@Configuration(value = "ehcache3TicketRegistryConfiguration", proxyBeanMethods = false)
@EnableConfigurationProperties(CasConfigurationProperties.class)
@ConditionalOnProperty(prefix = "cas.ticket.registry.ehcache3", name = "enabled", havingValue = "true", matchIfMissing = true)
@Slf4j
public class Ehcache3TicketRegistryConfiguration {

    @Autowired
    private CasConfigurationProperties casProperties;

    @Bean
    @ConditionalOnMissingBean(name = "ehcache3CacheManagerConfiguration")
    public ServiceCreationConfiguration ehcache3CacheManagerConfiguration() {
        val ehcacheProperties = casProperties.getTicket().getRegistry().getEhcache3();
        val terracottaClusterUri = ehcacheProperties.getTerracottaClusterUri();
        if (StringUtils.isNotBlank(terracottaClusterUri)) {
            val resourcePoolCapacity = Capacity.parse(ehcacheProperties.getResourcePoolSize());
            val clusterConfigBuilder = ClusteringServiceConfigurationBuilder.cluster(URI.create(terracottaClusterUri))
                .timeouts(TimeoutsBuilder.timeouts()
                    .connection(Duration.ofSeconds(ehcacheProperties.getClusterConnectionTimeout()))
                    .read(Duration.ofSeconds(ehcacheProperties.getClusterReadWriteTimeout()))
                    .write(Duration.ofSeconds(ehcacheProperties.getClusterReadWriteTimeout())).build())
                .autoCreate(s -> s.defaultServerResource(ehcacheProperties.getDefaultServerResource())
                    .resourcePool(ehcacheProperties.getResourcePoolName(), resourcePoolCapacity.getSize().longValue(),
                        MemoryUnit.valueOf(resourcePoolCapacity.getUnitOfMeasure().name())));
            return clusterConfigBuilder.build();
        }
        val rootDirectory = ehcacheProperties.getRootDirectory();
        val rootDirectoryFile = new File(rootDirectory);
        if (!rootDirectoryFile.exists()) {
            LOGGER.debug("Creating folder for ehcache ticket registry disk cache [{}]", rootDirectory);
            val mkdirResult = rootDirectoryFile.mkdirs();
            if (!mkdirResult) {
                LOGGER.warn("Unable to create folder for ehcache ticket registry disk cache [{}]", rootDirectory);
            }
        }
        return new DefaultPersistenceConfiguration(rootDirectoryFile);
    }

    @Bean
    @ConditionalOnMissingBean(name = "ehcache3TicketCacheManager")
    @Autowired
    public javax.cache.CacheManager ehcache3TicketCacheManager(@Qualifier("ehcache3CacheManagerConfiguration")
                                                               final ServiceCreationConfiguration ehcache3CacheManagerConfiguration) {
        val ehcacheProperties = casProperties.getTicket().getRegistry().getEhcache3();
        val ehcacheProvider = (EhcacheCachingProvider) Caching.getCachingProvider(EhcacheCachingProvider.class.getName());
        val statisticsAllEnabled = ehcacheProperties.isEnableStatistics() ? ConfigurationElementState.ENABLED : ConfigurationElementState.DISABLED;
        val managementAllAllEnabled = ehcacheProperties.isEnableManagement() ? ConfigurationElementState.ENABLED : ConfigurationElementState.DISABLED;
        val jsr107Config = new Jsr107Configuration(null, new HashMap<>(),
            false, managementAllAllEnabled, statisticsAllEnabled);
        val configuration = new DefaultConfiguration(ehcacheProvider.getDefaultClassLoader(),
            ehcache3CacheManagerConfiguration,
            jsr107Config);
        return ehcacheProvider.getCacheManager(ehcacheProvider.getDefaultURI(), configuration);
    }

    private CacheConfiguration buildCacheConfiguration(final TicketDefinition ticketDefinition) {
        val ehcacheProperties = casProperties.getTicket().getRegistry().getEhcache3();
        val terracottaClusterUri = ehcacheProperties.getTerracottaClusterUri();

        val cacheEventListenerConfiguration = CacheEventListenerConfigurationBuilder
            .newEventListenerConfiguration(new CasCacheEventListener(),
                EventType.CREATED, EventType.UPDATED, EventType.EXPIRED, EventType.REMOVED, EventType.EVICTED)
            .ordered().asynchronous();

        val storageTimeout = ticketDefinition.getProperties().getStorageTimeout();
        val expiryPolicy = ehcacheProperties.isEternal() ? ExpiryPolicyBuilder.noExpiration()
            : ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(storageTimeout));

        var resourcePools = ResourcePoolsBuilder.heap(ehcacheProperties.getMaxElementsInMemory());
        if (StringUtils.isNotBlank(terracottaClusterUri)) {
            resourcePools = resourcePools.with(
                ClusteredResourcePoolBuilder.clusteredShared(ehcacheProperties.getResourcePoolName()));
        }

        val resourcePoolCapacity = Capacity.parse(ehcacheProperties.getResourcePoolSize());
        resourcePools = resourcePools
            .offheap(resourcePoolCapacity.getSize().longValue(), MemoryUnit.valueOf(resourcePoolCapacity.getUnitOfMeasure().name()));

        if (StringUtils.isBlank(terracottaClusterUri)) {
            val perCacheCapacity = Capacity.parse(ehcacheProperties.getPerCacheSizeOnDisk());
            val persistOnDisk = ehcacheProperties.isPersistOnDisk();
            resourcePools = resourcePools.disk(perCacheCapacity.getSize().longValue(),
                MemoryUnit.valueOf(perCacheCapacity.getUnitOfMeasure().name()), persistOnDisk);
        }

        var cacheConfigBuilder = CacheConfigurationBuilder.newCacheConfigurationBuilder(
            String.class, Ticket.class, resourcePools)
            .withExpiry(expiryPolicy)
            .withService(cacheEventListenerConfiguration);

        if (StringUtils.isNotBlank(terracottaClusterUri)) {
            cacheConfigBuilder = cacheConfigBuilder.withService(
                ClusteredStoreConfigurationBuilder.withConsistency(Consistency.valueOf(ehcacheProperties.getClusteredCacheConsistency().name())));
        }

        return cacheConfigBuilder.build();
    }

    /**
     * Create ticket registry bean with all nececessary caches.
     * Using the spring ehcache wrapper bean so it can be initialized after the caches are built.
     *
     * @param ehcacheManager Spring EhCache manager bean, wraps EhCache manager and is used for cache actuator endpoint.
     * @param ticketCatalog  Ticket Catalog
     * @return Ticket Registry
     */
    @Autowired
    @Bean
    @RefreshScope
    public TicketRegistry ticketRegistry(@Qualifier("ehcache3TicketCacheManager") final javax.cache.CacheManager ehcacheManager,
                                         @Qualifier("ticketCatalog") final TicketCatalog ticketCatalog) {
        val ehcacheProperties = casProperties.getTicket().getRegistry().getEhcache3();
        val crypto = ehcacheProperties.getCrypto();

        val definitions = ticketCatalog.findAll();
        definitions.forEach(t -> {
            val cacheName = t.getProperties().getStorageName();
            if (ehcacheManager.getCache(cacheName, String.class, Ticket.class) == null) {
                val ehcacheConfiguration = buildCacheConfiguration(t);
                ehcacheManager.createCache(cacheName, Eh107Configuration.fromEhcacheCacheConfiguration(ehcacheConfiguration));
            }
        });

        return new EhCache3TicketRegistry(ticketCatalog, ehcacheManager,
            CoreTicketUtils.newTicketRegistryCipherExecutor(crypto, "ehcache3"));
    }

    /**
     * This bean is used by the spring boot cache actuator which spring boot admin can use to clear caches.
     * Actuator needs to be exposed in order for this bean to be used.
     *
     * @param ehcache3TicketCacheManager JSR107 wrapper of EhCache cache manager to be wrapped by spring cache manager.
     * @return Spring EhCacheCacheManager that wraps EhCache JSR107 CacheManager
     */
    @Bean
    public JCacheCacheManager ehCacheJCacheCacheManager(@Qualifier("ehcache3TicketCacheManager") final javax.cache.CacheManager ehcache3TicketCacheManager) {
        return new JCacheCacheManager(ehcache3TicketCacheManager);
    }

    private static class CasCacheEventListener implements CacheEventListener {
        @Override
        public void onEvent(final CacheEvent event) {
            LOGGER.trace("Event Type: [{}], Ticket Id: [{}]", event.getType().name(), event.getKey());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy