/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2016 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 http://neilcsmith.net if you need additional information or * have any questions. */ package net.neilcsmith.praxis.live.core; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; import net.neilcsmith.praxis.core.Component; import net.neilcsmith.praxis.hub.Hub; import net.neilcsmith.praxis.hub.net.MasterFactory; import net.neilcsmith.praxis.live.core.api.LogHandler; import net.neilcsmith.praxis.live.core.api.Task; import net.neilcsmith.praxis.logging.LogLevel; import org.openide.util.Exceptions; /** * * @author Neil C Smith (http://neilcsmith.net) */ public class DefaultHubManager { private final static Logger LOG = Logger.getLogger(DefaultHubManager.class.getName()); static enum State { Stopped, Starting, Running, Stopping }; private final static DefaultHubManager INSTANCE = new DefaultHubManager(); private final LocalSlaveManager localSlaves; private final Queue<Task> startupTasks; private final Queue<Task> shutdownTasks; private final PropertyChangeSupport pcs; private boolean distributed; private List<HubSlaveInfo> slaves; private Hub hub; private ExtensionContainer container; private State state; private boolean markForRestart; private RootManagerOverride rootManager; private DefaultHubManager() { state = State.Stopped; localSlaves = new LocalSlaveManager(); startupTasks = new LinkedList<>(); shutdownTasks = new LinkedList<>(); pcs = new PropertyChangeSupport(this); } public synchronized void start() { switch (state) { case Stopped: doStartup(); break; case Running: LOG.fine("start() called but already running"); return; case Starting: LOG.fine("start() called but already starting"); return; case Stopping: LOG.fine("start() called but in process of stopping. markForRestart set to true"); markForRestart = true; return; } } public synchronized void stop() { if (state == State.Stopped) { LOG.fine("stop() called but already stopped"); return; } else if (state == State.Stopping) { LOG.fine("stop() called but already stopping. markForRestart set to false"); markForRestart = false; return; } markForRestart = false; doShutdown(); } public synchronized void restart() { if (state == State.Stopped) { start(); return; } else if (state == State.Stopping) { markForRestart = true; return; } markForRestart = true; doShutdown(); } State getState() { return state; } void addPropertyChangeListener(PropertyChangeListener pl) { pcs.addPropertyChangeListener(pl); } void removePropertyChangeListener(PropertyChangeListener pl) { pcs.removePropertyChangeListener(pl); } private void doStartup() { updateState(State.Starting); distributed = HubSettings.getDefault().isDistributedHub(); if (distributed) { slaves = HubSettings.getDefault().getSlaveInfo(); } else { slaves = Collections.emptyList(); } initStartupTasks(); if (startupTasks.isEmpty()) { LOG.fine("No startup tasks found. Going straight to completeStartup"); completeStartup(); } else { nextStartupTask(); } } private void completeStartup() { if (state != State.Starting) { LOG.fine("Unexpected state in completeStartup() - stopping"); updateState(State.Stopped); return; } LOG.fine("completeStartup()"); try { initHub(); updateState(State.Running); } catch (Exception ex) { Exceptions.printStackTrace(ex); deinitHub(); updateState(State.Stopped); } markForRestart = false; } private void nextStartupTask() { Task task = startupTasks.poll(); // activeTask = task; if (task != null) { LOG.log(Level.FINE, "Executing task {0}", task.getClass()); Task.State st = task.execute(); switch (st) { case CANCELLED: LOG.log(Level.FINE, "Task cancelled - {0}", task.getClass()); cancelStartup(); break; case RUNNING: LOG.log(Level.FINE, "Task running - {0}", task.getClass()); task.addPropertyChangeListener(new TaskListener(task, true)); break; case ERROR: LOG.log(Level.WARNING, "Task error from {0}", task.getClass()); // notify error. // fall through default: nextStartupTask(); } } else { completeStartup(); } } private void cancelStartup() { startupTasks.clear(); updateState(State.Stopped); } private void doShutdown() { updateState(State.Stopping); initShutdownTasks(); if (shutdownTasks.isEmpty()) { LOG.fine("No shutdown tasks found. Going straight to completeShutdown"); completeShutdown(); } else { nextShutdownTask(); } } private void completeShutdown() { LOG.fine("completeShutdown()"); deinitHub(); updateState(State.Stopped); if (markForRestart) { LOG.fine("Restarting hub"); start(); } } private void nextShutdownTask() { Task task = shutdownTasks.poll(); // activeTask = task; if (task != null) { LOG.log(Level.FINE, "Executing task {0}", task.getClass()); Task.State st = task.execute(); switch (st) { case CANCELLED: LOG.log(Level.FINE, "Task cancelled - {0}", task.getClass()); cancelShutdown(); break; case RUNNING: LOG.log(Level.FINE, "Task running - {0}", task.getClass()); task.addPropertyChangeListener(new TaskListener(task, false)); break; case ERROR: LOG.log(Level.WARNING, "Task error from {0}", task.getClass()); // notify error. // fall through default: nextShutdownTask(); } } else { completeShutdown(); } } private void cancelShutdown() { shutdownTasks.clear(); updateState(State.Running); } private void updateState(State state) { State old = this.state; this.state = state; pcs.firePropertyChange(null, old, state); } private void initHub() throws Exception { Component[] extensions = Utils.findExtensions(); container = new ExtensionContainer(extensions); rootManager = new RootManagerOverride(); List<LogHandler> logHandlers = Utils.findLogHandlers(); Logging log = new Logging(logHandlers); LogLevel logLevel = Utils.findLogLevel(logHandlers); Hub.Builder builder = Hub.builder(); if (distributed) { builder.setCoreRootFactory(new MasterFactory(slaves)); } builder .addExtension(rootManager) .addExtension(log) .addExtension(container) .extendLookup(logLevel); hub = builder.build(); hub.start(); } private void deinitHub() { container.uninstallExtensions(); hub.shutdown(); try { hub.await(); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); } catch (ExecutionException ex) { Exceptions.printStackTrace(ex); } container = null; rootManager = null; hub = null; } private void initStartupTasks() { startupTasks.add(localSlaves.createStartupTask(slaves)); } private void initShutdownTasks() { Set<String> roots = rootManager.getKnownUserRoots(); LOG.log(Level.FINE, "Looking up handlers for {0}", Arrays.toString(roots.toArray())); String description = markForRestart ? "Hub Restart" : "Hub Shutdown"; shutdownTasks.addAll(Utils.findRootDeletionTasks(description, roots)); } private class TaskListener implements PropertyChangeListener { private final boolean startup; private final Task task; TaskListener(Task task, boolean startup) { this.task = task; this.startup = startup; } @Override public void propertyChange(PropertyChangeEvent pce) { task.removePropertyChangeListener(this); if (startup) { if (task.getState() == Task.State.CANCELLED) { cancelStartup(); } else { nextStartupTask(); } } else { if (task.getState() == Task.State.CANCELLED) { cancelShutdown(); } else { nextShutdownTask(); } } } } public static DefaultHubManager getInstance() { return INSTANCE; } }