package org.apache.archiva.web.startup;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import org.apache.archiva.common.ArchivaException;
import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
import org.apache.archiva.redback.components.scheduler.DefaultScheduler;
import org.apache.archiva.scheduler.repository.DefaultRepositoryArchivaTaskScheduler;
import org.apache.maven.index.NexusIndexer;
import org.apache.maven.index.context.IndexingContext;
import org.apache.archiva.redback.components.taskqueue.Task;
import org.apache.archiva.redback.components.taskqueue.execution.ThreadedTaskQueueExecutor;
import org.quartz.SchedulerException;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.lang.reflect.Field;
import java.util.Properties;
import java.util.concurrent.ExecutorService;

/**
 * ArchivaStartup - the startup of all archiva features in a deterministic order.
 */
public class ArchivaStartup
    implements ServletContextListener
{
    private ThreadedTaskQueueExecutor tqeDbScanning;

    private ThreadedTaskQueueExecutor tqeRepoScanning;

    private ThreadedTaskQueueExecutor tqeIndexing;

    private DefaultRepositoryArchivaTaskScheduler repositoryTaskScheduler;

    private PlexusSisuBridge plexusSisuBridge;

    private NexusIndexer nexusIndexer;

    @Override
    public void contextInitialized( ServletContextEvent contextEvent )
    {
        WebApplicationContext wac =
            WebApplicationContextUtils.getRequiredWebApplicationContext( contextEvent.getServletContext() );

        SecuritySynchronization securitySync = wac.getBean( SecuritySynchronization.class );

        repositoryTaskScheduler =
            wac.getBean( "archivaTaskScheduler#repository", DefaultRepositoryArchivaTaskScheduler.class );

        Properties archivaRuntimeProperties = wac.getBean( "archivaRuntimeProperties", Properties.class );

        tqeRepoScanning = wac.getBean( "taskQueueExecutor#repository-scanning", ThreadedTaskQueueExecutor.class );

        tqeIndexing = wac.getBean( "taskQueueExecutor#indexing", ThreadedTaskQueueExecutor.class );

        plexusSisuBridge = wac.getBean( PlexusSisuBridge.class );

        try
        {
            nexusIndexer = plexusSisuBridge.lookup( NexusIndexer.class );
        }
        catch ( PlexusSisuBridgeException e )
        {
            throw new RuntimeException( "Unable to get NexusIndexer: " + e.getMessage(), e );
        }
        try
        {
            securitySync.startup();
            repositoryTaskScheduler.startup();
            Banner.display( (String) archivaRuntimeProperties.get( "archiva.version" ) );
        }
        catch ( ArchivaException e )
        {
            throw new RuntimeException( "Unable to properly startup archiva: " + e.getMessage(), e );
        }
    }

    @Override
    public void contextDestroyed( ServletContextEvent contextEvent )
    {
        WebApplicationContext applicationContext =
            WebApplicationContextUtils.getRequiredWebApplicationContext( contextEvent.getServletContext() );

        // we log using servlet mechanism as due to some possible problem with slf4j when container shutdown
        // so servletContext.log
        ServletContext servletContext = contextEvent.getServletContext();

        // TODO check this stop

        /*
        if ( applicationContext != null && applicationContext instanceof ClassPathXmlApplicationContext )
        {
            ( (ClassPathXmlApplicationContext) applicationContext ).close();
        } */

        if ( applicationContext != null ) //&& applicationContext instanceof PlexusWebApplicationContext )
        {
            // stop task queue executors
            stopTaskQueueExecutor( tqeDbScanning, servletContext );
            stopTaskQueueExecutor( tqeRepoScanning, servletContext );
            stopTaskQueueExecutor( tqeIndexing, servletContext );

            // stop the DefaultArchivaTaskScheduler and its scheduler
            if ( repositoryTaskScheduler != null )
            {
                try
                {
                    repositoryTaskScheduler.stop();
                }
                catch ( SchedulerException e )
                {
                    servletContext.log( e.getMessage(), e );
                }

                try
                {
                    // shutdown the scheduler, otherwise Quartz scheduler and Threads still exists
                    Field schedulerField = repositoryTaskScheduler.getClass().getDeclaredField( "scheduler" );
                    schedulerField.setAccessible( true );

                    DefaultScheduler scheduler = (DefaultScheduler) schedulerField.get( repositoryTaskScheduler );
                    scheduler.stop();
                }
                catch ( Exception e )
                {
                    servletContext.log( e.getMessage(), e );
                }
            }

            // close the application context
            //applicationContext.close();
            // TODO fix close call
            //applicationContext.
        }

        // closing correctly indexer to close correctly lock and file
        for ( IndexingContext indexingContext : nexusIndexer.getIndexingContexts().values() )
        {
            try
            {
                indexingContext.close( false );
            }
            catch ( Exception e )
            {
                servletContext.log( "skip error closing indexingContext " + e.getMessage(), e );
            }
        }

    }

    private void stopTaskQueueExecutor( ThreadedTaskQueueExecutor taskQueueExecutor, ServletContext servletContext )
    {
        if ( taskQueueExecutor != null )
        {
            Task currentTask = taskQueueExecutor.getCurrentTask();
            if ( currentTask != null )
            {
                taskQueueExecutor.cancelTask( currentTask );
            }

            try
            {
                taskQueueExecutor.stop();
                ExecutorService service = getExecutorServiceForTTQE( taskQueueExecutor, servletContext );
                if ( service != null )
                {
                    service.shutdown();
                }
            }
            catch ( Exception e )
            {
                servletContext.log( e.getMessage(), e );
            }
        }
    }

    private ExecutorService getExecutorServiceForTTQE( ThreadedTaskQueueExecutor ttqe, ServletContext servletContext )
    {
        ExecutorService service = null;
        try
        {
            Field executorServiceField = ttqe.getClass().getDeclaredField( "executorService" );
            executorServiceField.setAccessible( true );
            service = (ExecutorService) executorServiceField.get( ttqe );
        }
        catch ( Exception e )
        {
            servletContext.log( e.getMessage(), e );
        }
        return service;
    }
}
