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

org.praxislive.ide.project.HubManager Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2023 Neil C Smith.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 3 only, as
 * published by the Free Software Foundation.
 *
 * This code 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
 * version 3 for more details.
 *
 * You should have received a copy of the GNU General Public License version 3
 * along with this work; if not, see http://www.gnu.org/licenses/
 *
 *
 * Please visit https://www.praxislive.org if you need additional information or
 * have any questions.
 */
package org.praxislive.ide.project;

import java.awt.EventQueue;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import org.praxislive.ide.core.api.Logging;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.swing.Timer;
import org.praxislive.hub.Hub;
import org.praxislive.ide.core.api.Task;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
import org.praxislive.core.ComponentType;
import org.praxislive.core.MainThread;
import org.praxislive.core.services.LogLevel;
import org.praxislive.core.services.RootManagerService;
import org.praxislive.core.services.ScriptService;
import org.praxislive.core.services.Service;
import org.praxislive.core.services.SystemManagerService;
import org.praxislive.hub.net.HubConfiguration;
import org.praxislive.hub.net.NetworkCoreFactory;
import org.praxislive.hub.net.ProxyInfo;
import org.praxislive.ide.core.api.AbstractTask;
import org.praxislive.ide.core.api.ExtensionContainer;
import org.praxislive.ide.project.spi.RootLifecycleHandler;
import org.praxislive.ide.core.api.SerialTasks;

/**
 *
 */
class HubManager {

    private final static Logger LOG = Logger.getLogger(HubManager.class.getName());

    static enum State {

        Stopped, Starting, Running, Stopping
    };

    private final DefaultPraxisProject project;
    private final HubProxyImpl proxy;
    private final Lookup lookup;
    private final InstanceContent lookupContent;

    private Hub hub;
    private ExtensionContainer container;
    private State state;
    private ServicesOverride servicesOverride;

    HubManager(DefaultPraxisProject project) {
        this.project = project;
        this.proxy = new HubProxyImpl(project);
        state = State.Stopped;
        lookupContent = new InstanceContent();
        lookupContent.add(proxy);
        lookupContent.add(LocalComponents.getInstance());
        lookup = new AbstractLookup(lookupContent);
    }

    Task createStartupTask() {
        return new StartUpTask(List.of(new InitHubTask()));
    }

    Task createShutdownTask() {
        var roots = servicesOverride.getKnownUserRoots();
        var description = "Shutdown"; // @TODO bundle
        var tasks = project.getLookup().lookupAll(RootLifecycleHandler.class).stream()
                .flatMap(handler -> handler.getDeletionTask(description, roots).stream())
                .collect(Collectors.toCollection(ArrayList::new));
        tasks.add(new DeinitHubTask());
        return new ShutDownTask(tasks);
    }

    State getState() {
        return state;
    }

    Lookup getLookup() {
        return lookup;
    }

    private void initHub() throws Exception {
        if (hub != null) {
            throw new IllegalStateException();
        }
        container = ExtensionContainer.create(project.getLookup());
        container.extensions().forEach(lookupContent::add);
        servicesOverride = new ServicesOverride(project);
        Logging log = Logging.create(project.getLookup());
        LogLevel logLevel = log.getLogLevel();

        var core = NetworkCoreFactory.builder()
                .childLauncher(new ChildLauncherImpl(project))
                .exposeServices(List.of(
                        RootManagerService.class,
                        SystemManagerService.class
                ))
                .hubConfiguration(HubConfiguration.builder()
                        .proxy(new Proxy())
                        .build())
                .build();

        var fakeMain = new FakeMain();

        hub = Hub.builder()
                .setCoreRootFactory(core)
                .addExtension(servicesOverride)
                .addExtension(log)
                .addExtension(container)
                .extendLookup(logLevel)
                .extendLookup(fakeMain)
                .build();
        hub.start();
    }

    private void deinitHub() {
        container.extensions().forEach(lookupContent::remove);
        hub.shutdown();
        try {
            hub.await();
        } catch (Exception ex) {
            Exceptions.printStackTrace(ex);
        }
        container = null;
        servicesOverride = null;
        hub = null;
    }

    private class StartUpTask extends SerialTasks {

        public StartUpTask(List tasks) {
            super(tasks);
        }

        @Override
        public Optional description() {
            return Optional.of("Initializing hub.");
        }

        @Override
        protected void beforeExecute() {
            if (HubManager.this.state != HubManager.State.Stopped) {
                throw new IllegalStateException();
            }
            HubManager.this.state = HubManager.State.Starting;
        }

        @Override
        protected void afterExecute() {
            if (hub != null && hub.isAlive()) {
                HubManager.this.state = HubManager.State.Running;
                proxy.start();
            } else {
                HubManager.this.state = HubManager.State.Stopped;
            }
        }

    }

    private class InitHubTask extends AbstractTask {

        private ProjectHelper helper;
        private int count;
        private Timer timer;

        @Override
        protected void handleExecute() throws Exception {
            initHub();
            helper = lookup.lookup(ProjectHelper.class);
            if (helper == null) {
                updateState(State.ERROR);
            } else {
                timer = new Timer(50, e -> checkHelper());
                timer.start();
                updateState(State.RUNNING);
            }
        }

        private void checkHelper() {
            if (helper.isConnected()) {
                timer.stop();
                updateState(State.COMPLETED);
            } else if (count++ > 10) {
                timer.stop();
                updateState(State.ERROR);
            }
        }

    }

    private class ShutDownTask extends SerialTasks {

        public ShutDownTask(List tasks) {
            super(tasks);
        }

        @Override
        protected void beforeExecute() {
            if (HubManager.this.state != HubManager.State.Running) {
                throw new IllegalStateException();
            }
            HubManager.this.state = HubManager.State.Stopping;
        }

        @Override
        protected void afterExecute() {
            if (hub != null && hub.isAlive()) {
                HubManager.this.state = HubManager.State.Running;
            } else {
                HubManager.this.state = HubManager.State.Stopped;
            }
        }
    }

    private class DeinitHubTask extends AbstractTask {

        @Override
        protected void handleExecute() throws Exception {
            try {
                proxy.stop();
            } catch (Exception ex) {
                Exceptions.printStackTrace(ex);
            }
            deinitHub();
            updateState(Task.State.COMPLETED);
        }

    }

    private static class FakeMain implements MainThread {

        @Override
        public void runLater(Runnable task) {
            // @TODO warn on first use? 
            EventQueue.invokeLater(task);
        }

        @Override
        public boolean isMainThread() {
            return EventQueue.isDispatchThread();
        }

    }

    private static class Proxy implements ProxyInfo {

        @Override
        public Optional exec() {
            return Optional.of(new Exec() {
            });
        }

        @Override
        public boolean matches(String string, ComponentType ct) {
            return !ct.toString().equals("root:gui");
        }

        @Override
        public List> services() {
            return List.of(ScriptService.class);
        }

        @Override
        public SocketAddress socketAddress() {
            return new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy