/*
 * Copyright Terracotta, Inc.
 *
 * 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
 *
 *     http://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 org.ehcache.core.config;

import org.ehcache.Cache;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.Configuration;
import org.ehcache.config.FluentCacheConfigurationBuilder;
import org.ehcache.config.FluentConfigurationBuilder;
import org.ehcache.spi.service.ServiceCreationConfiguration;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

public class CoreConfigurationBuilder<B extends CoreConfigurationBuilder<B>> implements FluentConfigurationBuilder<B> {

  private final Map<String, CacheConfiguration<?, ?>> caches;
  private final Collection<ServiceCreationConfiguration<?, ?>> serviceConfigurations;
  private final ClassLoader classLoader;

  
Create a configuration builder seeded from the given configuration.

Calling build() on the returned builder will produce a functionally equivalent configuration to seed.

Params:
  • seed – configuration to duplicate
Returns:a new configuration builder
/** * Create a configuration builder seeded from the given configuration. * <p> * Calling {@link #build()} on the returned builder will produce a functionally equivalent configuration to * {@code seed}. * * @param seed configuration to duplicate * @return a new configuration builder */
protected static CoreConfigurationBuilder<?> newConfigurationBuilder(Configuration seed) { return new CoreConfigurationBuilder<>(new CoreConfigurationBuilder<>(new CoreConfigurationBuilder<>(new CoreConfigurationBuilder<>(), seed.getCacheConfigurations()), seed.getServiceCreationConfigurations()), seed.getClassLoader()); } protected CoreConfigurationBuilder() { this.caches = emptyMap(); this.serviceConfigurations = emptyList(); this.classLoader = null; } protected CoreConfigurationBuilder(CoreConfigurationBuilder<?> builder, Map<String, CacheConfiguration<?, ?>> caches) { this.caches = unmodifiableMap(caches); this.serviceConfigurations = builder.serviceConfigurations; this.classLoader = builder.classLoader; } protected CoreConfigurationBuilder(CoreConfigurationBuilder<?> builder, Collection<ServiceCreationConfiguration<?, ?>> serviceConfigurations) { this.caches = builder.caches; this.serviceConfigurations = unmodifiableCollection(serviceConfigurations); this.classLoader = builder.classLoader; } protected CoreConfigurationBuilder(CoreConfigurationBuilder<?> builder, ClassLoader classLoader) { this.caches = builder.caches; this.serviceConfigurations = builder.serviceConfigurations; this.classLoader = classLoader; } @Override public Configuration build() { return new DefaultConfiguration(caches, classLoader, serviceConfigurations.toArray(new ServiceCreationConfiguration<?, ?>[serviceConfigurations.size()])); } @Override public CacheConfiguration<?, ?> getCache(String alias) { return caches.get(alias); } @Override public B withCache(String alias, CacheConfiguration<?, ?> config) { Map<String, CacheConfiguration<?, ?>> newCaches = new HashMap<>(caches); newCaches.put(alias, config); return newBuilderWith(newCaches); } @Override public B withoutCache(String alias) { Map<String, CacheConfiguration<?, ?>> newCaches = new HashMap<>(caches); newCaches.remove(alias); return newBuilderWith(newCaches); } @Override public B updateCache(String alias, UnaryOperator<FluentCacheConfigurationBuilder<?, ?, ?>> update) { CacheConfiguration<?, ?> existing = getCache(alias); if (existing == null) { throw new IllegalArgumentException("Cache does not exist"); } else { return withCache(alias, update.apply(existing.derive()).build()); } } @Override public B updateCaches(UnaryOperator<FluentCacheConfigurationBuilder<?, ?, ?>> update) { return newBuilderWith(caches.entrySet().stream().collect( toMap(Map.Entry::getKey, e -> update.apply(e.getValue().derive()).build()) )); } @Override public <C extends ServiceCreationConfiguration<?, ?>> Collection<C> getServices(Class<C> configurationType) { return serviceConfigurations.stream().filter(service -> configurationType.isAssignableFrom(service.getClass())).map(configurationType::cast).collect(toList()); } @Override public B withService(ServiceCreationConfiguration<?, ?> config) { List<ServiceCreationConfiguration<?, ?>> newServiceConfigurations = new ArrayList<>(serviceConfigurations); newServiceConfigurations.removeIf(other -> !other.compatibleWith(config) || !config.compatibleWith(other)); newServiceConfigurations.add(config); return newBuilderWith(newServiceConfigurations); } @Override public <C extends ServiceCreationConfiguration<?, ?>> B withoutServices(Class<C> clazz, Predicate<? super C> predicate) { List<ServiceCreationConfiguration<?, ?>> newServiceConfigurations = new ArrayList<>(serviceConfigurations); newServiceConfigurations.removeIf(c -> clazz.isInstance(c) && predicate.test(clazz.cast(c))); return newBuilderWith(newServiceConfigurations); } @Override public <R, C extends ServiceCreationConfiguration<?, R>> B updateServices(Class<C> clazz, UnaryOperator<R> update) { @SuppressWarnings("unchecked") Collection<? extends ServiceCreationConfiguration<?, R>> existing = getServices(clazz); if (existing.isEmpty()) { throw new IllegalStateException("Cannot updates service configurations. No services exist"); } else { B otherBuilder = withoutServices(clazz); for (ServiceCreationConfiguration<?, R> configuration : existing) { ServiceCreationConfiguration<?, ?> replacement = configuration.build(update.apply(configuration.derive())); if (replacement == null) { throw new NullPointerException(configuration.getClass().getSimpleName() + ".build(...) returned a null configuration instance"); } else { otherBuilder = otherBuilder.withService(replacement); } } return otherBuilder; } } @Override public ClassLoader getClassLoader() { return classLoader; } @Override public B withClassLoader(ClassLoader classLoader) { return newBuilderWith(requireNonNull(classLoader)); } @Override public B withDefaultClassLoader() { return newBuilderWith((ClassLoader) null); } @SuppressWarnings("unchecked") protected B newBuilderWith(Map<String, CacheConfiguration<?,?>> caches) { if (getClass().equals(CoreConfigurationBuilder.class)) { return (B) new CoreConfigurationBuilder<>(this, caches); } else { throw new AssertionError(); } } @SuppressWarnings("unchecked") protected B newBuilderWith(Collection<ServiceCreationConfiguration<?,?>> serviceConfigurations) { if (getClass().equals(CoreConfigurationBuilder.class)) { return (B) new CoreConfigurationBuilder<>(this, serviceConfigurations); } else { throw new AssertionError(); } } @SuppressWarnings("unchecked") protected B newBuilderWith(ClassLoader classLoader) { if (getClass().equals(CoreConfigurationBuilder.class)) { return (B) new CoreConfigurationBuilder<>(this, classLoader); } else { throw new AssertionError(); } } }