org.springframework.ide.eclipse.boot.dash.cloudfoundry.ApplicationRunningStateTracker.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.ide.eclipse.boot.dash.cloudfoundry.ApplicationRunningStateTracker.java

Source

/*******************************************************************************
 * Copyright (c) 2015, 2016 Pivotal, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Pivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cloudfoundry;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFAppState;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFApplication;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFApplicationDetail;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFInstanceState;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFInstanceStats;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.ClientRequests;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.console.LogType;
import org.springframework.ide.eclipse.boot.dash.model.RunState;
import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens.CancelationToken;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;

public class ApplicationRunningStateTracker {
    // Give time for Diego-enabled apps with health check that may take a while to start
    // Users can always manually stop the app if it is taking too long to check the run state of the app
    public static final long APP_START_TIMEOUT = 1000 * 60 * 10;

    public static final long WAIT_TIME = 1000;

    private final ClientRequests requests;

    private final String appName;

    private final CloudFoundryBootDashModel model;

    private final long timeout;

    private final CancelationToken cancelationToken;

    private final CloudAppDashElement app;

    public ApplicationRunningStateTracker(CancelationToken cancelationToken, CloudAppDashElement app) {
        this.model = app.getCloudModel();
        this.requests = model.getClient();
        this.appName = app.getName();
        this.timeout = APP_START_TIMEOUT;
        this.cancelationToken = cancelationToken;
        this.app = app;
    }

    protected void checkTerminate(IProgressMonitor monitor) throws OperationCanceledException {
        this.app.checkTerminationRequested(cancelationToken, monitor);
    }

    /**
     * Polls cloudfoundry until app has succeeded or failed to start. Sending updates to console
     * and return the final run state.
     */
    public RunState startTracking(IProgressMonitor monitor) throws Exception, OperationCanceledException {

        // fetch an updated Cloud Application that reflects changes that
        // were
        // performed on it. Make sure the element app reference is updated
        // as
        // run state of the element depends on the app being up to date.
        // Wait for application to be started
        RunState runState = RunState.UNKNOWN;

        long currentTime = System.currentTimeMillis();
        long roughEstimateFetchStatsms = 5000;

        long totalTime = currentTime + timeout;
        String checkingMessage = "Checking if the application is running";

        int estimatedAttempts = (int) (timeout / (WAIT_TIME + roughEstimateFetchStatsms));

        monitor.beginTask(checkingMessage, estimatedAttempts);

        model.getElementConsoleManager().writeToConsole(appName, checkingMessage + ". Please wait...",
                LogType.LOCALSTDOUT);

        CFApplicationDetail app = requests.getApplication(appName);

        if (app == null) {
            throw new OperationCanceledException();
        }

        // Get the guid, as it is more efficient for lookup
        //UUID appGuid = app.getGuid();

        while (runState != RunState.RUNNING && runState != RunState.FLAPPING && runState != RunState.CRASHED
                && currentTime < totalTime) {
            int timeLeft = (int) ((totalTime - currentTime) / 1000);

            // Don't log this. Only update the monitor
            monitor.setTaskName(checkingMessage + ". Time left before timeout: " + timeLeft + 's');

            checkTerminate(monitor);

            monitor.worked(1);

            runState = getRunState(app.getInstanceDetails());
            try {
                Thread.sleep(WAIT_TIME);
            } catch (InterruptedException e) {

            }

            app = requests.getApplication(app.getName());
            // App no longer exists
            if (app == null) {
                throw new OperationCanceledException();
            }

            currentTime = System.currentTimeMillis();
        }

        if (runState != RunState.RUNNING) {
            String warning = "Timed out waiting for application - " + appName
                    + " to start. Please wait and manually refresh the target, or check if the application logs show any errors.";
            model.getElementConsoleManager().writeToConsole(appName, warning, LogType.LOCALSTDERROR);
            throw ExceptionUtil.coreException(warning);

        } else {
            model.getElementConsoleManager().writeToConsole(appName,
                    "Application appears to have started - " + appName, LogType.LOCALSTDOUT);
        }
        return runState;
    }

    public static RunState getRunState(CFInstanceState instanceState) {
        RunState runState = null;
        if (instanceState != null) {
            switch (instanceState) {
            case RUNNING:
                runState = RunState.RUNNING;
                break;
            case CRASHED:
                runState = RunState.CRASHED;
                break;
            case FLAPPING:
                runState = RunState.FLAPPING;
                break;
            case STARTING:
                runState = RunState.STARTING;
                break;
            case DOWN:
                runState = RunState.INACTIVE;
                break;
            default:
                runState = RunState.UNKNOWN;
                break;
            }
        }

        return runState;
    }

    public static RunState getRunState(CFApplication app, List<CFInstanceStats> instances) {

        RunState runState = RunState.UNKNOWN;
        // if app desired state is "Stopped", return inactive
        if ((instances == null || instances.isEmpty()) && app.getState() == CFAppState.STOPPED) {
            runState = RunState.INACTIVE;
        } else {
            runState = getRunState(instances);
        }
        return runState;
    }

    private static RunState getRunState(List<CFInstanceStats> stats) {
        RunState runState = RunState.UNKNOWN;
        if (stats != null && !stats.isEmpty()) {
            for (CFInstanceStats stat : stats) {
                RunState instanceState = getRunState(stat.getState());
                runState = instanceState != null ? runState.merge(instanceState) : null;
            }
        }
        return runState;
    }
}