<commit_before>package com.commercetools.pspadapter.payone;

import com.commercetools.pspadapter.payone.config.PayoneConfig;
import com.commercetools.pspadapter.payone.config.PropertyProvider;
import com.commercetools.pspadapter.payone.config.ServiceConfig;
import com.commercetools.pspadapter.payone.domain.ctp.CommercetoolsQueryExecutor;
import com.commercetools.pspadapter.payone.domain.ctp.CustomTypeBuilder;
import com.commercetools.pspadapter.payone.domain.ctp.TypeCacheLoader;
import com.commercetools.pspadapter.payone.domain.ctp.paymentmethods.PaymentMethod;
import com.commercetools.pspadapter.payone.domain.payone.PayonePostService;
import com.commercetools.pspadapter.payone.domain.payone.PayonePostServiceImpl;
import com.commercetools.pspadapter.payone.domain.payone.model.common.NotificationAction;
import com.commercetools.pspadapter.payone.mapping.*;
import com.commercetools.pspadapter.payone.mapping.order.DefaultPaymentToOrderStateMapper;
import com.commercetools.pspadapter.payone.mapping.order.PaymentToOrderStateMapper;
import com.commercetools.pspadapter.payone.notification.NotificationDispatcher;
import com.commercetools.pspadapter.payone.notification.NotificationProcessor;
import com.commercetools.pspadapter.payone.notification.common.*;
import com.commercetools.pspadapter.payone.transaction.PaymentMethodDispatcher;
import com.commercetools.pspadapter.payone.transaction.TransactionExecutor;
import com.commercetools.pspadapter.payone.transaction.common.DefaultChargeTransactionExecutor;
import com.commercetools.pspadapter.payone.transaction.common.UnsupportedTransactionExecutor;
import com.commercetools.pspadapter.payone.transaction.creditcard.AuthorizationTransactionExecutor;
import com.commercetools.pspadapter.payone.transaction.paymentinadvance.BankTransferInAdvanceChargeTransactionExecutor;
import com.commercetools.service.OrderService;
import com.commercetools.service.OrderServiceImpl;
import com.commercetools.service.PaymentService;
import com.commercetools.service.PaymentServiceImpl;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.sphere.sdk.client.BlockingSphereClient;
import io.sphere.sdk.client.SphereClientFactory;
import io.sphere.sdk.payments.TransactionType;
import io.sphere.sdk.types.Type;
import org.quartz.CronScheduleBuilder;
import org.quartz.SchedulerException;

import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static com.commercetools.pspadapter.payone.util.PayoneConstants.PAYONE;

/**
 * @author fhaertig
 * @since 02.12.15
 */
public class ServiceFactory {

    private static final long DEFAULT_CTP_CLIENT_TIMEOUT = 10;

    private final ServiceConfig serviceConfig;
    private final PropertyProvider propertyProvider;
    private static final String PAYONE_INTERFACE_NAME = PAYONE;

    private BlockingSphereClient blockingSphereClient = null;

    private PaymentService paymentService = null;

    private OrderService orderService = null;

    private PaymentToOrderStateMapper paymentToOrderStateMapper = null;

    private ServiceFactory(final PropertyProvider propertyProvider) {
        this.propertyProvider = propertyProvider;
        this.serviceConfig = new ServiceConfig(propertyProvider);
    }

    /**
     * Creates a new service factory initialized with a default {@link PropertyProvider}.
     * @return the new factory instance, never null
     */
    public static ServiceFactory create() {
        return ServiceFactory.withPropertiesFrom(new PropertyProvider());
    }

    /**
     * Creates a new service factory initialized with the provided {@code propertyProvider}.
     * @param propertyProvider provides the configuration parameters
     * @return the new service factory instance, never null
     */
    public static ServiceFactory withPropertiesFrom(final PropertyProvider propertyProvider) {
        return new ServiceFactory(propertyProvider);
    }

    public static void main(String [] args) throws SchedulerException, MalformedURLException {
        final PropertyProvider propertyProvider = new PropertyProvider();
        // FIXME get rid of this (by using instance methods...)
        final ServiceConfig serviceConfig = new ServiceConfig(propertyProvider);
        final PayoneConfig payoneConfig = new PayoneConfig(propertyProvider);
        final ServiceFactory serviceFactory = ServiceFactory.withPropertiesFrom(propertyProvider);
        final BlockingSphereClient commercetoolsClient = serviceFactory.getBlockingCommercetoolsClient();
        final LoadingCache<String, Type> typeCache = serviceFactory.createTypeCache(commercetoolsClient);
        final PaymentDispatcher paymentDispatcher = ServiceFactory.createPaymentDispatcher(
                typeCache,
                payoneConfig,
                serviceConfig,
                commercetoolsClient);
        final NotificationDispatcher notificationDispatcher = ServiceFactory.createNotificationDispatcher(
                serviceFactory, payoneConfig);

        final IntegrationService integrationService = ServiceFactory.createService(
                new CommercetoolsQueryExecutor(commercetoolsClient),
                paymentDispatcher,
                notificationDispatcher,
                new CustomTypeBuilder(
                        commercetoolsClient,
                        CustomTypeBuilder.PermissionToStartFromScratch.fromBoolean(
                                serviceConfig.getStartFromScratch())));

        integrationService.start();

        ScheduledJobFactory.createScheduledJob(
                CronScheduleBuilder.cronSchedule(serviceConfig.getScheduledJobCronForShortTimeFramePoll()),
                ScheduledJobShortTimeframe.class,
                integrationService,
                paymentDispatcher);

        ScheduledJobFactory.createScheduledJob(
                CronScheduleBuilder.cronSchedule(serviceConfig.getScheduledJobCronForLongTimeFramePoll()),
                ScheduledJobLongTimeframe.class,
                integrationService,
                paymentDispatcher);
    }

    /**
     * Creates a new commercetools client instance.
     * @return the client
     */
    synchronized
    public BlockingSphereClient getBlockingCommercetoolsClient() {
        if (blockingSphereClient == null) {
            final SphereClientFactory sphereClientFactory = SphereClientFactory.of();
            blockingSphereClient = BlockingSphereClient.of(
                    sphereClientFactory.createClient(
                            serviceConfig.getCtProjectKey(),
                            serviceConfig.getCtClientId(),
                            serviceConfig.getCtClientSecret()),
                    DEFAULT_CTP_CLIENT_TIMEOUT,
                    TimeUnit.SECONDS);
        }

        return blockingSphereClient;
    }

    synchronized
    public PaymentService getPaymentService() {
        if (paymentService == null) {
            paymentService = new PaymentServiceImpl(getBlockingCommercetoolsClient());
        }

        return paymentService;
    }

    synchronized
    public OrderService getOrderService() {
        if (orderService == null) {
            orderService = new OrderServiceImpl(getBlockingCommercetoolsClient());
        }

        return  orderService;
    }

    synchronized
    public PaymentToOrderStateMapper getPaymentToOrderStateMapper() {
        if (paymentToOrderStateMapper == null) {
            paymentToOrderStateMapper = new DefaultPaymentToOrderStateMapper();
        }
        return paymentToOrderStateMapper;
    }

    public String getPayoneInterfaceName() {
        return PAYONE_INTERFACE_NAME;
    }

    // FIXME return shared instance
    public LoadingCache<String, Type> createTypeCache(final BlockingSphereClient client) {
        return CacheBuilder.newBuilder().build(new TypeCacheLoader(client));
    }

    public IntegrationService createService() {
        final BlockingSphereClient client = getBlockingCommercetoolsClient();
        final LoadingCache<String, Type> typeCache = createTypeCache(client);
        final PayoneConfig payoneConfig = new PayoneConfig(propertyProvider);

        return ServiceFactory.createService(
                new CommercetoolsQueryExecutor(client),
                createPaymentDispatcher(typeCache, payoneConfig, serviceConfig, client),
                createNotificationDispatcher(this, payoneConfig),
                new CustomTypeBuilder(
                        client,
                        CustomTypeBuilder.PermissionToStartFromScratch.fromBoolean(serviceConfig.getStartFromScratch())));
    }

    // FIXME get rid of this static method
    private static IntegrationService createService(
            final CommercetoolsQueryExecutor queryExecutor,
            final PaymentDispatcher paymentDispatcher,
            final NotificationDispatcher notificationDispatcher,<commit_msg>#139: avoid synchronized access in ServiceFactory,<commit_after>package com.commercetools.pspadapter.payone;

import com.commercetools.pspadapter.payone.config.PayoneConfig;
import com.commercetools.pspadapter.payone.config.PropertyProvider;
import com.commercetools.pspadapter.payone.config.ServiceConfig;
import com.commercetools.pspadapter.payone.domain.ctp.CommercetoolsQueryExecutor;
import com.commercetools.pspadapter.payone.domain.ctp.CustomTypeBuilder;
import com.commercetools.pspadapter.payone.domain.ctp.TypeCacheLoader;
import com.commercetools.pspadapter.payone.domain.ctp.paymentmethods.PaymentMethod;
import com.commercetools.pspadapter.payone.domain.payone.PayonePostService;
import com.commercetools.pspadapter.payone.domain.payone.PayonePostServiceImpl;
import com.commercetools.pspadapter.payone.domain.payone.model.common.NotificationAction;
import com.commercetools.pspadapter.payone.mapping.*;
import com.commercetools.pspadapter.payone.mapping.order.DefaultPaymentToOrderStateMapper;
import com.commercetools.pspadapter.payone.mapping.order.PaymentToOrderStateMapper;
import com.commercetools.pspadapter.payone.notification.NotificationDispatcher;
import com.commercetools.pspadapter.payone.notification.NotificationProcessor;
import com.commercetools.pspadapter.payone.notification.common.*;
import com.commercetools.pspadapter.payone.transaction.PaymentMethodDispatcher;
import com.commercetools.pspadapter.payone.transaction.TransactionExecutor;
import com.commercetools.pspadapter.payone.transaction.common.DefaultChargeTransactionExecutor;
import com.commercetools.pspadapter.payone.transaction.common.UnsupportedTransactionExecutor;
import com.commercetools.pspadapter.payone.transaction.creditcard.AuthorizationTransactionExecutor;
import com.commercetools.pspadapter.payone.transaction.paymentinadvance.BankTransferInAdvanceChargeTransactionExecutor;
import com.commercetools.service.OrderService;
import com.commercetools.service.OrderServiceImpl;
import com.commercetools.service.PaymentService;
import com.commercetools.service.PaymentServiceImpl;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.sphere.sdk.client.BlockingSphereClient;
import io.sphere.sdk.client.SphereClientFactory;
import io.sphere.sdk.payments.TransactionType;
import io.sphere.sdk.types.Type;
import org.quartz.CronScheduleBuilder;
import org.quartz.SchedulerException;

import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static com.commercetools.pspadapter.payone.util.PayoneConstants.PAYONE;

/**
 * @author fhaertig
 * @since 02.12.15
 */
public class ServiceFactory {

    private static final long DEFAULT_CTP_CLIENT_TIMEOUT = 10;

    private static final String PAYONE_INTERFACE_NAME = PAYONE;

    private final ServiceConfig serviceConfig;
    private final PropertyProvider propertyProvider;

    private final BlockingSphereClient blockingSphereClient;

    private final PaymentService paymentService;

    private final OrderService orderService;

    private final PaymentToOrderStateMapper paymentToOrderStateMapper;

    private ServiceFactory(final PropertyProvider propertyProvider) {
        this.propertyProvider = propertyProvider;
        this.serviceConfig = new ServiceConfig(propertyProvider);

        this.blockingSphereClient = createBlockingSphereClient();
        this.paymentService = createPaymentService();
        this.orderService = createOrderService();
        this.paymentToOrderStateMapper = createPaymentToOrderStateMapper();
    }

    /**
     * Creates a new service factory initialized with a default {@link PropertyProvider}.
     * @return the new factory instance, never null
     */
    public static ServiceFactory create() {
        return ServiceFactory.withPropertiesFrom(new PropertyProvider());
    }

    /**
     * Creates a new service factory initialized with the provided {@code propertyProvider}.
     * @param propertyProvider provides the configuration parameters
     * @return the new service factory instance, never null
     */
    public static ServiceFactory withPropertiesFrom(final PropertyProvider propertyProvider) {
        return new ServiceFactory(propertyProvider);
    }

    public static void main(String [] args) throws SchedulerException, MalformedURLException {
        final PropertyProvider propertyProvider = new PropertyProvider();
        // FIXME get rid of this (by using instance methods...)
        final ServiceConfig serviceConfig = new ServiceConfig(propertyProvider);
        final PayoneConfig payoneConfig = new PayoneConfig(propertyProvider);
        final ServiceFactory serviceFactory = ServiceFactory.withPropertiesFrom(propertyProvider);
        final BlockingSphereClient commercetoolsClient = serviceFactory.getBlockingCommercetoolsClient();
        final LoadingCache<String, Type> typeCache = serviceFactory.createTypeCache(commercetoolsClient);
        final PaymentDispatcher paymentDispatcher = ServiceFactory.createPaymentDispatcher(
                typeCache,
                payoneConfig,
                serviceConfig,
                commercetoolsClient);
        final NotificationDispatcher notificationDispatcher = ServiceFactory.createNotificationDispatcher(
                serviceFactory, payoneConfig);

        final IntegrationService integrationService = ServiceFactory.createService(
                new CommercetoolsQueryExecutor(commercetoolsClient),
                paymentDispatcher,
                notificationDispatcher,
                new CustomTypeBuilder(
                        commercetoolsClient,
                        CustomTypeBuilder.PermissionToStartFromScratch.fromBoolean(
                                serviceConfig.getStartFromScratch())));

        integrationService.start();

        ScheduledJobFactory.createScheduledJob(
                CronScheduleBuilder.cronSchedule(serviceConfig.getScheduledJobCronForShortTimeFramePoll()),
                ScheduledJobShortTimeframe.class,
                integrationService,
                paymentDispatcher);

        ScheduledJobFactory.createScheduledJob(
                CronScheduleBuilder.cronSchedule(serviceConfig.getScheduledJobCronForLongTimeFramePoll()),
                ScheduledJobLongTimeframe.class,
                integrationService,
                paymentDispatcher);
    }

    protected BlockingSphereClient createBlockingSphereClient() {
        return BlockingSphereClient.of(
                SphereClientFactory.of().createClient(
                        serviceConfig.getCtProjectKey(),
                        serviceConfig.getCtClientId(),
                        serviceConfig.getCtClientSecret()),
                DEFAULT_CTP_CLIENT_TIMEOUT,
                TimeUnit.SECONDS);
    }

    protected PaymentService createPaymentService() {
        return new PaymentServiceImpl(getBlockingCommercetoolsClient());
    }

    public BlockingSphereClient getBlockingCommercetoolsClient() {
        return blockingSphereClient;
    }

    protected OrderService createOrderService() {
        return new OrderServiceImpl(getBlockingCommercetoolsClient());
    }

    protected PaymentToOrderStateMapper createPaymentToOrderStateMapper() {
        return new DefaultPaymentToOrderStateMapper();
    }

    public PaymentService getPaymentService() {
        return paymentService;
    }

    public OrderService getOrderService() {
        return  orderService;
    }

    public PaymentToOrderStateMapper getPaymentToOrderStateMapper() {
        return paymentToOrderStateMapper;
    }

    public String getPayoneInterfaceName() {
        return PAYONE_INTERFACE_NAME;
    }

    // FIXME return shared instance
    public LoadingCache<String, Type> createTypeCache(final BlockingSphereClient client) {
        return CacheBuilder.newBuilder().build(new TypeCacheLoader(client));
    }

    public IntegrationService createService() {
        final BlockingSphereClient client = getBlockingCommercetoolsClient();
        final LoadingCache<String, Type> typeCache = createTypeCache(client);
        final PayoneConfig payoneConfig = new PayoneConfig(propertyProvider);

        return ServiceFactory.createService(
                new CommercetoolsQueryExecutor(client),
                createPaymentDispatcher(typeCache, payoneConfig, serviceConfig, client),
                createNotificationDispatcher(this, payoneConfig),
                new CustomTypeBuilder(
                        client,
                        CustomTypeBuilder.PermissionToStartFromScratch.fromBoolean(serviceConfig.getStartFromScratch())));
    }

    // FIXME get rid of this static method
    private static IntegrationService createService(
            final CommercetoolsQueryExecutor queryExecutor,
            final PaymentDispatcher paymentDispatcher,
            final NotificationDispatcher notificationDispatcher,
{'fraction_non_alphanumeric': 0.0664349169563538, 'fraction_numerical': 0.0010483915466534238, 'mean_word_length': 3.7161072079104867, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3959362', 'n_tokens_mistral': 4635, 'n_tokens_neox': 4465, 'n_words': 823}