/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (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.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * 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 com.queplix.core.modules.services;

import com.queplix.core.error.GenericSystemException;
import com.queplix.core.modules.services.ejb.ScriptManager;
import com.queplix.core.modules.services.ejb.ScriptManagerHome;
import com.queplix.core.modules.services.jxb.Task;
import com.queplix.core.tools.AbstractTool;
import com.queplix.core.utils.JNDINames;

import javax.naming.InitialContext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Class to start all service monitors.
 *
 * @author Dmitry Gaponenko [DAG]
 * @author [ALB] Baranov Andrey
 * @author [ONZ] Oleg N. Zhovtanyuk
 * @version $Revision: 1.1.1.1 $ $Date: 2005/09/12 15:30:59 $
 */

public final class ServiceRunner
        extends AbstractTool {

    // ----------------------------------------------------- Fields

    /**
     * List of running monitors.
     */
    private List monitors = Collections.synchronizedList(new ArrayList());

    // ----------------------------------------------------- Main()

    /**
     * Main method.
     *
     * @param args command line arguments
     */
    public static void main(String[] args) {

        long time = System.currentTimeMillis();
        ServiceRunner sr = new ServiceRunner();
        sr.INFO("Service runner instance is created");

        try {
            InitialContext context = new InitialContext();
            Integer runInterval = (Integer) context.lookup(
                    JNDINames.ServiceRunInterval);
            Integer checkInterval = (Integer) context.lookup(
                    JNDINames.ServiceCheckInterval);

            sr.go(runInterval, checkInterval);

        } catch (Throwable t) {
            t.printStackTrace();
            try {
                sr.ERROR(t);
            } catch (Exception _e) {
            }
            throw new GenericSystemException(t);

        } finally {
            try {
                sr.INFO("Service runner stopped...");
                sr.INFO("	life time (sec.): "
                        + (System.currentTimeMillis() - time) / 1000);
            } catch (Exception _e) {
            }
        }
    }

    // ----------------------------------------------------- API methods

    /**
     * Start the monitor.
     *
     * @param monitor service monitor
     * @throws Exception
     */
    public void startMonitor(ServiceMonitor monitor)
            throws Exception {

        monitor.start();
        monitors.add(monitor);

        INFO("Monitor " + monitor + " started.");
    }

    /**
     * Complete the monitor.
     *
     * @param monitor service monitor
     */
    public void completeMonitor(ServiceMonitor monitor) {

        monitor.stop();
        monitors.remove(monitor);

        INFO("Monitor " + monitor + " completed.");
    }

    /**
     * Interrupt the monitor.
     *
     * @param monitor service monitor
     */
    public void interruptMonitor(ServiceMonitor monitor) {

        try {
            monitor.interrupt();
        } catch (Exception ex) {
            ERROR(ex);
        }
        monitors.remove(monitor);

        INFO("Service " + monitor + " interrupted.");
    }

    // ----------------------------------------------------- Private methods

    /**
     * Manages monitors.
     *
     * @param runInterval   monitor run interval
     * @param checkInterval monitor check interval
     */
    public void go(Integer runInterval, Integer checkInterval) {

        if(runInterval == null || checkInterval == null) {
            throw new IllegalStateException();
        }

        // Register shutdown hook
        Runtime.getRuntime().addShutdownHook(new ServiceRunnerHook());

        try {

            //
            // 1. Reanimate all autostarted tasks.
            //

            // Get "should be reanimate" tasks
            Collection shouldBeReanimTasks = getScriptManager()
                    .getAutostartedTasks();

            // Reanimate.
            if(shouldBeReanimTasks != null) {
                for(Iterator it = shouldBeReanimTasks.iterator(); it.hasNext();)
                {
                    Task autostartedTask = (Task) it.next();

                    if(getLogger().isDebugEnabled()) {
                        DEBUG("Found autostarted task. Reanimate task");
                        DEBUG("    task: " + autostartedTask.getId());
                    }

                    getScriptManager().reanimateTask(
                            autostartedTask.getId().longValue());
                }
            }

            do {

                //
                // 2. Check already started monitors
                //

                // Get "should be run" tasks
                Collection shouldBeRunTasks = getScriptManager().getRunTasks();
                if(shouldBeRunTasks == null) {
                    shouldBeRunTasks = Collections.EMPTY_LIST;
                }

                // Try to find monitor in list of "should be run" tasks
                // If found - remove from list
                // If not found - interrupt monitor
                label1:
                for(int i = 0; i < monitors.size(); i++) {
                    ServiceMonitor monitor = (ServiceMonitor) monitors.get(i);

                    for(Iterator it = shouldBeRunTasks.iterator();
                        it.hasNext();) {
                        Task task = (Task) it.next();

                        if(task.getId().longValue() == monitor.getId()) {
                            // Found !!! Remove task from "should be run" list

                            if(getLogger().isDebugEnabled()) {
                                DEBUG("Check run monitor - ok");
                                DEBUG("    monitor: " + monitor);
                                DEBUG("    task: " + task.getId());
                            }

                            it.remove();
                            continue label1;
                        }
                    }

                    if(getLogger().isDebugEnabled()) {
                        DEBUG("Check run monitor - fail. Interrupt monitor");
                        DEBUG("    monitor: " + monitor);
                    }

                    if(monitor.isAlive()) {
                        // Interrupt monitor
                        interruptMonitor(monitor);
                    } else {
                        // Complete monitor
                        completeMonitor(monitor);
                    }
                }

                //
                // 3. Mark other tasks from the list as "completed"
                //

                for(Iterator it = shouldBeRunTasks.iterator(); it.hasNext();) {
                    Task obsoleteTask = (Task) it.next();

                    if(getLogger().isDebugEnabled()) {
                        DEBUG("Found obsolete task. Complete task");
                        DEBUG("    task: " + obsoleteTask.getId());
                    }

                    getScriptManager().completeTask(
                            obsoleteTask.getId().longValue(), false);
                }

                //
                // 4. Run new monitors
                //

                INFO("============================================>>");
                INFO("Try to start new services:");

                Collection maturedTasks = getScriptManager().getMaturedTasks();
                if(maturedTasks != null) {
                    for(Iterator it = maturedTasks.iterator(); it.hasNext();) {

                        // Get task
                        Task task = (Task) it.next();

                        // Creating new service
                        Service service = new Service(getLogonSession(), task);

                        if(getLogger().isDebugEnabled()) {
                            DEBUG("Prepare to start new service");
                            DEBUG("    task: " + task.getId());
                            DEBUG("    service: " + service);
                        }

                        // Creating new monitor
                        ServiceMonitor monitor = new ServiceMonitor(service);

                        try {
                            // Start monitor
                            startMonitor(monitor);

                            // Wait a bit...
                            Thread.sleep(runInterval.intValue());

                        } catch (Exception ex) {
                            ERROR("Error starting monitor " + monitor + ".",
                                    ex);
                        }
                    }
                }

                INFO("All " + monitors.size() + " services started.");
                INFO("Wait " + checkInterval.intValue() + " (ms)...");
                INFO("<<============================================");

                // Wait a bit..
                Thread.sleep(checkInterval.intValue());

            } while(true);

        } catch (Exception ex) {
            ERROR(ex);
        }

        //
        // 4. Exception!. Stop all monitors
        // @see ServiceRunnerHook

        System.exit(1);
    }

    // ----------------------------------------------------- Protected methods

    /**
     * Get ScriptManager remote interface
     *
     * @return remote interface
     */
    protected ScriptManager getScriptManager() {
        return (ScriptManager)
                getCacheObjectManager().getRemoteObject(
                        JNDINames.ScriptManagerRemote, ScriptManagerHome.class);
    }

    // ----------------------------------------------------- Inner class

    /**
     * <p>Shutdown Hook</p>
     *
     * @author [ALB] Baranov Andrey
     * @version $Revision: 1.1.1.1 $ $Date: 2005/09/12 15:30:59 $
     */
    final class ServiceRunnerHook
            extends Thread {

        /**
         * Thread runner
         */
        public void run() {

            INFO("Service runner interrupted, interrupt active monitors...");
            INFO("Interrupt active monitors. Size: " + monitors.size());

            // Killing all non-dead monitors.
            while(monitors.size() > 0) {
                ServiceMonitor monitor = (ServiceMonitor) monitors.get(0);
                interruptMonitor(monitor);
            }

            INFO("Service runner interrupted");
        }

    } // end of inner class

} // end of class
