All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.dirigible.components.terminal.endpoint.TerminalWebsocketConfig Maven / Gradle / Ivy

/*
 * Copyright (c) 2024 Eclipse Dirigible contributors
 *
 * All rights reserved. This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0
 */
package org.eclipse.dirigible.components.terminal.endpoint;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;

import org.apache.commons.io.IOUtils;
import org.eclipse.dirigible.components.base.endpoint.BaseEndpoint;
import org.eclipse.dirigible.components.terminal.endpoint.TerminalWebsocketHandler.ProcessRunnable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

/**
 * The Class TerminalWebsocketConfig.
 */
@Configuration
@EnableWebSocket
@ConditionalOnProperty(name = "terminal.enabled", havingValue = "true")
public class TerminalWebsocketConfig implements WebSocketConfigurer {

    /** The Constant TERMINAL_PREFIX. */
    private static final String TERMINAL_PREFIX = "[ws:terminal] ";

    // /** The Constant FEATURE_TERMINAL_IS_DISABLED_IN_THIS_MODE. */
    // private static final String FEATURE_TERMINAL_IS_DISABLED_IN_THIS_MODE = "Feature 'Terminal' is
    // disabled in this mode.";

    /** The Constant PERMISSIONS_FAILED. */
    private static final String PERMISSIONS_FAILED = "Failed to set permissions on file";

    /** The Constant logger. */
    private static final Logger logger = LoggerFactory.getLogger(TerminalWebsocketConfig.class);

    static {
        runTTYD();
    }

    /**
     * Register web socket handlers.
     *
     * @param registry the registry
     */
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(getConsoleWebsocketHandler(), BaseEndpoint.PREFIX_ENDPOINT_WEBSOCKETS + "ide/terminal");
    }

    /**
     * Gets the data transfer websocket handler.
     *
     * @return the data transfer websocket handler
     */
    @Bean
    public WebSocketHandler getConsoleWebsocketHandler() {
        return new TerminalWebsocketHandler();
    }

    /** The started. */
    static volatile boolean started = false;

    /**
     * Run TTYD.
     */
    public synchronized static void runTTYD() {
        if (!started) {
            // if (Configuration.isAnonymousModeEnabled()) {
            // if (logger.isWarnEnabled()) {logger.warn(TERMINAL_PREFIX +
            // FEATURE_TERMINAL_IS_DISABLED_IN_THIS_MODE);}
            // return;
            // }
            try {
                String command = "";
                String os = System.getProperty("os.name")
                                  .toLowerCase();
                if ((os.indexOf("nix") >= 0 || os.indexOf("nux") >= 0 || os.indexOf("aix") > 0)) {
                    command = "bash -c ./ttyd.sh";
                    File ttydShell = new File("./ttyd.sh");
                    if (!ttydShell.exists()) {
                        // ttyd binary should be placed in advance to $CATALINA_HOME/bin

                        createShellScript(ttydShell, "./ttyd -p 9000 -W bash");
                        if (ttydShell.setExecutable(true)) {
                            File ttydExecutable = new File("./ttyd");
                            createExecutable(TerminalWebsocketConfig.class.getResourceAsStream("/ttyd_linux.x86_64_1.6.0"), ttydExecutable);
                            if (!ttydExecutable.setExecutable(true)) {
                                if (logger.isWarnEnabled()) {
                                    logger.warn(TERMINAL_PREFIX + PERMISSIONS_FAILED);
                                }
                            }
                        } else {
                            if (logger.isWarnEnabled()) {
                                logger.warn(TERMINAL_PREFIX + PERMISSIONS_FAILED);
                            }
                        }
                    }
                } else if (os.indexOf("mac") >= 0) {
                    command = "bash -c ./ttyd.sh";
                    File ttydShell = new File("./ttyd.sh");
                    if (!ttydShell.exists()) {
                        // ttyd should be pre-installed with: brew install ttyd
                        // ProcessRunnable processRunnable = new ProcessRunnable("brew install ttyd");
                        // new Thread(processRunnable).start();
                        // processRunnable.getProcess().waitFor();

                        createShellScript(ttydShell, "ttyd -p 9000 -W bash");
                        ttydShell.setExecutable(true);
                    }
                } else if (os.indexOf("win") >= 0) {
                    logger.error("Windows is not yet supported");
                } else {
                    logger.error("Unknown OS: " + os);
                }

                ProcessRunnable processRunnable = new ProcessRunnable(command);
                new Thread(processRunnable).start();

            } catch (Exception e) {
                logger.error(TERMINAL_PREFIX + e.getMessage(), e);
            }
            started = true;
        }
    }

    /**
     * Creates the shell script.
     *
     * @param file the file
     * @param command the command
     * @throws FileNotFoundException the file not found exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private static void createShellScript(File file, String command) throws FileNotFoundException, IOException {
        try (FileOutputStream fos = new FileOutputStream(file)) {
            IOUtils.write(command, fos, Charset.defaultCharset());
        }
    }

    /**
     * Creates the executable.
     *
     * @param in the in
     * @param file the file
     * @throws FileNotFoundException the file not found exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private static void createExecutable(InputStream in, File file) throws FileNotFoundException, IOException {
        try (FileOutputStream fos = new FileOutputStream(file)) {
            IOUtils.copy(in, fos);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy