MetalogFactoryImpl.java

package io.github.jonloucks.metalog.impl;


import io.github.jonloucks.concurrency.api.*;
import io.github.jonloucks.contracts.api.*;
import io.github.jonloucks.metalog.api.*;

import java.util.Optional;
import java.util.function.Consumer;

import static io.github.jonloucks.concurrency.api.GlobalConcurrency.findConcurrencyFactory;
import static io.github.jonloucks.contracts.api.BindStrategy.IF_ALLOWED;
import static io.github.jonloucks.contracts.api.BindStrategy.IF_NOT_BOUND;
import static io.github.jonloucks.contracts.api.Checks.*;
import static io.github.jonloucks.contracts.api.GlobalContracts.lifeCycle;

/**
 * Creates Metalog instances
 * Opt-in construction via reflection, ServiceLoader or directly.
 */
public final class MetalogFactoryImpl implements MetalogFactory {
    
    /**
     * Publicly accessible constructor as an entry point into this library.
     * It can be invoked via reflection, ServiceLoader or directly.
     */
    public MetalogFactoryImpl() {
    }
    
    @Override
    public Metalog create(Metalog.Config config) {
        final Metalog.Config validConfig = enhancedConfigCheck(config);
        final Repository repository = validConfig.contracts().claim(Repository.FACTORY).get();
        
        installConcurrency(validConfig, repository);
        installCore(validConfig, repository);
        
        final MetalogImpl metalog = new MetalogImpl(validConfig, repository, true);
        repository.keep(Metalog.CONTRACT, () -> metalog, IF_ALLOWED);
        return metalog;
    }
    
    @Override
    public Metalog create(Consumer<Metalog.Config.Builder> builderConsumer) {
        final Consumer<Metalog.Config.Builder> validBuilderConsumer = builderConsumerCheck(builderConsumer);
        final ConfigBuilderImpl builder = new ConfigBuilderImpl();
        
        validBuilderConsumer.accept(builder);
        
        return create(builder);
    }
    
    @Override
    public void install(Metalog.Config config, Repository repository) {
        final Metalog.Config validConfig = enhancedConfigCheck(config);
        final Repository validRepository = nullCheck(repository, "Repository must be present.");
        
        installConcurrency(validConfig, validRepository);
        installCore(validConfig, validRepository);
        
        final Promisor<Metalog> metalogPromisor = lifeCycle(() -> new MetalogImpl(validConfig, validRepository, false));
        
        validRepository.keep(Metalog.CONTRACT, metalogPromisor, IF_ALLOWED);
    }
    
    private void installConcurrency(Metalog.Config config, Repository repository) {
        final Concurrency.Config concurrencyConfig = new Concurrency.Config() {
            @Override
            public Contracts contracts() {
                return config.contracts();
            }
        };
        //noinspection ResultOfMethodCallIgnored
        contractsCheck(concurrencyConfig.contracts());
        final Optional<ConcurrencyFactory> optionalFactory = findConcurrencyFactory(concurrencyConfig);
        
        optionalFactory.ifPresent(f -> f.install(concurrencyConfig, repository));
    }
    
    private Metalog.Config enhancedConfigCheck(Metalog.Config config) {
        final Metalog.Config candidateConfig = configCheck(config);
        //noinspection ResultOfMethodCallIgnored
        contractsCheck(candidateConfig.contracts());
        return candidateConfig;
    }
    
    private void installCore(Metalog.Config config, Repository repository) {
        repository.require(Repository.FACTORY);
        repository.require(WaitableFactory.CONTRACT);
        repository.require(StateMachineFactory.CONTRACT);
        
        final BindStrategy strategy = IF_NOT_BOUND;
        
        repository.keep(Metalog.Config.Builder.FACTORY, () -> ConfigBuilderImpl::new, strategy);
        repository.keep(Entities.Builder.FACTORY, () -> EntitiesImpl::new, strategy);
        repository.keep(Entity.Builder.FACTORY, () -> EntityImpl::new, strategy);
        repository.keep(Meta.Builder.FACTORY, () -> MetaImpl::new, strategy);
        
        repository.keep(MetalogFactory.CONTRACT, lifeCycle(MetalogFactoryImpl::new), strategy);
        repository.keep(Dispatcher.KEYED_FACTORY, () -> () -> new KeyedDispatcherImpl(config), strategy);
        repository.keep(Dispatcher.UNKEYED_FACTORY, () -> ()-> new UnkeyedDispatcherImpl(config), strategy);
        repository.keep(MainDispatcher.CONTRACT, lifeCycle(() -> new MainDispatcherImpl(config, repository)), strategy);
        repository.keep(Console.CONTRACT, lifeCycle(() -> new ConsoleImpl(config)), strategy);
    }
}