@SuppressWarnings({"unchecked", "rawtypes"})
    @Override
    public synchronized void rebuild() {
        LOGGER.trace(() -> buildMessage("rebuild"));

        final CacheHolder<K, V> existingCacheHolder = this.cacheHolder;
        final CacheConfig newCacheConfig = cacheConfigSupplier.get();

        if (existingCacheHolder != null
            && Objects.equals(existingCacheHolder.getCacheConfig(), newCacheConfig)) {
            LOGGER.info("Clearing cache '{}' (Property path: '{}'). No config changed.",
                    name, getBasePropertyPath());
            CacheUtil.clear(getCache());
        } else {
            if (existingCacheHolder != null) {
                // Don't log initial cache creation, only explicit rebuilds
                LOGGER.info("Rebuilding cache '{}' from config (Property path: '{}'). newCacheConfig: {}.",
                        name, getBasePropertyPath(), newCacheConfig);
            }
            final Caffeine newCacheBuilder = Caffeine.newBuilder();

            configureStatisticsRecording(newCacheBuilder, newCacheConfig);

            NullSafe.consume(newCacheConfig.getMaximumSize(), newCacheBuilder::maximumSize);
            NullSafe.consume(
                    newCacheConfig.getExpireAfterAccess(),
                    StroomDuration::getDuration,
                    newCacheBuilder::expireAfterAccess);
            NullSafe.consume(
                    newCacheConfig.getExpireAfterWrite(),
                    StroomDuration::getDuration,
                    newCacheBuilder::expireAfterWrite);
            NullSafe.consume(
                    newCacheConfig.getRefreshAfterWrite(),
                    StroomDuration::getDuration,
                    newCacheBuilder::refreshAfterWrite);

            if (removalNotificationConsumer != null) {
                final RemovalListener<K, V> removalListener = createRemovalListener();
                newCacheBuilder.removalListener(removalListener);
            }

            final Cache<K, V> newCache = createCacheFromBuilder(newCacheBuilder);

            final CacheHolder<K, V> newCacheHolder = new CacheHolder<>(newCache, newCacheBuilder, newCacheConfig);
            LOGGER.debug("Assigning new cacheHolder instance");
            this.cacheHolder = newCacheHolder;
        }
    }
Subclasses should implement this method to add to the cacheBuilder as required
and return a cache built from cacheBuilder. The builder already has a
removalNotificationConsumer if applicable and recordStats has been set.
stroom-cache/stroom-cache-impl/src/main/java/stroom/cache/impl/AbstractStroomCache.java