/*
* Created on Oct 22, 2003 by mschilli
*/
package alma.acs.commandcenter.gui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import si.ijs.maci.ClientInfo;
import si.ijs.maci.ComponentInfo;
import si.ijs.maci.ContainerInfo;
import alma.acs.commandcenter.meta.Firestarter.OrbInitException;
import alma.acs.commandcenter.meta.GuiMaciSupervisor;
import alma.acs.commandcenter.meta.IMaciSupervisor;
import alma.acs.commandcenter.meta.IMaciSupervisor.CannotRetrieveManagerException;
import alma.acs.commandcenter.meta.IMaciSupervisor.CorbaNoPermissionException;
import alma.acs.commandcenter.meta.IMaciSupervisor.CorbaNotExistException;
import alma.acs.commandcenter.meta.IMaciSupervisor.CorbaTransientException;
import alma.acs.commandcenter.meta.IMaciSupervisor.CorbaUnknownException;
import alma.acs.commandcenter.meta.IMaciSupervisor.NotConnectedToManagerException;
import alma.acs.commandcenter.meta.IMaciSupervisor.UnknownErrorException;
import alma.acs.commandcenter.meta.MaciInfo;
import alma.acs.commandcenter.meta.MaciInfo.FolderInfo;
import alma.acs.commandcenter.meta.MaciInfo.InfoDetail;
import alma.acs.commandcenter.meta.MaciInfo.SortingTreeNode;
import alma.acs.commandcenter.meta.MaciSupervisor;
import alma.acs.util.AcsLocations;
import alma.maciErrType.CannotGetComponentEx;
import alma.maciErrType.ComponentConfigurationNotFoundEx;
import alma.maciErrType.ComponentNotAlreadyActivatedEx;
import alma.maciErrType.NoPermissionEx;
import alma.maciErrType.wrappers.AcsJCannotDeactivateComponentEx;
import alma.maciErrType.wrappers.AcsJComponentDeactivationFailedEx;
import alma.maciErrType.wrappers.AcsJComponentDeactivationUncleanEx;
import com.xtmod.util.collections.TreeMerge;
/**
* @author mschilli
*/
public class DeploymentTree extends JTree {
protected ContextMenu containerContextMenu;
protected ContextMenu managerContextMenu;
protected ContextMenu clientContextMenu;
protected ContextMenu componentContextMenu;
protected ContextMenu folderContextMenu;
protected TreeEventForwarder treeEventForwarder;
protected List<ModelConverter> modelConverters;
protected Renderer cellRenderer;
// assigned on each invokation of show
protected DefaultMutableTreeNode target;
// assigned on each invokation of show
protected GuiMaciSupervisor selectedSupervisor;
// external logic that can create supervisor instances
protected DeploymentTreeController ctrl;
public DeploymentTree(DeploymentTreeController ctrl) {
this (ctrl, true);
}
// msc 2009-04: introducing "allowControl" flag for permission handling (used by OMC)
public DeploymentTree(DeploymentTreeController ctrl, boolean allowControl) {
super(new SortingTreeNode("Deployment Info"));
this.setRootVisible(false);
this.setShowsRootHandles(true);
// --- cell renderer
ToolTipManager.sharedInstance().registerComponent(this);
this.setCellRenderer(cellRenderer = new Renderer());
// --- forward events from the MaciTree-Models
// (there will be one for each macimanager) to our own, all-in-one TreeModel
treeEventForwarder = new TreeEventForwarder (this.getTreeModel());
modelConverters = new Vector<ModelConverter>();
// --- setup context menus
clientContextMenu = new ClientContextMenu (allowControl);
managerContextMenu = new ManagerContextMenu (allowControl);
containerContextMenu = new ContainerContextMenu (allowControl);
componentContextMenu = new ComponentContextMenu (allowControl);
folderContextMenu = new FolderContextMenu (allowControl);
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked (MouseEvent evt) {
if (SwingUtilities.isRightMouseButton(evt)) {
showContextMenu(evt);
}
}
});
this.getSelectionModel().addTreeSelectionListener(new SelectionListener());
this.ctrl = ctrl;
}
// synchronous run in swing thread
private void runSwingNow (Runnable r) {
if (SwingUtilities.isEventDispatchThread())
r.run();
else
try {
SwingUtilities.invokeAndWait(r);
} catch (Exception exc) {
/* exc.printStackTrace(); */
}
}
/**
* @param evt
*/
protected void showContextMenu (MouseEvent evt) {
TreePath targetPath = this.getClosestPathForLocation(evt.getX(), evt.getY());
if (targetPath == null) {
// clicked into a totally empty tree (no manager shown): ignore click.
return;
}
setSelectionPath(targetPath);
if (targetPath.getPathCount() == 1) {
// clicked on the descriptive super-root node "Deployment Info"
// that has no function besides looking good
return;
}
// the supervisor (which is in the rootnode) for this subtree
selectedSupervisor = maciSupervisor(((SortingTreeNode) targetPath.getPathComponent(1)));
// the node the mouse was clicked on
target = (SortingTreeNode) targetPath.getLastPathComponent();
Object userObject = target.getUserObject();
ContextMenu menu;
if (userObject instanceof IMaciSupervisor) {
menu = managerContextMenu;
} else if (userObject instanceof ContainerInfo) {
menu = containerContextMenu;
} else if (userObject instanceof ClientInfo) {
menu = clientContextMenu;
} else if (userObject instanceof ComponentInfo) {
menu = componentContextMenu;
} else if (userObject instanceof FolderInfo) {
menu = folderContextMenu;
} else if (userObject instanceof InfoDetail) {
// msc 2012-06: help user navigate in large datasets (cf. COMP-3684)
// this menu is built dynamically from rather expensive information.
menu = new ContextMenu(false);
int[] selectedHandles = ((SortingTreeNode)target).representedHandles;
if (selectedHandles.length>0) {
menu.add(new JLabel(" Scroll to ..."));
menu.add(new JSeparator(JSeparator.HORIZONTAL));
SortingTreeNode mgrNode = (SortingTreeNode)target.getPath()[1];
List<Object> list = new ArrayList<Object>();
list.add(mgrNode);
for (SortingTreeNode top : mgrNode.childrens())
list.addAll(Collections.list(top.children()));
for (Object obj : list) {
final SortingTreeNode elem = (SortingTreeNode)obj;
for (int i=0; i<selectedHandles.length; i++) {
if (elem.represents(selectedHandles[i])) {
Object elemUserObject = elem.getUserObject();
String elemName=null;
if (elemUserObject instanceof ComponentInfo)
elemName = ((ComponentInfo)elemUserObject).name;
else if (elemUserObject instanceof ContainerInfo)
elemName = ((ContainerInfo)elemUserObject).name;
else if (elemUserObject instanceof ClientInfo)
elemName = ((ClientInfo)elemUserObject).name;
else if (elemUserObject instanceof MaciSupervisor)
elemName = "Manager";
if (elemName!=null) {
menu.add(new AbstractAction(selectedHandles[i]+" = "+elemName){
@Override public void actionPerformed (ActionEvent evt) {
DeploymentTree.this.scrollPathToVisible(new TreePath(elem.getPath()));
}
});
}
}
}
}
}
} else {
return;
}
menu.show(this, evt.getX(), evt.getY());
}
// get the supervisor stored in the tree node
protected GuiMaciSupervisor maciSupervisor (DefaultMutableTreeNode managerNode) {
return (GuiMaciSupervisor) managerNode.getUserObject();
}
protected SortingTreeNode getRoot () {
return (SortingTreeNode) super.getModel().getRoot();
}
protected DefaultTreeModel getTreeModel () {
return (DefaultTreeModel) super.getModel();
}
protected GuiMaciSupervisor getMaciSupervisor (String managerLoc) throws OrbInitException {
GuiMaciSupervisor mrf = ctrl.giveMaciSupervisor(managerLoc);
mrf.setConnectsAutomatically(false);
return mrf;
}
protected void startAndAddMaciSupervisor (GuiMaciSupervisor mrf) throws NoPermissionEx, CannotRetrieveManagerException,
CorbaTransientException, CorbaNotExistException, UnknownErrorException {
mrf.start(); // start is smart - can be called repeatedly
addManager(mrf);
}
public void addManager (GuiMaciSupervisor mrfotogen) throws NoPermissionEx, CorbaTransientException, CorbaNotExistException,
UnknownErrorException {
// retrieve the tree-structured info that the manager offers as a TreeModel
MaciInfo maciInfo = null;
try {
maciInfo = mrfotogen.getMaciInfo();
} catch (NotConnectedToManagerException exc) {
// really shouldn't happen (the passed in supervisor is started)
throw new AssertionError("This supervisor has obviously not been started: " + mrfotogen);
}
// no exception occurred, we have valid info
// the root of maciInfo is just a node with the boring string "Manager"...
final SortingTreeNode managerNode = ((SortingTreeNode)maciInfo.getRoot()).clone();
// ...but the root of guiInfo will be a node holding the MaciSupervisor
managerNode.setUserObject(mrfotogen);
DefaultTreeModel guiInfo = new DefaultTreeModel(managerNode);
ModelConverter mc = new ModelConverter(maciInfo, guiInfo);
modelConverters.add(mc);
maciInfo.addTreeModelListener(mc);
// have events forwarded from the TreeModel to our own all-in-one TreeModel
treeEventForwarder.addSource(guiInfo);
// store the additional manager node in our tree
getTreeModel().insertNodeInto(managerNode, getRoot(), getRoot().getChildCount());
// populate guitree, will make the tree display
mc.convertCompleteModel();
// must expand the new manager node otherwise the subtree won't be visible
runSwingNow(new Runnable(){
public void run () {
expandPath(new TreePath(new Object[]{getRoot(), managerNode}));
// for user joy also expand the manager's subnodes
expandPath(new TreePath(new Object[]{getRoot(), managerNode, managerNode.getChildAt(0)}));
expandPath(new TreePath(new Object[]{getRoot(), managerNode, managerNode.getChildAt(1)}));
expandPath(new TreePath(new Object[]{getRoot(), managerNode, managerNode.getChildAt(2)}));
}
});
}
public boolean removeManager (String managerLoc, boolean dismissManager) {
DefaultMutableTreeNode managerNode = getManagerNode(managerLoc);
if (managerNode == null) {
return false;
}
GuiMaciSupervisor ms = maciSupervisor(managerNode);
// make the MaciSupervisor forget its manager connection
if (dismissManager) {
ms.dismissManager();
}
for (ModelConverter mc : modelConverters) {
if (maciSupervisor(mc.managerNode()) == ms) {
mc.sourceModel.removeTreeModelListener(mc);
modelConverters.remove(mc);
break;
}
}
removeNode(managerNode);
return true;
}
public void refreshManagers () {
// iterate over manager nodes
for (SortingTreeNode managerNode : getRoot().childrens()) {
shieldedRefreshManager(maciSupervisor(managerNode));
}
}
/**
* Finds the manager node with the given managerLocation inside
*/
protected DefaultMutableTreeNode getManagerNode (String managerLoc) {
String toFind = managerLoc;
// iterate over macisupervisor nodes
for (SortingTreeNode managerNode : getRoot().childrens()) {
// compare corbalocs
String managerLocation = maciSupervisor(managerNode).getManagerLocation();
if (managerLocation.equals(toFind))
return managerNode;
}
return null;
}
/**
* Little helper for the renderer. Just because it seems nice for the user. The
* specified node must contain a ClientInfo user object.
*/
protected boolean isMyself (DefaultMutableTreeNode node) {
try {
// find manager node
DefaultMutableTreeNode n = node;
do {
n = (DefaultMutableTreeNode) n.getParent();
} while (!(n.getUserObject() instanceof IMaciSupervisor));
// compare handles
ClientInfo info = (ClientInfo) node.getUserObject();
return (maciSupervisor(n).myMaciHandle() == info.h);
} catch (Exception e) {
// instead of making the above code rock-stable, let's do it cheap
return false;
}
}
/**
* Removes an arbitrary node from the gui
*/
protected void removeNode (DefaultMutableTreeNode node) {
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();
// index needed for event we'll publish subsequently
int index = parent.getIndex(node);
// remove node from tree
parent.remove(node);
// force the model to publish an event
// getTreeModel().nodeStructureChanged(getRoot()); // dramatic event, too coarse
getTreeModel().nodesWereRemoved(parent, new int[]{index}, new Object[]{node});
}
/**
* Signals to the user that an action takes longer.
*/
protected void setBusy (boolean b) {
int cursor = (b) ? Cursor.WAIT_CURSOR : Cursor.DEFAULT_CURSOR;
this.setCursor(Cursor.getPredefinedCursor(cursor));
}
// --- "freeze view" logic ---
protected boolean isViewFrozen;
/**
* Will make the model converters pause, so the deployment
* trees remain unchanged so the user can navigate the
* trees without disturbance.
*/
public void setViewFrozen (boolean newValue) {
isViewFrozen = newValue;
/* when "freeze" gets switched off, there may be
* changes pending in some model converters */
if (newValue == false) {
for (ModelConverter mc : modelConverters)
mc.convertCompleteModelIfDirty();
}
}
/**
* This is public so outside code like a toggle
* button could update its state.
*/
public boolean isViewFrozen() {
return isViewFrozen;
}
//
// =========================================================
// ===================== Inner Types =======================
// =========================================================
//
/**
* Provides appropriate treatment for all the node types.
*/
protected class Renderer extends DefaultTreeCellRenderer {
Border bluelineBorder = new LineBorder(Color.blue, 1);
Border graylineBorder = new LineBorder(Color.gray, 1);
Border emptyBorder = new EmptyBorder(1, 1, 1, 1);
Color grayBackground = Color.lightGray;
int[] currentlySelectedHandles = new int[]{};
Component hiddenNode = Box.createRigidArea(new Dimension(0,0));
@Override
public Component getTreeCellRendererComponent (JTree tree, Object value, boolean sel, boolean expand, boolean leaf, int row, boolean foc) {
super.getTreeCellRendererComponent(tree, value, sel, expand, leaf, row, foc);
this.setIcon(null);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
// ==== caption ====
String[] captions = getCaptions(node);
String text = captions[0];
String tooltip = captions[1];
this.setText(text);
this.setToolTipText(tooltip);
// ==== tree filtering ====
// idea for this cheap filtering taken from:
// http://stackoverflow.com/questions/9234297/filtering-on-a-jtree
if (node instanceof SortingTreeNode)
if (((SortingTreeNode)node).filtered)
return hiddenNode;
// ==== selection ====
/*
* We want the real selection blue, and the "deputy"-selections gray. Since our
* superclass implementation determines the selection background in paint() and
* not in getRendererComponent(), we need to set the
* "backgroundNonSelectionColor" property which is getting evaluated by paint()
*/
Border border = null;
if (sel) {
border = bluelineBorder;
if (node instanceof SortingTreeNode) {
currentlySelectedHandles = ((SortingTreeNode) node).representedHandles;
}
} else {
Color backgroundNonSelectionColor = null;
if (node instanceof SortingTreeNode) {
SortingTreeNode casted = (SortingTreeNode) node;
for (int i=0; i<currentlySelectedHandles.length; i++) {
if (casted.represents(currentlySelectedHandles[i])) {
border = graylineBorder;
backgroundNonSelectionColor = grayBackground;
break;
}
}
}
// sets the non-selection background to either "null" or "gray"
this.setBackgroundNonSelectionColor(backgroundNonSelectionColor);
if (border == null)
border = emptyBorder;
}
this.setBorder(border);
// === done ===
return this;
}
/** @return (text,tooltip) */
String[] getCaptions (DefaultMutableTreeNode node) {
String text; /* compiler will optimize the string operations */
String tooltip = null;
Object userObject = node.getUserObject();
if (userObject == null) {
text = "empty node (strange)";
} else if (userObject instanceof IMaciSupervisor) {
String mgrLoc = maciSupervisor(node).getManagerLocation();
String[] mgr = AcsLocations.convert(mgrLoc);
text = "Manager on " + mgr[0] + ":" + mgr[1];
} else if (userObject instanceof ContainerInfo) {
ContainerInfo casted = (ContainerInfo) userObject;
int nCOBs = (casted.components != null) ? casted.components.length : -1;
text = "'" + casted.name + "' [id " + casted.h + "] : " + nCOBs + " component" + ((nCOBs == 1) ? "" : "s");
} else if (userObject instanceof ClientInfo) {
ClientInfo casted = (ClientInfo) userObject;
text = "'" + casted.name + "' [id " + casted.h + "]";
text += (isMyself(node)) ? " (myself)" : "";
} else if (userObject instanceof ComponentInfo) {
ComponentInfo casted = (ComponentInfo) userObject;
int nClients = (casted.clients != null) ? casted.clients.length : -1;
text = "'" + casted.name + "' [id " + casted.h + "] : " + nClients + " client" + ((nClients == 1) ? "" : "s");
} else if (userObject instanceof InfoDetail) {
/*
* InfoDetail nodes are hanging below Components and Clients and Containers.
* Thus, they all share the same visualization of, for instance, the info
* detail "reference"
*/
InfoDetail casted = (InfoDetail) userObject;
if (casted.key.equals("container_name"))
text = "needs: " + casted.value;
else if (casted.key.equals("reference"))
text = "reference: " + (casted.value.equals("null") ? "invalid" : "ok");
else if (casted.key.equals("container"))
text = "hosted by: " + (casted.value.equals("0") ? "no container" : casted.value);
else if (casted.key.equals("access"))
text = "access: " + (casted.value.equals("0") ? "unrestricted" : casted.value);
else
text = casted.key + ": " + casted.value;
if (node instanceof SortingTreeNode) {
if (((SortingTreeNode)node).representedHandles.length > 0)
tooltip = "Right-Click for Navigation";
}
} else if (userObject instanceof FolderInfo) {
FolderInfo info = (FolderInfo) userObject;
text = info.name;
text = text + " (" + node.getChildCount() + ")";
if (info.hasFilter())
text += ", showing only '"+info.filter+"'";
tooltip = "Right-Click for Filtering";
} else
text = String.valueOf(node);
return new String[] {text, tooltip};
}
}
/**
* Listens on selection-changes in the tree to trigger a repaint-event.
*/
protected class SelectionListener implements TreeSelectionListener {
public void valueChanged (TreeSelectionEvent e) {
Object node = e.getPath().getLastPathComponent();
if (node instanceof SortingTreeNode)
if (!((SortingTreeNode)node).filtered)
cellRenderer.currentlySelectedHandles = ((SortingTreeNode) node).representedHandles;
repaint();
}
}
void applyFilters (DefaultTreeModel tm) {
SortingTreeNode mgrNode = (SortingTreeNode)tm.getRoot();
for (SortingTreeNode folder : mgrNode.childrens()) {
applyFilter(folder);
}
}
void applyFilter (SortingTreeNode folder) {
FolderInfo folderInfo = (FolderInfo)folder.getUserObject();
for (SortingTreeNode entry : folder.childrens()) {
String caption = cellRenderer.getCaptions(entry)[0];
boolean passes = !folderInfo.hasFilter() || caption.contains (folderInfo.filter);
for (Enumeration<?> subtree = entry.breadthFirstEnumeration(); subtree.hasMoreElements();)
((SortingTreeNode)(subtree.nextElement())).filtered = !passes;
}
}
protected class ModelConverter implements TreeModelListener {
protected MaciInfo sourceModel;
protected DefaultTreeModel targetModel;
public ModelConverter(MaciInfo sourceModel, DefaultTreeModel targetModel) {
this.sourceModel = sourceModel;
this.targetModel = targetModel;
}
public DefaultMutableTreeNode managerNode () {
return (DefaultMutableTreeNode) targetModel.getRoot();
}
// --- "freeze view" logic ---
protected boolean isDirty;
protected void convertCompleteModelIfDirty () {
if (isDirty) {
isDirty = false;
convertCompleteModel();
}
}
// --- conversion logic ---
public void treeNodesChanged (TreeModelEvent e) {}
public void treeNodesInserted (TreeModelEvent e) {}
public void treeNodesRemoved (TreeModelEvent e) {}
public void treeStructureChanged (TreeModelEvent e) {
if (isViewFrozen()) {
isDirty = true;
return;
}
convertCompleteModel();
applyFilters (targetModel);
}
protected final TreeMerge<String> treemerger = new TreeMerge<String>(){
@Override protected String identifyExisting (TreeNode x) {
return identify((DefaultMutableTreeNode)x);
}
@Override protected String identifyIncoming (TreeNode x) {
return identify((DefaultMutableTreeNode)x);
}
String identify (DefaultMutableTreeNode x) {
Object userObject = x.getUserObject();
if (userObject instanceof ComponentInfo)
return ((ComponentInfo) userObject).name;
if (userObject instanceof ClientInfo)
return String.valueOf(((ClientInfo) userObject).h);
if (userObject instanceof ContainerInfo)
return ((ContainerInfo) userObject).name;
if (userObject instanceof FolderInfo)
return ((FolderInfo) userObject).name;
return x.toString();
}
@Override protected boolean isUpdate (TreeNode exist, TreeNode incom) {
/* our tree-renderer adds the childcount to some nodes (the folder info nodes),
* so if the childcount changes we want a tree-change event to be sent to the
* jtree. we could run the renderer here to find out if "incom" would be
* rendered differently from "exist" and thus we consider this an update. but
* for simplicity, i'm re-implementing a tiny portion of renderer logic here. */
if (exist.getChildCount() != incom.getChildCount())
return true;
/* Keeping it kind of simple by looking at the toString() of the treenode
* which is in fact a toString() of the userobject. One could also do some
* instanceof here and look at the userobjects more closely to find out
* whether they have different contents than before. Checking via
* toString() means that nodes that have a corbastruct as their userobject
* will be updated at each refresh. This is a bit costly but makes sure the
* tree always shows up-to-date info. */
return !String.valueOf(exist).equals(String.valueOf(incom));
}
@Override protected MutableTreeNode create (TreeNode x) {
return (DefaultMutableTreeNode) ((DefaultMutableTreeNode) x).clone();
}
@Override protected void applyUpdate (TreeNode exist, TreeNode incom) {
((DefaultMutableTreeNode)exist).setUserObject(((DefaultMutableTreeNode)incom).getUserObject());
if (exist instanceof SortingTreeNode && incom instanceof SortingTreeNode) // always true?
((SortingTreeNode)exist).representedHandles = ((SortingTreeNode)incom).representedHandles;
}
};
/** Used to force data visibility between threads, as per Java Memory Model */
private AtomicBoolean jmmFlushRefresh = new AtomicBoolean();
protected void convertCompleteModel() {
treemerger.diff (targetModel, sourceModel);
/* this is the main point of the whole model conversion business: modify the
* jtree's tree model only in the swing thread. the maci info is modified by the
* supervisor in whatever thread it uses. we listen to those changes and transfer
* them to the tree's underlying tree model.... using the swing thread... */
if (!treemerger.areEqual()) {
// flush
jmmFlushRefresh.set(true);
runSwingNow(new Runnable() {
public void run () {
// refresh
jmmFlushRefresh.get();
treemerger.merge();
}
});
}
}
}
/**
* Forwards events from one or more TreeModels to a TreeModel.
*
* This will accept events from any TreeModels it is registered with. It will then
* raise that event on the target TreeModel.
*
* @author mschilli
*/
protected class TreeEventForwarder implements TreeModelListener {
protected DefaultTreeModel forwardTarget;
// --- construction / setup ---
public TreeEventForwarder(DefaultTreeModel forwardTarget) {
this.forwardTarget = forwardTarget;
}
// register as a listener with specified TreeModel
public void addSource (TreeModel source) {
source.addTreeModelListener(this);
}
// --- forwarding logic ---
synchronized public void treeStructureChanged (TreeModelEvent e) {
/* System.err.println("TEF: "+Thread.currentThread().getName()+" treeStructureChanged");*/
TreeNode n = (TreeNode) e.getTreePath().getLastPathComponent();
forwardTarget.nodeStructureChanged(n);
}
public void treeNodesChanged (TreeModelEvent e) {
/* System.err.println("TEF: "+Thread.currentThread().getName()+" treeNodesChanged "+e);*/
TreeNode n = (TreeNode) e.getTreePath().getLastPathComponent();
forwardTarget.nodesChanged(n, e.getChildIndices());
}
public void treeNodesInserted (TreeModelEvent e) {
/* System.err.println("TEF: "+Thread.currentThread().getName()+" treeNodesInserted "+e);*/
TreeNode n = (TreeNode) e.getTreePath().getLastPathComponent();
forwardTarget.nodesWereInserted (n, e.getChildIndices());
}
public void treeNodesRemoved (TreeModelEvent e) {
/* System.err.println("TEF: "+Thread.currentThread().getName()+" treeNodesRemoved "+e);*/
TreeNode n = (TreeNode) e.getTreePath().getLastPathComponent();
forwardTarget.nodesWereRemoved(n, e.getChildIndices(), e.getChildren());
}
}
//
// =================== Context Menus =====================
//
/**
* Base class for our context menus
*/
protected class ContextMenu extends JPopupMenu {
protected ContextMenu (boolean allowControl) {}
/**
* If this menu is empty, we don't bother to show it.
*/
@Override public void show (Component invoker, int x, int y) {
if (getComponentCount() == 0)
return;
super.show(invoker, x, y);
}
}
protected class ManagerContextMenu extends ContextMenu {
protected ManagerContextMenu (boolean allowControl) {
super(allowControl);
this.add(new ManagerRefreshAction());
if (allowControl) {
this.add(new JPopupMenu.Separator());
this.add(new ManagerPingAction());
this.add(new ManagerShutdownAction());
}
/*
* msc (2008-10): "remove from view" hardly needed: if a
* manager goes away, we offer to remove it anyhow. and
* other than that, this entry is undesirable inside the omc.
*
* this.add(new JPopupMenu.Separator());
* this.add(new RemoveFromViewAction());
*/
}
}
protected class FolderContextMenu extends ContextMenu {
protected JTextField txtFilter = new JTextField(8);
protected FolderContextMenu (boolean allowControl) {
super (allowControl);
Box b = Box.createHorizontalBox();
b.add(new JLabel(" Show only "));
b.add(txtFilter);
b.add(new JLabel(" "));
this.add(b);
this.add(new JSeparator(JSeparator.HORIZONTAL));
this.add(new AbstractAction("Apply"){
@Override public void actionPerformed (ActionEvent e) {
((FolderInfo)target.getUserObject()).filter = txtFilter.getText().trim();
applyFilter((SortingTreeNode)target);
// this re-renders the tree, and collapses the subtrees.
getTreeModel().reload(target);
}
});
this.add(new AbstractAction("Reset"){
@Override public void actionPerformed (ActionEvent e) {
txtFilter.setText(null);
((FolderInfo)target.getUserObject()).filter = txtFilter.getText().trim();
applyFilter((SortingTreeNode)target);
// this re-renders the tree, and collapses the subtrees.
getTreeModel().reload(target);
}
});
}
// updates the menu gui before show
@Override protected void firePopupMenuWillBecomeVisible () {
txtFilter.setText(((FolderInfo)target.getUserObject()).filter);
super.firePopupMenuWillBecomeVisible();
}
}
protected class ContainerContextMenu extends ContextMenu {
protected ContainerContextMenu (boolean allowControl) {
super (allowControl);
if (allowControl) {
this.add(new ContainerPingAction());
this.add(new ContainerMessageAction());
this.add(new ContainerDisconnectAction());
this.add(new ContainerShutdownAction());
this.add(new JPopupMenu.Separator());
this.add(new ContainerLogoutAction());
}
}
}
protected class ClientContextMenu extends ContextMenu {
protected ClientContextMenu (boolean allowControl) {
super (allowControl);
if (allowControl) {
this.add(new ClientPingAction());
this.add(new ClientMessageAction());
this.add(new ClientDisconnectAction());
this.add(new JPopupMenu.Separator());
this.add(new ClientLogoutAction());
}
}
}
protected class ComponentContextMenu extends ContextMenu {
protected ComponentContextMenu (boolean allowControl) {
super (allowControl);
if (allowControl) {
this.add(new ComponentRequestAction());
this.add(new ComponentReleaseAction());
this.add(new JPopupMenu.Separator());
this.add(new ComponentForceReleaseAction());
}
}
}
//
// ======================= Actions =========================
//
/**
* Performs the work to be done within the event-dispatcher thread
*/
protected abstract class SwingAction extends AbstractAction {
protected SwingAction(String name) {
super(name);
}
final public void actionPerformed (ActionEvent e) {
try {
actionPerformed();
} catch (Exception exc) {
/*
* This catch-clause will rarely be executed: the menu-item actions mostly use
* the shielded API which catches exceptions way before
*/
ErrorBox.showErrorDialog(DeploymentTree.this, "\"" + getValue(NAME) + "\" failed", exc);
}
}
protected abstract void actionPerformed ();
}
/**
* Performs the work to be done delayed, and NOT within the event-dispatcher thread.
* Used for most actions in the context menus.
*/
protected abstract class BackgroundAction extends AbstractAction {
protected BackgroundAction(String name) {
super(name);
}
final public void actionPerformed (ActionEvent e) {
ctrl.getBackgroundExecutor().execute(new Runnable() {
public void run () {
setBusy(true);
try {
actionPerformed();
} catch (Exception exc) {
/* This catch-clause will rarely be executed: the menu-item actions
* mostly use the shielded API which catches exceptions way before */
ErrorBox.showErrorDialog(DeploymentTree.this, "\"" + getValue(NAME) + "\" failed", exc);
} finally {
setBusy(false);
}
}
});
}
protected abstract void actionPerformed () throws Exception;
}
protected class RemoveFromViewAction extends SwingAction {
protected RemoveFromViewAction() {
super("Remove from View");
}
@Override
public void actionPerformed () {
removeNode(target);
}
}
protected class ManagerRefreshAction extends BackgroundAction {
protected ManagerRefreshAction() {
super("Refresh Info");
}
@Override
public void actionPerformed () {
shieldedRefreshManager(selectedSupervisor);
}
}
protected class ManagerPingAction extends BackgroundAction {
protected ManagerPingAction() {
super("Send Ping Request");
}
@Override
public void actionPerformed () {
shieldedPingManager(selectedSupervisor);
}
}
protected class ManagerShutdownAction extends BackgroundAction {
protected ManagerShutdownAction() {
super("Send Shutdown Request");
}
@Override
public void actionPerformed () throws Exception {
shieldedShutdownManager(selectedSupervisor);
}
}
protected class ContainerPingAction extends BackgroundAction {
protected ContainerPingAction() {
super("Send Ping Request");
}
@Override
public void actionPerformed () throws Exception {
selectedSupervisor.containerPing((ContainerInfo) target.getUserObject());
}
}
protected class ContainerShutdownAction extends BackgroundAction {
protected ContainerShutdownAction() {
super("Send Shutdown Request");
}
@Override
public void actionPerformed () throws Exception {
selectedSupervisor.containerShutdown((ContainerInfo) target.getUserObject());
}
}
protected class ContainerMessageAction extends BackgroundAction {
protected ContainerMessageAction() {
super("Send Message...");
}
@Override
public void actionPerformed () throws Exception {
String msg = JOptionPane.showInputDialog(DeploymentTree.this, "Enter message text:");
if (msg != null)
selectedSupervisor.containerMessage((ContainerInfo) target.getUserObject(), IMaciSupervisor.MSG_INFORMATION, msg);
}
}
protected class ClientPingAction extends BackgroundAction {
protected ClientPingAction() {
super("Send Ping Request");
}
@Override
public void actionPerformed () throws Exception {
selectedSupervisor.clientPing((ClientInfo) target.getUserObject());
}
}
protected class ContainerDisconnectAction extends BackgroundAction {
protected ContainerDisconnectAction() {
super("Send Disconnect Request");
}
@Override
public void actionPerformed () throws Exception {
selectedSupervisor.containerDisconnect((ContainerInfo) target.getUserObject());
}
}
protected class ClientDisconnectAction extends BackgroundAction {
protected ClientDisconnectAction() {
super("Send Disconnect Request");
}
@Override
public void actionPerformed () throws Exception {
selectedSupervisor.clientDisconnect((ClientInfo) target.getUserObject());
}
}
protected class ClientMessageAction extends BackgroundAction {
protected ClientMessageAction() {
super("Send Message...");
}
@Override
public void actionPerformed () throws Exception {
String msg = JOptionPane.showInputDialog(DeploymentTree.this, "Enter message text:");
if (msg != null)
selectedSupervisor.clientMessage((ClientInfo) target.getUserObject(), IMaciSupervisor.MSG_INFORMATION, msg);
}
}
protected class ContainerLogoutAction extends BackgroundAction {
protected ContainerLogoutAction() {
super("Have logged out by Manager");
}
@Override
public void actionPerformed () throws Exception {
shieldedLogoutContainer(selectedSupervisor, (ContainerInfo) target.getUserObject());
}
}
protected class ClientLogoutAction extends BackgroundAction {
protected ClientLogoutAction() {
super("Have logged out by Manager");
}
@Override
public void actionPerformed () throws Exception {
shieldedLogoutClient(selectedSupervisor, (ClientInfo) target.getUserObject());
}
}
protected class ComponentRequestAction extends BackgroundAction {
protected ComponentRequestAction() {
super("Acquire reference ( Activate )");
}
@Override
public void actionPerformed () throws Exception {
String name = ((ComponentInfo) target.getUserObject()).name;
shieldedGetComponent(selectedSupervisor, name);
}
}
protected class ComponentReleaseAction extends BackgroundAction {
protected ComponentReleaseAction() {
super("Release reference");
}
@Override
public void actionPerformed () throws Exception {
String name = ((ComponentInfo) target.getUserObject()).name;
shieldedReleaseComponents(selectedSupervisor, new String[]{name});
}
}
protected class ComponentForceReleaseAction extends BackgroundAction {
protected ComponentForceReleaseAction() {
super("Force system-wide deactivation");
}
@Override
public void actionPerformed () throws Exception {
String name = ((ComponentInfo) target.getUserObject()).name;
shieldedForceReleaseComponent(selectedSupervisor, name);
}
}
//
// ======================= Shielded API =========================
//
public void shieldedRefreshManager (GuiMaciSupervisor supervisor) {
try {
supervisor.getMaciInfo();
} catch (NoPermissionEx exc) {
mce.handleException(supervisor, exc);
} catch (NotConnectedToManagerException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaTransientException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaNotExistException exc) {
mce.handleException(supervisor, exc);
} catch (UnknownErrorException exc) {
mce.handleException(supervisor, exc);
}
}
public void shieldedAddManager (String managerLoc) {
GuiMaciSupervisor supervisor = null;
try {
supervisor = getMaciSupervisor(managerLoc);
} catch (OrbInitException exc1) {
mce.handleException(exc1);
}
try {
startAndAddMaciSupervisor(supervisor);
} catch (CannotRetrieveManagerException exc) {
mce.handleException(supervisor, exc);
} catch (NoPermissionEx exc) {
mce.handleException(supervisor, exc);
} catch (CorbaTransientException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaNotExistException exc) {
mce.handleException(supervisor, exc);
} catch (UnknownErrorException exc) {
mce.handleException(supervisor, exc);
}
}
/**
* Ping Manager (shielded)
*/
public void shieldedPingManager (GuiMaciSupervisor supervisor) {
try {
supervisor.managerPing();
} catch (NotConnectedToManagerException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaTransientException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaNotExistException exc) {
mce.handleException(supervisor, exc);
} catch (UnknownErrorException exc) {
mce.handleException(supervisor, exc);
}
}
/**
* Shutdown Manager (shielded)
*/
public void shieldedShutdownManager (GuiMaciSupervisor supervisor) {
try {
supervisor.managerShutdown();
} catch (NotConnectedToManagerException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaNoPermissionException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaTransientException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaNotExistException exc) {
mce.handleException(supervisor, exc);
} catch (UnknownErrorException exc) {
mce.handleException(supervisor, exc);
}
}
/**
* Logout Container (shielded)
*/
public void shieldedLogoutContainer (GuiMaciSupervisor supervisor, ContainerInfo info) {
try {
supervisor.managerLogout(info);
} catch (NoPermissionEx exc) {
mce.handleException(supervisor, exc);
} catch (NotConnectedToManagerException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaTransientException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaNotExistException exc) {
mce.handleException(supervisor, exc);
} catch (UnknownErrorException exc) {
mce.handleException(supervisor, exc);
}
}
/**
* Logout Client (shielded)
*/
public void shieldedLogoutClient (GuiMaciSupervisor supervisor, ClientInfo info) {
try {
supervisor.managerLogout(info);
} catch (NoPermissionEx exc) {
mce.handleException(supervisor, exc);
} catch (NotConnectedToManagerException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaTransientException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaNotExistException exc) {
mce.handleException(supervisor, exc);
} catch (UnknownErrorException exc) {
mce.handleException(supervisor, exc);
}
}
/**
* Retrieve component (shielded)
*
* @return component or <code>null</code>
*
* @throws ComponentNotAlreadyActivatedEx
* @throws CannotGetComponentEx
* @throws ComponentConfigurationNotFoundEx
*/
public org.omg.CORBA.Object shieldedGetComponent (GuiMaciSupervisor supervisor, String curl)
throws ComponentNotAlreadyActivatedEx, CannotGetComponentEx, ComponentConfigurationNotFoundEx {
try {
return supervisor.managerGetComponent(curl);
} catch (NoPermissionEx exc) {
mce.handleException(supervisor, exc);
} catch (NotConnectedToManagerException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaTransientException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaNotExistException exc) {
mce.handleException(supervisor, exc);
} catch (UnknownErrorException exc) {
mce.handleException(supervisor, exc);
}
return null;
}
/**
* Release components (shielded)
*/
public void shieldedReleaseComponents (GuiMaciSupervisor supervisor, String[] curls) {
try {
supervisor.managerReleaseComponents(curls);
} catch (CorbaUnknownException exc) {
/* thrown by manager if component was never retrieved, we ignore this.*/
} catch (NoPermissionEx exc) {
mce.handleException(supervisor, exc);
} catch (NotConnectedToManagerException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaTransientException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaNotExistException exc) {
mce.handleException(supervisor, exc);
} catch (UnknownErrorException exc) {
mce.handleException(supervisor, exc);
} catch (AcsJCannotDeactivateComponentEx ex) { // @TODO remove after change in maci.idl
mce.handleException(supervisor, ex);
} catch (AcsJComponentDeactivationUncleanEx ex) {
mce.handleException(supervisor, ex);
} catch (AcsJComponentDeactivationFailedEx ex) {
mce.handleException(supervisor, ex);
}
}
/**
* Force-release component (shielded)
*/
public void shieldedForceReleaseComponent (GuiMaciSupervisor supervisor, String curl) {
try {
supervisor.managerForceReleaseComponent(curl);
} catch (NoPermissionEx exc) {
mce.handleException(supervisor, exc);
} catch (NotConnectedToManagerException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaTransientException exc) {
mce.handleException(supervisor, exc);
} catch (CorbaNotExistException exc) {
mce.handleException(supervisor, exc);
} catch (UnknownErrorException exc) {
mce.handleException(supervisor, exc);
}
}
final protected ManagerConnectionExceptionHandler mce = new ManagerConnectionExceptionHandler();
protected class ManagerConnectionExceptionHandler {
protected void handleException (OrbInitException exc) {
String msg = "Failed to initialize local orb. This prevents all corba connectivity.";
ErrorBox.showErrorDialog(DeploymentTree.this, msg, exc);
}
protected void handleException (GuiMaciSupervisor ms, NoPermissionEx exc) {
seemsManagerHasChangedOrHasCutConnection(ms);
}
protected void handleException (GuiMaciSupervisor ms, CorbaNoPermissionException exc) {
seemsManagerHasChangedOrHasCutConnection(ms);
}
protected void handleException (GuiMaciSupervisor ms, NotConnectedToManagerException exc) {
seemsWeHaveDisconnected(ms);
}
protected void handleException (GuiMaciSupervisor ms, CannotRetrieveManagerException exc) {
seemsManagerDoesNotExist(ms);
}
protected void handleException (GuiMaciSupervisor ms, CorbaNotExistException exc) {
seemsManagerDoesNotExist(ms);
}
protected void handleException (GuiMaciSupervisor ms, CorbaTransientException exc) {
seemsManagerIsDown(ms);
}
protected void handleException (IMaciSupervisor ms, UnknownErrorException exc) {
String msg = "Unforeseen error talking to manager! Please report this to the Acs team.";
ErrorBox.showErrorDialog(DeploymentTree.this, msg, exc);
}
protected void handleException (GuiMaciSupervisor ms, AcsJCannotDeactivateComponentEx exc) { // @TODO remove after change in maci.idl
seemsComponentDeactivationFailed(ms);
}
protected void handleException (GuiMaciSupervisor ms, AcsJComponentDeactivationUncleanEx exc) {
seemsComponentDeactivationFailed(ms);
}
protected void handleException (GuiMaciSupervisor ms, AcsJComponentDeactivationFailedEx exc) {
seemsComponentDeactivationFailed(ms);
}
protected void seemsManagerIsDown (GuiMaciSupervisor ms) {
String managerLoc = ms.getManagerLocation();
String msg = "Seems the manager at " + managerLoc + " is down.\n" + "Ok to remove it from the View?";
int answer = JOptionPane.showConfirmDialog(DeploymentTree.this, msg, "Communication Failed", JOptionPane.YES_NO_OPTION);
try {
if (answer == JOptionPane.OK_OPTION) {
removeManager(managerLoc, false);
}
} catch (Exception exc1) {
ErrorBox.showErrorDialog(DeploymentTree.this, "Failed to remove manager from view", exc1);
}
}
protected void seemsManagerHasChangedOrHasCutConnection (GuiMaciSupervisor ms) {
String managerLoc = ms.getManagerLocation();
String msg = "Seems the manager at " + managerLoc + " has changed or\n"
+ "has cut the connection. Will try to reconnect.";
JOptionPane.showMessageDialog(DeploymentTree.this, msg, "Communication Failed", JOptionPane.INFORMATION_MESSAGE);
try {
// dismiss old supervisor...
removeManager(managerLoc, true);
// ...and re-add it
ms.start();
addManager(ms);
} catch (Exception exc1) {
ErrorBox.showMessageDialog(DeploymentTree.this, "Failed to reconnect to manager", false);
}
}
protected void seemsWeHaveDisconnected (GuiMaciSupervisor ms) {
String managerLoc = ms.getManagerLocation();
String msg = "I have no connection to the manager at " + managerLoc + ".\n" + "Do you want to connect now?";
int answer = JOptionPane.showConfirmDialog(DeploymentTree.this, msg, "Communication Failed",
JOptionPane.YES_NO_OPTION);
try {
if (answer == JOptionPane.OK_OPTION) {
// dismiss supervisor...
removeManager(managerLoc, true);
// ...and re-add it
ms.start();
addManager(ms);
}
} catch (Exception exc1) {
ErrorBox.showMessageDialog(DeploymentTree.this, "Failed to connect to manager", false);
}
}
protected void seemsManagerDoesNotExist (GuiMaciSupervisor ms) {
String managerLoc = ms.getManagerLocation();
String msg = "No manager exists at " + managerLoc + ".\n" + "Do you want to retry to connect?";
for (;;) {
int answer = JOptionPane.showConfirmDialog(DeploymentTree.this, msg, "Communication Failed",
JOptionPane.YES_NO_OPTION);
try {
if (answer == JOptionPane.OK_OPTION) {
ms.start();
addManager(ms);
}
break;
} catch (Exception exc) {
continue;
}
}
}
private void seemsComponentDeactivationFailed (GuiMaciSupervisor ms) {
String msg = "The manager reported a problem taking down the component.\nThe component may still be active.";
ErrorBox.showMessageDialog(DeploymentTree.this, msg, true);
}
}
}