/* * Created on 25.10.2003 * */ package alma.acs.commandcenter.app; import java.awt.Rectangle; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import javax.help.HelpSet; import javax.help.HelpSetException; import org.exolab.castor.xml.MarshalException; import org.exolab.castor.xml.ValidationException; import org.omg.CORBA.ORB; import alma.acs.commandcenter.engine.ExecuteAcs; import alma.acs.commandcenter.engine.ExecuteContainer; import alma.acs.commandcenter.engine.ExecuteManager; import alma.acs.commandcenter.engine.ExecuteServices; import alma.acs.commandcenter.engine.ExecuteTools; import alma.acs.commandcenter.engine.Executor; import alma.acs.commandcenter.engine.ToolManager; import alma.acs.commandcenter.engine.ExecuteTools.ToolStarter; import alma.acs.commandcenter.gui.CommandCenterGui; import alma.acs.commandcenter.gui.DeploymentTreeController; import alma.acs.commandcenter.meta.Firestarter; import alma.acs.commandcenter.meta.GuiMaciSupervisor; import alma.acs.commandcenter.meta.IMaciSupervisor; import alma.acs.commandcenter.meta.Firestarter.OrbInitException; import alma.acs.commandcenter.util.MiscUtils; import alma.acs.logging.AcsLogger; import alma.acs.util.ACSPorts; import alma.entity.xmlbinding.acscommandcenterproject.AcsCommandCenterProject; import alma.entity.xmlbinding.acscommandcenterproject.ContainerT; import alma.entity.xmlbinding.acscommandcentertools.AcsCommandCenterTools; import alma.entity.xmlbinding.acscommandcentertools.Tool; /** * The business logic for Command Center. * */ public class CommandCenterLogic { // // ============ Members / Delegates ==================== // public AcsCommandCenterProject project; public MyProjectRunModel model; public StartupOptions startupOptions; public ExecuteServices executeServices; public ExecuteManager executeManager; public ExecuteContainer executeContainer; public ExecuteAcs executeAcs; public ExecuteTools executeTools; public Firestarter firestarter; protected CommandCenterGui gui; protected ProjectMaker projectMaker; protected AcsLogger log; // // ============ Startup / Shutdown ==================== // public void prepare (StartupOptions startupOptions) { this.startupOptions = startupOptions; log = MiscUtils.getPackageLogger(this); // Make up the creator string for command center projects. projectCreatorId = (version().equals(""))? null : "acc-"+version(); projectMaker = new ProjectMaker(projectCreatorId); project = projectMaker.createProject(); model = new MyProjectRunModel(project); executeServices = new ExecuteServices(model); executeManager = new ExecuteManager(model); executeContainer = new ExecuteContainer(); executeAcs = new ExecuteAcs(model); executeTools = new ExecuteTools(model); firestarter = new Firestarter("AcsCommandCenter", log, null); Executor.remoteDaemonEnable(firestarter); // msc (2007-11): needed for talking to daemons deploymentTreeControllerImpl = new DeploymentTreeControllerImpl(); gui = new CommandCenterGui(this); gui.prepare(); // --- read the built-in tools try { URL url = findResource(ToolManager.getDefaultBuiltinToolsName(), ""); loadBuiltinTools(url); } catch (Exception exc) { log.severe("*** FATAL: Could not read definition of built-in tools." + " Printing stacktrace to stderr and exiting. ***"); exc.printStackTrace(System.err); exit(4); } // --- read the extra tools try { URL url = findResource(ToolManager.getDefaultExtraToolsName(), ""); installExtraTools(url); } catch (Exception exc) { log.info("Failed to read " + ToolManager.getDefaultExtraToolsName() + "; reason was: " + exc); } // --- read the helpset try { ClassLoader cl = null; URL url = findResource(HELPSET_NAME, ""); this.helpSet = new HelpSet(cl, url); } catch (HelpSetException ex) { log.info("couldn't read helpset, no help available"); } } public void go () { boolean admincMode = (startupOptions.manager != null); gui.go (admincMode); if (admincMode) { gui.deployTree.shieldedAddManager(startupOptions.manager); } } public void stop () { Executor.remoteDownAll(); if (deploymentTreeControllerImpl != null) deploymentTreeControllerImpl.stop(); if (firestarter != null) firestarter.shutdown(); gui.stop(); // we stop these rather late because the // thread that executes this very method // may itself be a background thread. bgThreads.shutdownNow(); exit(0); } /** * System.exit() can be prevented by setting the boolean flag to false through * the corresponding command line switch. */ public void exit(final int code) { log.fine("requested to exit with exit code '" + code + "'"); if (startupOptions.doExitOnClose) { // trying to tear down a hanging in-process Acs // can freeze the whole application. // thus, we use a watchdog to ensure termination. new Thread(){@Override public void run(){ try { Thread.sleep(8*1000); } catch (InterruptedException exc) {} System.out.println("VM still up, shooting it now."); try { Thread.sleep(1*1000); } catch (InterruptedException exc) {} Runtime.getRuntime().halt(code); }}.start(); System.exit(code); } else { log.fine("I am configured with the no-exit option, will not shut down the VM"); } } // // ============ Versioning ==================== // /** assigned in prepare() */ protected String projectCreatorId; public String projectCreatorId() { return projectCreatorId; } /** assigned in version() */ protected String version = null; /** * Returns the first non-empty line of file "src/VERSION", * or the empty string in any erroneous case. * @return a valid version info or "" */ public String version() { if (version == null) { String ret = null; try { URL u = this.getClass().getClassLoader().getResource("VERSION"); BufferedReader r = new BufferedReader(new InputStreamReader(u.openStream())); while ((ret = r.readLine()) != null) { ret = ret.trim(); if (! "".equals(ret)) { break; } } } catch (Exception e) {} ret = (ret == null)? "" : ret; this.version = ret; } return version; } // // ============ Resource Loading ==================== // /** * Finds a resource in the resource folder. */ public URL findResource (String name) { return findResource(name, "alma/acs/commandcenter/resources/"); } /** * Finds a resource in the specified location. */ public URL findResource (String name, String where) { URL ret = this.getClass().getClassLoader().getResource(where + name); if (ret == null) { log.fine("couldn't find resource '" + name + "' at " + where); } else { log.finer("found resource " + ret); } return ret; } // // ============ HelpSet ==================== // protected final String HELPSET_NAME = "AcsCommandCenter.hs"; protected HelpSet helpSet; public HelpSet getHelpSet() { return this.helpSet; } // // ============ Tool Loading / Installing ==================== // public URL latestBuiltinToolsUrl; public URL currentExtraToolsUrl; /** * API method */ public void installExtraTools (URL url) throws Exception { InputStream s = url.openStream(); installExtraTools(s); // we store the Url to be able to show its // content in the "view current config" action currentExtraToolsUrl = url; } /** * internal method */ protected void installExtraTools (InputStream f) throws Exception { // --- make delegate read in the xml file ToolManager.readExtraTools(new InputStreamReader(f)); AcsCommandCenterTools tools = ToolManager.getExtraTools(); // --- loop over all "tool" definitions for (int i = 0; i < tools.getToolCount(); i++) { Tool tool = tools.getTool(i); // this map will be used to transport values from ToolInputPanel to // ToolStarter HashMap<String,Object> input = new HashMap<String,Object>(); // ToolStarter knows how to start a tool with the given input final ToolStarter ts = executeTools.addTool(tool, input); // The gui will show the ToolInputPanel to the user and afterwards // notify the ActionListener gui.addExtraTool(tool, input, ts); } } public void removeExtraTools () { gui.removeAllExtraTools(); } /** * API method */ public void loadBuiltinTools (URL url) throws Exception { InputStream is = url.openStream(); loadBuiltinTools(is); // we store the Url to be able to show its // content in the "view current config" action latestBuiltinToolsUrl = url; } /** * internal method */ protected void loadBuiltinTools (InputStream f) throws Exception { InputStreamReader r = new InputStreamReader(f); // this call will replace 0 to n builtin-tool definitions ToolManager.readBuiltinTools(r); } // // ============ Project Management ==================== // public AcsCommandCenterProject createProject () { return projectMaker.createProject(); } public void loadProject (File f) { try { AcsCommandCenterProject p = projectMaker.loadProject(f); project = p; model.setProject(p); gui.setCurrentProjectFile(f); gui.currentProjectChanged(); } catch (Exception exc) { log.info("could not load project file '" + f + "' due to " + exc); } } public AcsCommandCenterProject readProject (File f) throws FileNotFoundException, MarshalException, ValidationException, IOException { return projectMaker.readProject(f); } public void writeProject (AcsCommandCenterProject p, File f) throws IOException, MarshalException, ValidationException { projectMaker.writeProject(p, f); } public void moreContainers () { this.project.getContainers().addContainer(projectMaker.createContainer()); } public void lessContainers () { if (this.project.getContainers().getContainerCount() == 0) return; int indexOfLast = this.project.getContainers().getContainerCount() - 1; ContainerT last = this.project.getContainers().getContainer(indexOfLast); this.project.getContainers().removeContainer(last); } /** * Removes a container from the project. * @return the removed container, or null if index invalid */ public ContainerT removeContainer (int index) { if (index < 0 || index >= this.project.getContainers().getContainerCount()) { return null; } ContainerT cont = this.project.getContainers().getContainer(index); this.project.getContainers().removeContainer(cont); return cont; } /** * Inserts a container into the project at the given index, that is, * the container will afterwards have the index <code>index</code>. */ public void insertContainer(ContainerT cont, int index) { this.project.getContainers().addContainer(index, cont); } // // ============ Variable Management ==================== // public Map<String, Object>[] giveVariableMapsForGui() { Map<String, Object>[] ret = new Map[3]; Map<String, Object> m0 = model.getVariables(); ret[0] = m0; List<String> projectVariableNames = giveProjectVariableNames(); // in project (with values from session if set, otherwise null) Map<String, Object> m1 = new HashMap<String, Object>(); for (Iterator<String> it = projectVariableNames.iterator() ; it.hasNext() ;) { String key = it.next(); m1.put(key, m0.get(key)); } ret[1] = m1; // in session only Map<String, Object> m2 = new HashMap<String, Object>(m0); m2.keySet().removeAll(projectVariableNames); ret[2] = m2; return ret; } public void handleUnresolvableVariable(String name) { variablesDiscoveredOnTheFly.add(name); gui.showUnresolvableVariableErrorDialog(name); } private List<String> variablesDiscoveredOnTheFly = new LinkedList<String>(); /** * For later use by whoever, could currently as well be "protected" */ public List<String> giveProjectVariableNames() { List<String> ret = new LinkedList<String>(); scanForVariables(project, ret); ret.addAll(variablesDiscoveredOnTheFly); return ret; } // --- Scanning through project for variables --- private void scanForVariables(Object[] xx, List<String> l) { for (int i = 0; i < xx.length; i++) { scanForVariables(xx[i], l); } } private void scanForVariables(Object x, List<String> l) { Method[] mm = x.getClass().getDeclaredMethods(); for (int i = 0; i < mm.length; i++) { Method m = mm[i]; if (m.getName().startsWith("get") && ((m.getModifiers() & Modifier.PUBLIC) != 0) && (m.getParameterTypes().length == 0)) { Object rv = null; try { rv = m.invoke(x, new Object[]{}); } catch (Exception exc) {} if (rv == null) { continue; } if (rv instanceof String) { scanForVariables((String)rv, 0, l); } else if (rv instanceof Object[]) { scanForVariables((Object[])rv, l); } if (rv instanceof Object) { scanForVariables((Object)rv, l); } } } } private void scanForVariables(String s, int i, List<String> l) { int markerStart = s.indexOf("${", i); if (markerStart != -1) { int markerEnd = s.indexOf("}", markerStart); if (markerEnd != -1) { String embeddedVarName = s.substring(markerStart + 2, markerEnd); l.add(embeddedVarName); // continue with next variable scanForVariables(s, markerEnd+1, l); } } } // // ======= Options from Command Line etc. ========== // /** * A struct to pass command line options from the CLI parser to the application. * Clients of this class must check for <code>null</code> before using its contents. */ public static class StartupOptions { public Rectangle geometry; public File project; public String manager; public boolean doExitOnClose = true; } // // ======= RunModel implementation ========== // /** * Extension of ProjectRunModel: * * We do not make use of getManagerLocalJavaAgainstCDBHost() (and the gui will offer * no way to change the value). Instead we always use "localhost". * * */ public class MyProjectRunModel extends ProjectRunModel { public MyProjectRunModel(AcsCommandCenterProject project) { super(project); } @Override public String getManagerLocalJavaAgainstCDBHost () { return ACSPorts.getIP(); } } // Background Actions // ============================================================ /** Factory for unlimited number of daemons threads */ private ExecutorService bgThreads = Executors.newCachedThreadPool(new ThreadFactory(){ ThreadFactory def = Executors.defaultThreadFactory(); public Thread newThread (Runnable r) { Thread ret = def.newThread(r); ret.setDaemon(true); return ret; } }); public void runBackground (Runnable r) { bgThreads.execute(r); } // DeploymentTreeController implementation // ============================================================ public DeploymentTreeControllerImpl deploymentTreeControllerImpl; public class DeploymentTreeControllerImpl implements DeploymentTreeController { // create MaciSupervisors // ----------------------------------------- // (i) we cache previously created ones // (ii) newly created ones will not be start()-ed synchronized public GuiMaciSupervisor giveMaciSupervisor(String managerLoc) throws OrbInitException { GuiMaciSupervisor ret = managerLoc2instance.get(managerLoc); if (ret == null) { ORB orb = firestarter.giveOrb(); ret = new GuiMaciSupervisor("AcsCommandCenter", managerLoc, orb, log); ret.setRefreshesPeriodically(true); managerLoc2instance.put(managerLoc, ret); } return ret; } private Map<String, GuiMaciSupervisor> managerLoc2instance = new WeakHashMap<String, GuiMaciSupervisor>(); // background actions // ----------------------------------------- public java.util.concurrent.Executor getBackgroundExecutor() { return bgThreads; } // lifecycle of this class // ----------------------------------------- /** Call stop() on all MaciSupervisors */ synchronized public void stop() { for (IMaciSupervisor s : managerLoc2instance.values()) { try { s.stop(); } catch (Exception exc) {/* ignore */} } } } }