/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2012 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.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import net.neilcsmith.praxis.core.Call; import net.neilcsmith.praxis.core.CallArguments; import net.neilcsmith.praxis.core.ComponentAddress; import net.neilcsmith.praxis.core.ControlAddress; import net.neilcsmith.praxis.core.PacketRouter; import net.neilcsmith.praxis.core.info.ControlInfo; import net.neilcsmith.praxis.core.interfaces.RootManagerService; import net.neilcsmith.praxis.core.interfaces.ServiceManager; import net.neilcsmith.praxis.core.interfaces.ServiceUnavailableException; import net.neilcsmith.praxis.core.interfaces.SystemManagerService; import net.neilcsmith.praxis.impl.AbstractAsyncControl; import net.neilcsmith.praxis.impl.AbstractControl; import net.neilcsmith.praxis.impl.AbstractSwingRoot; import net.neilcsmith.praxis.impl.SimpleControl; import net.neilcsmith.praxis.live.core.api.Task; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.util.Exceptions; /** * * @author Neil C Smith <http://neilcsmith.net> */ class RootManagerOverride extends AbstractSwingRoot { private final static Logger LOG = Logger.getLogger(RootManagerOverride.class.getName()); private ComponentAddress defaultService; private Set<String> knownRoots; RootManagerOverride() { super(EnumSet.of(Caps.Component)); registerControl(RootManagerService.ADD_ROOT, new AddRootControl()); registerControl(RootManagerService.REMOVE_ROOT, new RemoveRootControl()); registerControl(RootManagerService.ROOTS, new RootsControl()); registerInterface(RootManagerService.INSTANCE); registerControl(SystemManagerService.SYSTEM_EXIT, new ExitControl()); registerInterface(SystemManagerService.INSTANCE); knownRoots = new LinkedHashSet<String>(); } Set<String> getKnownUserRoots() { return knownRoots; } private ComponentAddress getDefaultServiceAddress() throws ServiceUnavailableException { if (defaultService == null) { ServiceManager manager = getLookup().get(ServiceManager.class); if (manager == null) { throw new ServiceUnavailableException(); } ComponentAddress[] services = manager.findAllServices(RootManagerService.INSTANCE); defaultService = services[services.length - 1]; } return defaultService; } private class AddRootControl extends AbstractAsyncControl { @Override protected Call processInvoke(Call call) throws Exception { ControlAddress to = ControlAddress.create(getDefaultServiceAddress(), RootManagerService.ADD_ROOT); return Call.createCall(to, getAddress(), call.getTimecode(), call.getArgs()); } @Override protected Call processResponse(Call call) throws Exception { if (call.getType() == Call.Type.RETURN) { // sucessfully added Call active = getActiveCall(); knownRoots.add(active.getArgs().get(0).toString()); } return Call.createReturnCall(getActiveCall(), call.getArgs()); } @Override public ControlInfo getInfo() { return RootManagerService.ADD_ROOT_INFO; } } private class RootsControl extends AbstractAsyncControl { @Override protected Call processInvoke(Call call) throws Exception { ControlAddress to = ControlAddress.create(getDefaultServiceAddress(), RootManagerService.ROOTS); return Call.createCall(to, getAddress(), call.getTimecode(), call.getArgs()); } @Override protected Call processResponse(Call call) throws Exception { return Call.createReturnCall(getActiveCall(), call.getArgs()); } @Override public ControlInfo getInfo() { return RootManagerService.ROOTS_INFO; } } // private class RemoveRootControl extends AbstractAsyncControl { // // @Override // protected Call processInvoke(final Call call) throws Exception { // // Object ret = DialogDisplayer.getDefault().notify( // new NotifyDescriptor.Confirmation("Remove root " + call.getArgs().get(0).toString())); // if (ret == NotifyDescriptor.YES_OPTION) { // ControlAddress to = ControlAddress.create(getDefaultServiceAddress(), RootManagerService.REMOVE_ROOT); // return Call.createCall(to, getAddress(), call.getTimecode(), call.getArgs()); // } else { // return Call.createErrorCall(call, CallArguments.EMPTY); // } // // } // // @Override // protected Call processResponse(Call call) throws Exception { // if (call.getType() == Call.Type.RETURN) { // // sucessfully added // Call active = getActiveCall(); // knownRoots.remove(active.getArgs().get(0).toString()); // } // return Call.createReturnCall(getActiveCall(), call.getArgs()); // } // // @Override // public ControlInfo getInfo() { // return RootManagerService.REMOVE_ROOT_INFO; // } // } private class RemoveRootControl extends AbstractControl { private Map<String, List<Call>> pending; private Map<Integer, String> forwarded; private RemoveRootControl() { pending = new HashMap<String, List<Call>>(); forwarded = new HashMap<Integer, String>(); } @Override public void call(Call call, PacketRouter router) throws Exception { switch (call.getType()) { case INVOKE: case INVOKE_QUIET: processInvoke(call, router); break; case RETURN: processResponse(call, router, false); break; default: processResponse(call, router, true); break; } } private void processInvoke(Call call, PacketRouter router) throws Exception { String rootID = call.getArgs().get(0).toString(); List<Call> calls = pending.get(rootID); if (calls == null) { LOG.log(Level.FINE, "No pending calls found for root removal /{0}", rootID); List<Task> tasks = Utils.findRootDeletionTasks("Deleting /" + rootID, Collections.singleton(rootID)); if (tasks.isEmpty()) { LOG.log(Level.FINE, "No tasks found for root removal /{0}", rootID); Object ret = DialogDisplayer.getDefault().notify( new NotifyDescriptor.Confirmation("Remove root " + call.getArgs().get(0).toString(), "Remove Root?", NotifyDescriptor.YES_NO_OPTION)); if (ret == NotifyDescriptor.YES_OPTION) { forwardCall(rootID, call, router); } else { router.route(Call.createErrorCall(call, CallArguments.EMPTY)); return; // don't allow call to be added to pending } } else { if (tasks.size() > 1) { LOG.log(Level.WARNING, "More than one deletion task for root /{0}\nOnly first task will be run", rootID); } LOG.log(Level.FINE, "Starting root deletion task"); Task task = tasks.get(0); task.execute(); if (task.getState() == Task.State.RUNNING) { LOG.log(Level.FINE, "Task still running - add PCL"); task.addPropertyChangeListener(new TaskListener(task, rootID)); } else if (task.getState() == Task.State.COMPLETED) { LOG.log(Level.FINE, "Task completed synchronously - forwarding call"); forwardCall(rootID, call, router); } else { LOG.log(Level.FINE, "Synchronous task error"); router.route(Call.createErrorCall(call, CallArguments.EMPTY)); return; // don't allow call to be added to pending } } calls = new ArrayList<Call>(1); calls.add(call); pending.put(rootID, calls); } else { LOG.log(Level.FINE, "Pending call found for root removal /{0}", rootID); calls.add(call); } } private void forwardCallFromTask(String rootID) throws Exception { LOG.log(Level.FINE, "Task Completed OK - forwarding call to remove /{0}", rootID); List<Call> inbound = pending.get(rootID); PacketRouter router = getPacketRouter(); forwardCall(rootID, inbound.get(0), router); } private void forwardCall(String rootID, Call call, PacketRouter router) throws Exception { ControlAddress to = ControlAddress.create(getDefaultServiceAddress(), RootManagerService.REMOVE_ROOT); Call forward = Call.createCall(to, getAddress(), call.getTimecode(), call.getArgs()); forwarded.put(forward.getMatchID(), rootID); router.route(forward); } private void processResponse(Call call, PacketRouter router, boolean error) { String rootID = forwarded.remove(call.getMatchID()); List<Call> inbound = pending.remove(rootID); for (Call in : inbound) { Call response; if (error) { response = Call.createErrorCall(in, call.getArgs()); } else { response = Call.createReturnCall(in, call.getArgs()); } router.route(response); } } private void taskError(String rootID) { List<Call> inbound = pending.remove(rootID); PacketRouter router = getPacketRouter(); for (Call in : inbound) { router.route(Call.createErrorCall(in, CallArguments.EMPTY)); } } @Override public ControlInfo getInfo() { return RootManagerService.REMOVE_ROOT_INFO; } private class TaskListener implements PropertyChangeListener { private String rootID; private Task task; private TaskListener(Task task, String rootID) { this.task = task; this.rootID = rootID; } @Override public void propertyChange(PropertyChangeEvent pce) { if (task.getState() == Task.State.COMPLETED) { try { forwardCallFromTask(rootID); return; } catch (Exception ex) { Exceptions.printStackTrace(ex); } } taskError(rootID); } } } private class ExitControl extends AbstractControl { @Override public void call(Call call, PacketRouter router) throws Exception { if (call.getType() == Call.Type.INVOKE || call.getType() == Call.Type.INVOKE_QUIET) { for (String id : knownRoots) { ControlAddress to = ControlAddress.create("/" + id + ".stop"); Call msg = Call.createQuietCall(to, getAddress(), getTime(), CallArguments.EMPTY); getPacketRouter().route(msg); } } else { // no op? } } @Override public ControlInfo getInfo() { return SystemManagerService.SYSTEM_EXIT_INFO; } } }