/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc.  Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * END COPYRIGHT BLOCK **/
package com.netscape.management.admserv.panel;

import java.net.*;
import com.netscape.management.client.util.*;
import com.netscape.management.client.console.ConsoleInfo;
import com.netscape.management.admserv.config.*;
import com.netscape.management.client.comm.CommRecord;
import com.netscape.management.client.comm.CommManager;
import java.io.ByteArrayInputStream;

public class RestartOperation extends AdminOperation {
    protected static int STATE_REQ_RESTART = 0;
    protected static int STATE_GET_NEWPID = 1;

    protected int _oldPID = -1, _newPID = -1;
    protected int _state;
    protected String _taskArguments = "op=restart";
    protected IRestartControl _restartControl;

    String _i18nSendRequest = _resource.getString("restart","SendRequest");
    String _i18nAccepted = _resource.getString("restart","Accepted");
    String _i18nWait = _resource.getString("restart","Wait");
    String _i18nDone = _resource.getString("restart","Done");
    String _i18nStopped = _resource.getString("restart","Stopped");

    public RestartOperation(ConsoleInfo consoleInfo) {
        super(consoleInfo, "admin-serv/tasks/Operation/Restart");
        _restartControl =
                (IRestartControl)_consoleInfo.get(IRestartControl.ID);
        createActionPanel(/*multiLined=*/true);
    }

    // Override createTask to add arguments and use custom AdmTask
    protected void createTask(ConsoleInfo ci,
            String adminURL) throws RemoteRequestException {
        try {
            _task = new TimeoutAdmTask(new URL(adminURL),
                    ci.getAuthenticationDN(),
                    ci.getAuthenticationPassword());
        } catch (MalformedURLException e) {
            Debug.println(""+e);
            throw new RemoteRequestException(e);
        }

        _task.setArguments(_taskArguments);
    }


    /**
      * Issue restart request and record pid returned in the response. Afterwards, poll
      * for pid until new pid is detected
      */
    public void performOperationalTask(ConsoleInfo ci,
            String taskURL) throws RemoteRequestException {
        _state = STATE_REQ_RESTART;
        _oldPID = _newPID = -1;
        _taskArguments = "op=restart";
        _actionPanel.setStatusText(_i18nSendRequest);
        super.performOperationalTask(ci, taskURL);
        _actionPanel.setStatusText(_i18nAccepted);

        // Read oldPID from the CGI result
        if (_task != null && _task.getResult() != null) {
            String pid = (String)_task.getResult().get("pid");
            try {
                _oldPID = Integer.parseInt(pid);
            } catch (Exception e) {}
            Debug.println("restart pid =" + pid);
        }

        if (_oldPID == -1) {
            throw new RemoteRequestException(taskURL, "No server PID in CGI response,\nRestart CGI needs to be upgraded to the latest version");
        }

        // Send the message to the Server Object that the restart is not needed any more
        if (_restartControl != null) {
            _restartControl.setNeedsAutoRestart(false);
        }

        // set new parameters for polling the server PID
        long t0 = System.currentTimeMillis();
        boolean stopDetected = false;

        _state = STATE_GET_NEWPID;
        _taskArguments = "op=getpid"; // must be set before the call to createTask
        createTask(ci, ci.getAdminURL() + _taskURL);
        ((TimeoutAdmTask)_task).setMaxWaitInterval(5000);

        _actionPanel.setStatusText(_i18nWait);
        _actionPanel.setStatusTextSameLIne("\n");

        // Poll pid, wait until server switched to a new PID
        while (_newPID == -1 || _newPID == _oldPID) {

            // Sleep 3 sec between consecutive poll requests
            try {
                Thread.currentThread().sleep(3000);
            } catch (Exception e) {}

            Debug.println("Exec " + ((TimeoutAdmTask)_task).getURL());
            _task.exec();
            processAdmTaskStatus(taskURL);

            // Try frist to detect when the server is stopped
            if (!stopDetected && _task != null && _task.getStatus() == -1 &&
                    _task.getException() != null && _task.getException()
                    instanceof java.net.ConnectException) {
                stopDetected = true;
                _actionPanel.setStatusTextSameLIne("\n" +
                        _i18nStopped + "\n");

                // If port number has changed we need to use new adminURL for poll requests
                String postRestartURL = (_restartControl == null) ? null :
                        _restartControl.getPostRestartURL();
                if (postRestartURL != null) {
                    Debug.println("post restart URL = " + postRestartURL);
                    _consoleInfo.setAdminURL(postRestartURL);
                    createTask(ci, postRestartURL + _taskURL);
                    ((TimeoutAdmTask)_task).setMaxWaitInterval(5000);
                }
            }

            // Read new PID from the CGI result
            if (_task != null && _task.getStatus() == 0 &&
                    _task.getResult() != null) {
                String pid = (String)_task.getResult().get("pid");
                try {
                    _newPID = Integer.parseInt(pid);
                } catch (Exception e) {}
                Debug.println("getpid pid =" + pid + " old pid = " +
                        _oldPID);
            }
            _actionPanel.setStatusTextSameLIne("*");

            // Start a new line if after 90 sec restart not detected
            if ((System.currentTimeMillis() - t0) > 90000) {
                t0 = System.currentTimeMillis();
                _actionPanel.setStatusTextSameLIne("\n");
            }
        }
        _actionPanel.setStatusText(_i18nDone + "\n");

        Debug.println("Operation time = " +
                (System.currentTimeMillis() - t0) / 1000.);

    }


    // Override processAdmTaskStatus() to handle errors during pid polling
    public void processAdmTaskStatus(String url)
            throws RemoteRequestException {
        Debug.println("status="+_task.getStatus() + " ex=" +
                _task.getException() + " result="+_task.getResult());
        int status = _task.getStatus();

        if (_state == STATE_GET_NEWPID) {
            if (status == -1) {
                // We can tolerate this errors as the server might be just going up or down
                Debug.println("getpid request error " +
                        _task.getException());
                return;
            } else if (status == -2) {
                Debug.println("getpid request timedout");
                // Timeout occured
                return;
            }
        }
        super.processAdmTaskStatus(url);
    }
}


class TimeoutAdmTask extends AdmTask {

    private long _maxWait;
    private CommRecord _commRecord;

    public TimeoutAdmTask(URL url, String userID, String userPassword) {
        super(url, userID, userPassword);
    }

    public void setMaxWaitInterval(long maxWait) {
        _maxWait = maxWait;
    }


    public String getURL() {
        return _adminURL.toString();
    }

    // Override waitForFinish() to limit wait time
    public synchronized void waitForFinish() {
        while (!_finished) {
            try {
                if (_maxWait == 0) {
                    wait();
                } else {
                    long t0 = System.currentTimeMillis();
                    Debug.println("wait " + _maxWait);
                    wait(_maxWait);
                    if (!_finished) {
                        _error = -2; //REQUEST_TIMEDOUT;
                        if (_response != null)
                            _response.clear();
                        _httpManager.terminate(_commRecord);
                        return;
                    }
                }
            } catch (Exception e) {}
        }
    }

    // Override exec() to record CommRecord. Need it for terminate() on httpmanager
    public int exec() {
        _responseString = new StringBuffer();
        ByteArrayInputStream data = null;
        try {
            Debug.println("exec " + _adminURL + " args:"+ _argumentString);

            if (_response != null)
                _response.clear();
            _finished = false;
            _exception = null;
            _error = 0;

            data = new ByteArrayInputStream(_argumentString.getBytes());
            if (data == null) {
                _commRecord = _httpManager.post(_adminURL, this, null, null,
                        0, CommManager.FORCE_BASIC_AUTH);
            } else {
                _commRecord = _httpManager.post(_adminURL, this, null, data,
                        data.available(), CommManager.FORCE_BASIC_AUTH);
            }

            waitForFinish();
            return(0);
        } catch (Exception e) {
            Debug.println(""+e);
            _exception = e;
            _error = -1;
            return -1;
        }
    }
}
