/*
* Copyright to the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rioproject.tools.ui.servicenotification;
import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.discovery.DiscoveryManagement;
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.treetable.AbstractMutableTreeTableNode;
import org.rioproject.eventcollector.api.EventCollector;
import org.rioproject.eventcollector.api.UnknownEventCollectorRegistration;
import org.rioproject.tools.ui.AbstractNotificationUtility;
import org.rioproject.tools.ui.ChainedRemoteEventListener;
import org.rioproject.tools.ui.servicenotification.filter.FilterCriteria;
import org.rioproject.tools.ui.servicenotification.filter.FilterListener;
import org.rioproject.tools.ui.servicenotification.filter.FilterPanel;
import org.rioproject.ui.Util;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumnModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.rmi.server.ExportException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
/**
* Utility to display {@link org.rioproject.event.RemoteServiceEvent}s using either an {@link EventCollector} or
* subscribe to services for notification(s). The utility also provides filtering support as well as other handy
* options.
*
* @author Dennis Reedy
*/
public class RemoteEventTable extends AbstractNotificationUtility {
private final JXTreeTable eventTable;
private final RemoteEventTreeModel dataModel;
private final ChainedRemoteEventListener remoteEventListener;
private final RemoteEventConsumerManager eventConsumerManager;
private DiscoveryManagement dMgr;
private final Configuration config;
private final EventColorManager eventColorManager = new EventColorManager();
private final FilterPanel filterPanel;
private final RemoteServiceEventDetailsTable detailsTable;
private final JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true);
public RemoteEventTable(final Configuration config, final Properties props) throws ExportException, ConfigurationException {
super(new BorderLayout(8, 8));
this.config = config;
JPanel topPanel = new JPanel(new BorderLayout(8, 8));
eventConsumerManager = new RemoteEventConsumerManager();
filterPanel = new FilterPanel(new FilterApplier(), new TreeExpander(), new EventCollectorControl(this), props);
topPanel.add(filterPanel, BorderLayout.NORTH);
java.util.List<String> columns = new ArrayList<String>();
columns.add("Deployment");
columns.add("Description");
columns.add("When");
dataModel = new RemoteEventTreeModel(new RootNode(), columns);
UIDefaults defaults = UIManager.getDefaults( );
Icon openIcon = defaults.getIcon("Tree.expandedIcon");
Icon closedIcon = defaults.getIcon("Tree.collapsedIcon");
Color normalBackground = new Color(215,225, 205);
Color warningBackground = new Color(255, 245, 205);
Color minorBackground = new Color(255, 235, 205);
Color criticalColor = new Color(245, 205, 205);
Color indeterminateColor = new Color(235, 235, 205);
eventTable = new JXTreeTable(dataModel);
eventTable.setRootVisible(false);
ColorHighlighter normalHighlighter = new ColorHighlighter(new HighlightPredicate() {
@Override
public boolean isHighlighted(Component component, ComponentAdapter componentAdapter) {
if(!componentAdapter.isLeaf())
return false;
Object value = componentAdapter.getValue(0);
return value != null && eventColorManager.isNormal((String) value);
}
});
normalHighlighter.setBackground(normalBackground);
ColorHighlighter indeterminateHighlighter = new ColorHighlighter(new HighlightPredicate() {
@Override
public boolean isHighlighted(Component component, ComponentAdapter componentAdapter) {
if(!componentAdapter.isLeaf())
return false;
Object value = componentAdapter.getValue(0);
return value != null && eventColorManager.isIndeterminate((String) value);
}
});
indeterminateHighlighter.setBackground(indeterminateColor);
ColorHighlighter minorHighlighter = new ColorHighlighter(new HighlightPredicate() {
@Override
public boolean isHighlighted(Component component, ComponentAdapter componentAdapter) {
Object value = componentAdapter.getValue(0);
return value != null && eventColorManager.isMinor((String) value);
}
});
minorHighlighter.setBackground(minorBackground);
ColorHighlighter warningHighlighter = new ColorHighlighter(new HighlightPredicate() {
@Override
public boolean isHighlighted(Component component, ComponentAdapter componentAdapter) {
Object value = componentAdapter.getValue(0);
return value != null && eventColorManager.isWarning((String)value);
}
});
warningHighlighter.setBackground(warningBackground);
ColorHighlighter criticalHighlighter = new ColorHighlighter(new HighlightPredicate() {
@Override
public boolean isHighlighted(Component component, ComponentAdapter componentAdapter) {
Object value = componentAdapter.getValue(0);
return value != null && eventColorManager.isCritical((String) value);
}
});
criticalHighlighter.setBackground(criticalColor);
eventTable.addHighlighter(normalHighlighter);
eventTable.addHighlighter(warningHighlighter);
eventTable.addHighlighter(criticalHighlighter);
eventTable.addHighlighter(minorHighlighter);
eventTable.addHighlighter(indeterminateHighlighter);
dataModel.setTreeTable(eventTable);
eventTable.setShowsRootHandles(false);
eventTable.setAutoCreateColumnsFromModel(false);
//eventTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
detailsTable = new RemoteServiceEventDetailsTable();
JPanel detailsPanel = new JPanel(new BorderLayout(8, 8));
detailsPanel.add(detailsTable);
eventTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if(!e.getValueIsAdjusting()) {
detailsTable.setRemoteServiceEventNode(getRemoteServiceEventNode(eventTable.getSelectedRow()));
}
}
});
//no icons
eventTable.setLeafIcon(null);
eventTable.setOpenIcon(openIcon);
eventTable.setClosedIcon(closedIcon);
eventTable.addMouseListener(new RowListener());
TableColumnModel cm = eventTable.getColumnModel();
cm.getColumn(0).setPreferredWidth(60);
cm.getColumn(0).setWidth(60);
cm.getColumn(0).setMaxWidth(500);
cm.getColumn(1).setPreferredWidth(400);
cm.getColumn(1).setMaxWidth(1000);
cm.getColumn(2).setPreferredWidth(100);
cm.getColumn(2).setMaxWidth(500);
final JScrollPane scroller = new JScrollPane(eventTable);
scroller.getViewport().setBackground(Color.WHITE);
topPanel.add(scroller, BorderLayout.CENTER);
splitPane.add(topPanel, JSplitPane.TOP);
splitPane.add(detailsPanel, JSplitPane.BOTTOM);
splitPane.setDividerSize(8);
add(splitPane, BorderLayout.CENTER);
/* Create the event consumer for EventCollector notification */
remoteEventListener = new ChainedRemoteEventListener(new RemoteEventConsumer(this), config);
}
public void init(final Properties props) {
int dividerLocation = splitPane.getHeight()-splitPane.getHeight()/6;
splitPane.setDividerLocation(dividerLocation);
}
public boolean getUseEventCollector() {
return filterPanel.getUseEventCollector();
}
public int getDividerLocation() {
return splitPane.getDividerLocation();
}
public void expandAll() {
try {
eventTable.expandAll();
} catch(IllegalArgumentException e) {
Util.showError(e, this, "Could not expand tree");
}
}
public RemoteEventTreeModel getDataModel() {
return dataModel;
}
public void setDiscoveryManagement(final DiscoveryManagement dMgr) throws Exception {
this.dMgr = dMgr;
createEventListener();
}
public void createEventListener() throws Exception {
if(getUseEventCollector()) {
eventConsumerManager.terminate();
eventConsumerManager.setUseEventCollector(true);
eventConsumerManager.registerForEventCollectorNotification(remoteEventListener, config);
} else {
if(dMgr==null)
throw new IllegalStateException("Cannot register for service notifications without a DiscoveryManagement instance");
eventConsumerManager.terminate();
eventConsumerManager.setUseEventCollector(false);
eventConsumerManager.registerForAllServiceNotification(new RemoteEventConsumer(this), dMgr);
}
}
public void addEventCollector(final EventCollector eventCollector) throws LeaseDeniedException,
IOException,
UnknownEventCollectorRegistration {
eventConsumerManager.addEventCollector(eventCollector);
filterPanel.setUseEventCollectorCheckBoxText();
}
public void removeEventCollector(final EventCollector eventCollector) {
eventConsumerManager.removeEventCollector(eventCollector);
filterPanel.setUseEventCollectorCheckBoxText();
}
public void terminate() {
remoteEventListener.terminate();
if(eventConsumerManager!=null)
eventConsumerManager.terminate();
}
private RemoteServiceEventNode getRemoteServiceEventNode(final int row) {
if(row==-1)
return null;
TreePath path = eventTable.getPathForRow(row);
if(path==null)
return null;
return dataModel.getRemoteServiceEventNode(row);
}
public int getTotalItemCount() {
int rowCounter = 0;
for(DeploymentNode dNode : dataModel.getDeploymentNodes()) {
for(int i=0; i<dNode.getChildCount(); i++) {
RemoteServiceEventNode rNode = (RemoteServiceEventNode)dNode.getChildAt(i);
if(eventColorManager.isCritical((String)rNode.getValueAt(0))) {
rowCounter++;
}
}
}
//return dataModel.getRowCount();
return rowCounter;
}
class RowListener extends MouseAdapter {
public void mouseClicked(final MouseEvent e) {
int clickCount = e.getClickCount();
int row = eventTable.rowAtPoint(new Point(e.getX(), e.getY()));
if(row==-1)
return;
if(clickCount==1) {
AbstractMutableTreeTableNode node = dataModel.getNode(row);
if(node instanceof DeploymentNode) {
if(eventTable.isExpanded(row)) {
eventTable.collapseRow(row);
} else {
eventTable.expandRow(row);
}
}
}
}
public void mousePressed(final MouseEvent e) {
maybeShowPopup(e);
}
public void mouseReleased(final MouseEvent e) {
maybeShowPopup(e);
}
void maybeShowPopup(final MouseEvent e) {
if(e.isPopupTrigger()) {
JPopupMenu popup = new JPopupMenu();
JMenuItem delete = new JMenuItem("Delete");
delete.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent ae) {
int[] rows = eventTable.getSelectedRows();
for(int i=rows.length-1;i>=0;i--){
dataModel.removeItem(rows[i]-i);
}
notifyListeners();
}
});
popup.add(delete);
popup.pack();
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
}
class TreeExpander implements TreeExpansionListener {
@Override
public void expand() {
eventTable.expandAll();
}
@Override
public void collapse() {
int row = eventTable.getRowCount() - 1;
while (row >= 0) {
eventTable.collapseRow(row);
row--;
}
}
}
class EventCollectorControl implements EventCollectorListener {
Component parent;
EventCollectorControl(final Component parent) {
this.parent = parent;
}
@Override
public void handleEventCollectorRegistration(final boolean useEventCollector) {
try {
createEventListener();
} catch (Exception e) {
Util.showError(e, parent, "Could not create Event Listener");
}
}
@Override
public int getEventControllerCount() {
return eventConsumerManager.getEventControllerCount();
}
@Override
public void refresh() {
try {
Map<DeploymentNode, Boolean> nodes = new LinkedHashMap<DeploymentNode, Boolean>();
for(DeploymentNode dNode : dataModel.getDeploymentNodes()) {
int row = dataModel.getDeploymentNodeRow(dNode);
boolean expanded = row != -1 && eventTable.isExpanded(dataModel.getDeploymentNodeRow(dNode));
nodes.put(dNode, expanded);
}
dataModel.reset();
eventConsumerManager.refresh();
for(Map.Entry<DeploymentNode, Boolean> entry : nodes.entrySet()) {
if(entry.getValue()) {
int row = dataModel.getDeploymentNodeRow(entry.getKey());
eventTable.expandRow(dataModel.getDeploymentNodeRow(entry.getKey()));
dataModel.updated(eventTable.getPathForRow(row));
}
}
} catch (UnknownEventCollectorRegistration e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class FilterApplier implements FilterListener {
public void notify(final FilterCriteria filterCriteria) {
if(filterCriteria==null && dataModel.getFilterCriteria()==null)
return;
dataModel.setFilterCriteria(filterCriteria);
eventTable.expandAll();
}
}
}