/*
 * 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.ErrorHelper;
import com.queplix.core.modules.services.ejb.ScriptManager;
import com.queplix.core.utils.log.AbstractLogger;

/**
 * Service monitor class.
 *
 * @author [ALB] Baranov Andrey
 * @version $Revision: 1.1.1.1 $ $Date: 2005/09/12 15:30:59 $
 */

final class ServiceMonitor
        extends AbstractLogger {

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

    // service
    private Service service;

    // state flag
    private volatile boolean stateFlag;

    // current thread
    private Thread thread;

    // ----------------------------------------------------- Constructor

    ServiceMonitor(Service service) {

        if(service == null) {
            throw new IllegalStateException();
        }

        this.service = service;
        this.stateFlag = false;
    }

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

    /**
     * Get monitor ID
     *
     * @return service id
     */
    long getId() {
        return service.getId();
    }

    /**
     * Get service
     *
     * @return Service object
     */
    Service getService() {
        return service;
    }

    /**
     * Return true if service exists
     *
     * @return true or false
     */
    boolean isAlive() {
        return stateFlag;
    }

    /**
     * Start monitor
     *
     * @throws Exception
     */
    synchronized final void start()
            throws Exception {

        INFO("Try to start monitor. Service: " + service);

        if(stateFlag) {
            // already started
            WARN("Monitor already started. Service: " + service);

        } else {
            // run task and thread
            stateFlag = true;
            runTask();
            thread = new ServiceMonitorThread();
            thread.start();
        }
    }

    /**
     * Stop monitor.
     */
    synchronized final void stop() {

        INFO("Try to stop monitor. Service: " + service);

        if(!stateFlag) {
            // already stopped
            WARN("Monitor already stopped. Service: " + service);

        } else {
            // stop task and thread
            stateFlag = false;
            completeTask();
            synchronized(this) {
                notify();
            }
        }
    }

    /**
     * Interrupt monitor.
     *
     * @throws Exception
     */
    synchronized final void interrupt()
            throws Exception {

        INFO("Try to interrupt monitor. Service: " + service);

        if(thread == null) {
            // already interrupted
            stateFlag = false;
            WARN("Monitor already interrupted. Service: " + service);

        } else {

            // interrupt task and thread
            try {
                interruptTask();
            } catch (Throwable t) {
                ERROR(t);
            }

            // 1. try to stop
            stateFlag = false;
            synchronized(this) {
                notify();
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
            }

            // 2. kill it.
            try {
                thread.interrupt();
                INFO("Interrupted!");
            } catch (Throwable t) {
                ERROR(t);
            }

            thread = null;
        }
    }

    // ----------------------------------------------------- Public methods

    // Get string

    public String toString() {
        return service.toString();
    }

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

    /**
     * Run the task
     */
    private synchronized void runTask() {
        try {
            getScriptManager().runTask(getId(), false);
        } catch (Exception ex) {
            ERROR(ex);
        }
    }

    /**
     * Wait the task
     *
     * @return timeout to wait in millis.
     */
    private synchronized long waitTask() {
        long timout = 0;
        try {
            timout = getScriptManager().waitTask(getId(), false);
        } catch (Exception ex) {
            ERROR(ex);
        }
        return timout;
    }

    /**
     * Complete the task
     */
    private synchronized void completeTask() {
        try {
            getScriptManager().completeTask(getId(), false);
        } catch (Exception ex) {
            ERROR(ex);
        }
    }

    /**
     * Interrupt the task
     */
    private synchronized void interruptTask() {
        try {
            getScriptManager().interruptTask(getId(), false);
        } catch (Exception ex) {
            ERROR(ex);
        }
    }

    /**
     * Get ScriptManager remote interface
     *
     * @return remote interface
     */
    private ScriptManager getScriptManager() {
        return (ScriptManager) service.scriptManager;
    }

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

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

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

            try {
                while(stateFlag) {

                    // prform service
                    DEBUG("     -> thread will be performed. Service: "
                            + service);
                    service.perform();

                    if(!service.getTask().getRepeat().booleanValue()) {
                        break;
                    }

                    if(!stateFlag) {
                        break;
                    }

                    Integer delay = service.getTask().getDelay();
                    if(delay == null) {
                        throw new NullPointerException("Delay is NULL");
                    }

                    if(delay.intValue() > 0) {
                        // wait task
                        long timeout = waitTask();
                        DEBUG("     -> thread will be waited for " + timeout
                                + " ms. Service: " + service);

                        if(timeout > 0) {
                            try {
                                synchronized(this) {
                                    wait(timeout);
                                }
                            } catch (InterruptedException iex) {
                                // interrupted during Thread.wait execution
                                // try to complete
                                break;
                            }
                        }

                        // run task
                        DEBUG("     -> thread will be run again. Service: "
                                + service);
                        runTask();
                    }
                }

                // complete thread
                DEBUG("     <- thread will be stopped. Service: " + service);
                ServiceMonitor.this.stop();

            } catch (Throwable t) {

                // interrupt thread
                DEBUG("     <- thread will be interrupted. Service: "
                        + service);
                try {
                    ServiceMonitor.this.interrupt();
                } catch (Exception e) {
                }

                ErrorHelper.throwSystemException(t, ServiceMonitor.this);
            }
        }

    } // end of inner class

} // end of class
