/**
 * Copyright (c) 2007-2012 EBM WebSourcing, 2012-2018 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.bc.ftp;

import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.ow2.petals.bc.ftp.connection.FTPConnectionInfo;
import org.ow2.petals.bc.ftp.connection.FTPConnectionInfoBuilder;
import org.ow2.petals.bc.ftp.connection.WrappedFTPClient;
import org.ow2.petals.bc.ftp.connection.WrappedFTPClientFactory;
import org.ow2.petals.component.framework.AbstractComponent;
import org.ow2.petals.component.framework.api.configuration.SuConfigurationParameters;
import org.ow2.petals.component.framework.api.exception.PEtALSCDKException;
import org.ow2.petals.component.framework.jbidescriptor.generated.Provides;
import org.ow2.petals.component.framework.su.AbstractServiceUnitManager;
import org.ow2.petals.component.framework.su.ServiceUnitDataHandler;

/**
 * @author Mathieu CARROLLE - EBM WebSourcing
 */
public class FtpSuManager extends AbstractServiceUnitManager {

    public FtpSuManager(final AbstractComponent component) {
        super(component);
    }

    @Override
    protected void doDeploy(final ServiceUnitDataHandler suDH) throws PEtALSCDKException {
        final Provides provides = suDH.getDescriptor().getServices().getProvides().get(0);
        final SuConfigurationParameters extensions = suDH.getConfigurationExtensions(provides);

        try {
            final GenericObjectPool<WrappedFTPClient> ftpConnectionPool = this.buildFtpConnectionPool(provides,
                    extensions);
            ((FTPComponent) getComponent()).getMapOfPool().put(provides, ftpConnectionPool);
        } catch (final MissingElementException e) {
            throw new PEtALSCDKException(e);
        }
    }

    @Override
    protected void doUndeploy(final ServiceUnitDataHandler suDH) throws PEtALSCDKException {
        final Provides provides = suDH.getDescriptor().getServices().getProvides().get(0);
        final ObjectPool<WrappedFTPClient> pool = ((FTPComponent) getComponent()).getMapOfPool().get(provides);

        final PEtALSCDKException ex = new PEtALSCDKException("Error during undeploy");

        try {
            this.logger.fine("Closing pool's resource");
            pool.clear();
            pool.close();
        } catch (Exception e) {
            ex.addSuppressed(e);
        }

        ex.throwIfNeeded();
    }

    /**
     * On placeholder values reload, we must adjust FTP connection pools
     */
    @Override
    protected void onPlaceHolderValuesReloaded() {
        super.onPlaceHolderValuesReloaded();

        for (final Provides provides : ((FTPComponent) getComponent()).getMapOfPool().keySet()) {
            // First, build a new FTP connection pool that could be used when closing the previous one
            // Second, close the previous FTP connection pool to free its resources
            final GenericObjectPool<WrappedFTPClient> oldFftpConnectionPool = ((FTPComponent) getComponent())
                    .getMapOfPool().get(provides);
            if (oldFftpConnectionPool != null) {
                this.logger.fine(String.format("Rebuilding FTP connection pool's resources for '%s/%s-%s'",
                        provides.getInterfaceName().toString(), provides.getServiceName().toString(),
                        provides.getEndpointName()));
                try {
                    final GenericObjectPool<WrappedFTPClient> newFtpConnectionPool = this.buildFtpConnectionPool(
                            provides, this.getSUDataHandler(provides).getConfigurationExtensions(provides));
                    ((FTPComponent) getComponent()).getMapOfPool().put(provides, newFtpConnectionPool);
                } catch (final MissingElementException e) {
                    this.logger.warning(String.format(
                            "A configuration error occurs rebuilding FTP connection pool for '%s/%s-%s'. Check updated placeholder values.",
                            provides.getInterfaceName().toString(), provides.getServiceName().toString(),
                            provides.getEndpointName()));
                }

                this.logger.fine(String.format("Closing FTP connection pool's resources for '%s/%s-%s'",
                        provides.getInterfaceName().toString(), provides.getServiceName().toString(),
                        provides.getEndpointName()));
                oldFftpConnectionPool.close();
            } else {
                this.logger.warning(String.format(
                        "Unable to rebuilt FTP connection pool for '%s/%s-%s'. Perhaps it was undeploy concurrently to the placeholder reloading or a previous placeholder reloading failed.",
                        provides.getInterfaceName().toString(), provides.getServiceName().toString(),
                        provides.getEndpointName()));
            }
        }
    }

    private final GenericObjectPool<WrappedFTPClient> buildFtpConnectionPool(final Provides provides,
            final SuConfigurationParameters extensions) throws MissingElementException {

        final FTPConnectionInfo connectionConfiguration = FTPConnectionInfoBuilder.buildFTPConnectionInfo(extensions,
                provides.getRetrypolicy());
        final WrappedFTPClientFactory factory = new WrappedFTPClientFactory(connectionConfiguration, this.logger);
        return new GenericObjectPool<>(factory, this.configurePool(connectionConfiguration));

    }

    /**
     * Configure the pool
     * 
     * @param connectionConfiguration
     * @param pool
     * @return
     */
    private GenericObjectPoolConfig configurePool(final FTPConnectionInfo connectionConfiguration) {
        final GenericObjectPoolConfig conf = new GenericObjectPoolConfig();
        int maxConnection = connectionConfiguration.getMaxConnection();
        if (maxConnection <= 0) {
            conf.setBlockWhenExhausted(false);
        } else {
            this.logger.config("Number of maximum connection for the pool: " + maxConnection);
            // On borrow action, we block until an object is free
            conf.setBlockWhenExhausted(true);
            // Wait is unlimited;
            conf.setMaxWaitMillis(-1);
            conf.setMaxTotal(maxConnection);
            conf.setMaxIdle(maxConnection);
        }
        // Minimun time before an object become eligible to eviction
        conf.setMinEvictableIdleTimeMillis(connectionConfiguration.getMaxIdleTime());
        conf.setTimeBetweenEvictionRunsMillis(connectionConfiguration.getMaxIdleTime() / 2);
        conf.setTestOnBorrow(true);
        return conf;
    }
}
