001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002 *
003 * Copyright 2000-2013, Michael Meyling <mime@qedeq.org>.
004 *
005 * "Hilbert II" is free software; you can redistribute
006 * it and/or modify it under the terms of the GNU General Public
007 * License as published by the Free Software Foundation; either
008 * version 2 of the License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU General Public License for more details.
014 */
015
016 package org.qedeq.gui.se.pane;
017
018 import java.awt.Color;
019 import java.awt.Dimension;
020 import java.awt.Font;
021 import java.awt.event.ComponentAdapter;
022 import java.awt.event.ComponentEvent;
023 import java.awt.event.MouseEvent;
024 import java.lang.reflect.InvocationTargetException;
025 import java.util.Iterator;
026 import java.util.Set;
027 import java.util.TreeSet;
028
029 import javax.swing.BorderFactory;
030 import javax.swing.Icon;
031 import javax.swing.JLabel;
032 import javax.swing.JPanel;
033 import javax.swing.JScrollPane;
034 import javax.swing.JTable;
035 import javax.swing.ListSelectionModel;
036 import javax.swing.SwingUtilities;
037 import javax.swing.event.ListSelectionEvent;
038 import javax.swing.event.ListSelectionListener;
039 import javax.swing.table.AbstractTableModel;
040 import javax.swing.table.DefaultTableCellRenderer;
041 import javax.swing.table.JTableHeader;
042 import javax.swing.table.TableColumnModel;
043 import javax.swing.text.SimpleAttributeSet;
044 import javax.swing.text.StyleConstants;
045
046 import org.qedeq.base.io.IoUtility;
047 import org.qedeq.base.trace.Trace;
048 import org.qedeq.base.utility.DateUtility;
049 import org.qedeq.base.utility.YodaUtility;
050 import org.qedeq.gui.se.util.GuiHelper;
051 import org.qedeq.kernel.bo.common.PluginCall;
052 import org.qedeq.kernel.bo.common.QedeqBo;
053 import org.qedeq.kernel.bo.common.QedeqBoSet;
054 import org.qedeq.kernel.bo.common.ServiceProcess;
055
056 import com.jgoodies.forms.builder.DefaultFormBuilder;
057 import com.jgoodies.forms.layout.CellConstraints;
058 import com.jgoodies.forms.layout.FormLayout;
059 import com.jgoodies.forms.layout.RowSpec;
060
061 /**
062 * Shows QEDEQ module specific error pane.
063 *
064 * @author Michael Meyling
065 */
066
067 public class ProcessListPane extends JPanel {
068
069 /** This class. */
070 private static final Class CLASS = ProcessListPane.class;
071
072 /** Currently selected process. */
073 private final Set selectedProcesses;
074
075 /** Start automatic refresh thread? */
076 private boolean automaticRefresh = true;
077
078 /** Table model. */
079 private ProcessListModel model = new ProcessListModel();
080
081 /** This table holds the error descriptions. */
082 private final JTable list = new JTable(model) {
083
084 /**
085 * Just return the service process of the row.
086 *
087 * @param e Mouse event.
088 * @return Tool tip text.
089 */
090 public String getToolTipText(final MouseEvent e) {
091 String tip = null;
092 java.awt.Point p = e.getPoint();
093 final int row = rowAtPoint(p);
094 final int col = columnAtPoint(p);
095 try {
096 final ServiceProcess process = model.getServiceProcess(row);
097 switch (col) {
098 case 0:
099 if (process.isBlocked()) {
100 tip = "Process is waiting";
101 } else if (process.isRunning()) {
102 tip = "Process is running";
103 } else if (process.wasFailure()) {
104 tip = "Process was stopped.";
105 } else if (process.wasSuccess()) {
106 tip = "Process has finished";
107 }
108 break;
109 case 1: tip = (process.getPluginCall() != null
110 ? process.getPluginCall().getPlugin().getPluginDescription()
111 : process.getExecutionActionDescription());
112 break;
113 case 2:
114 case 3:
115 case 4:
116 case 5: tip = process.getQedeqUrl();
117 break;
118 case 7: tip = GuiHelper.getToolTipText(process.getExecutionActionDescription());
119 break;
120 case 8: tip = GuiHelper.getToolTipText(process.getPluginCall() != null
121 ? process.getPluginCall().getParameterString() : "");
122 break;
123 default: tip = "";
124 }
125 } catch (RuntimeException ex) {
126 return super.getToolTipText(e);
127 }
128 return tip;
129 }
130
131 };
132
133 /** Write with this font attributes. */
134 private final SimpleAttributeSet errorAttrs = new SimpleAttributeSet();
135
136 /** Our scroll area. */
137 private JScrollPane scrollPane;
138
139 /**
140 * Creates new panel.
141 */
142 public ProcessListPane() {
143 super(false);
144 selectedProcesses = new TreeSet();
145 setupView();
146 }
147
148 /**
149 * Send selection events.
150 */
151 // private void selectLine() {
152 // Trace.param(CLASS, this, "selectLine", "selectedLine", selectedLine);
153 // }
154
155 /**
156 * Assembles the GUI components of the panel.
157 */
158 private final void setupView() {
159 list.setFont(new Font("Lucida Sans Unicode", Font.PLAIN, list.getFont().getSize()));
160 list.addComponentListener(new ComponentAdapter() {
161 public void componentResized(final ComponentEvent e) {
162 list.scrollRectToVisible(list.getCellRect(list.getRowCount() - 1, 0, true));
163 }
164 });
165
166 final FormLayout layout = new FormLayout(
167 "min:grow",
168 "0:grow");
169 final DefaultFormBuilder builder = new DefaultFormBuilder(layout, this);
170 builder.setBorder(BorderFactory.createEmptyBorder());
171 builder.setRowGroupingEnabled(true);
172
173 final CellConstraints cc = new CellConstraints();
174 builder.appendRow(new RowSpec("0:grow"));
175
176 list.setDefaultRenderer(Icon.class, new IconCellRenderer());
177
178 final ListSelectionModel rowSM = list.getSelectionModel();
179 rowSM.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
180
181 // if selection changes remember the process number (= selected row (starting with 0))
182 rowSM.addListSelectionListener(new ListSelectionListener() {
183 public void valueChanged(final ListSelectionEvent e) {
184 if (e.getValueIsAdjusting()) {
185 return;
186 }
187 selectedProcesses.clear();
188 if (!rowSM.isSelectionEmpty()) {
189 int minIndex = rowSM.getMinSelectionIndex();
190 int maxIndex = rowSM.getMaxSelectionIndex();
191 for (int i = minIndex; i <= maxIndex; i++) {
192 if (rowSM.isSelectedIndex(i)) {
193 final ServiceProcess process = model.getServiceProcess(i);
194 if (process != null) {
195 selectedProcesses.add(process);
196 }
197 }
198 }
199 }
200 }
201 });
202 /*
203 // doing a click shall open the edit window
204 list.addMouseListener(new MouseAdapter() {
205 public void mouseClicked(final MouseEvent e) {
206 if (e.getClickCount() == 2) {
207 Trace.trace(CLASS, this, "setupView$vmouseClicked", "doubleClick");
208 }
209 selectLine();
210 }
211 });
212
213 // pressing the enter key shall open the edit window
214 list.getActionMap().put("selectNextRowCell", new AbstractAction() {
215 public void actionPerformed(final ActionEvent event) {
216 Trace.param(CLASS, this, "setupView$actionPerformed", "event" , event);
217 selectLine();
218 }
219 });
220 */
221 scrollPane = new JScrollPane(list);
222 builder.add(scrollPane,
223 cc.xywh(builder.getColumn(), builder.getRow(), 1, 2, "fill, fill"));
224
225 StyleConstants.setForeground(this.errorAttrs, Color.red);
226 // because we override getScrollableTracksViewportWidth:
227 scrollPane.getViewport().setBackground(Color.white);
228 StyleConstants.setBackground(this.errorAttrs, Color.white); // TODO mime 20080124: testing
229
230
231 // the default table cell renderer uses a JLabel to render the heading and we want to
232 // left align the header columns
233 // TODO mime 20080415: left align with small spaces would be better
234 final JTableHeader header = list.getTableHeader();
235 ((JLabel) header.getDefaultRenderer()).setHorizontalAlignment(JLabel.LEFT);
236 //set height of header correctly
237 header.setPreferredSize(new Dimension(list.getTableHeader().getWidth(),
238 (int) (1.1 * this.getFontMetrics(getFont()).getHeight())));
239
240 changeHeaderWidth();
241 final Thread refresh = new Thread() {
242 public void run() {
243 while (automaticRefresh) {
244 updateView();
245 IoUtility.sleep(5000); // refresh every 5 seconds
246 }
247 }
248 };
249 refresh.setDaemon(true);
250 refresh.start();
251 }
252
253 /**
254 * Make 2. column smaller.
255 */
256 private void changeHeaderWidth() {
257 TableColumnModel columnModel = list.getColumnModel();
258 columnModel.getColumn(0).setPreferredWidth(24);
259 columnModel.getColumn(0).setMinWidth(24);
260 columnModel.getColumn(1).setPreferredWidth(200);
261 columnModel.getColumn(1).setMinWidth(150);
262 columnModel.getColumn(2).setPreferredWidth(200);
263 columnModel.getColumn(2).setMinWidth(150);
264 columnModel.getColumn(3).setPreferredWidth(100);
265 columnModel.getColumn(3).setMinWidth(100);
266 columnModel.getColumn(4).setPreferredWidth(100);
267 columnModel.getColumn(4).setMinWidth(100);
268 columnModel.getColumn(5).setPreferredWidth(100);
269 columnModel.getColumn(5).setMinWidth(100);
270 columnModel.getColumn(6).setPreferredWidth(60);
271 columnModel.getColumn(6).setMinWidth(60);
272 columnModel.getColumn(7).setPreferredWidth(2000);
273 columnModel.getColumn(7).setMinWidth(100);
274 columnModel.getColumn(8).setPreferredWidth(200);
275 columnModel.getColumn(8).setMinWidth(100);
276 }
277
278 /**
279 * Update from model.
280 */
281 public synchronized void updateView() {
282 final String method = "updateView";
283 Trace.begin(CLASS, this, method);
284 ((AbstractTableModel) list.getModel()).fireTableDataChanged();
285 list.invalidate();
286 this.revalidate();
287 // TODO 20130228 remove me if refresh works
288 // list.repaint();
289 // this.repaint();
290 }
291
292 public void refreshStates() {
293 Runnable stateChanged = new Runnable() {
294 public void run() {
295 updateView();
296 }
297 };
298 SwingUtilities.invokeLater(stateChanged);
299 }
300
301 /**
302 * We render our cells with this class.
303 *
304 * @author Michael Meyling
305 */
306 private static class IconCellRenderer extends DefaultTableCellRenderer {
307
308 protected void setValue(final Object value) {
309 if (value instanceof Icon) {
310 setIcon((Icon) value);
311 super.setValue(null);
312 } else {
313 setIcon(null);
314 super.setValue(value);
315 }
316 }
317
318 }
319
320 public void setRunningOnly(final boolean runningOnly) {
321 model.setOnlyRunning(runningOnly);
322 selectedProcesses.clear();
323 list.getSelectionModel().clearSelection();
324 refreshStates();
325 }
326
327 public void stopSelected() {
328 Iterator iterator = selectedProcesses.iterator();
329 while (iterator.hasNext()) {
330 final ServiceProcess process = (ServiceProcess) iterator.next();
331 if (process != null && process.isRunning()) {
332 process.interrupt();
333 }
334 }
335 }
336
337 /**
338 * Print stack trace of selected service processes to new window if the
339 * method <code>Thread.getStackTrace()</code> is supported form the currently running java
340 * version.
341 */
342 public void stackTraceSelected() {
343 if (!selectedProcesses.isEmpty()) {
344 StringBuffer result = new StringBuffer();
345 Iterator iterator = selectedProcesses.iterator();
346 while (iterator.hasNext()) {
347 final ServiceProcess process = (ServiceProcess) iterator.next();
348 if (process != null && process.isRunning()
349 && YodaUtility.existsMethod(Thread.class, "getStackTrace", new Class[] {})) {
350 StackTraceElement[] trace = new StackTraceElement[] {};
351 result.append("id ").append(process.getId());
352 try {
353 trace = (StackTraceElement[]) YodaUtility.executeMethod(
354 process.getThread(), "getStackTrace", new Class[] {}, new Object[] {});
355 } catch (NoSuchMethodException e) {
356 // ignore
357 } catch (InvocationTargetException e) {
358 // ignore
359 }
360 for (int i = 0; i < trace.length; i++) {
361 result.append(trace[i]);
362 result.append("\n");
363 }
364 result.append("\n\n");
365 }
366 }
367 (new TextPaneWindow("Stacktrace",
368 GuiHelper.readImageIcon("tango/16x16/devices/video-display.png"),
369 result.toString())).setVisible(true);
370 }
371 }
372
373
374 /**
375 * Print details of selected service processes new window.
376 */
377 public void detailsSelected() {
378 if (!selectedProcesses.isEmpty()) {
379 StringBuffer result = new StringBuffer();
380 Iterator iterator = selectedProcesses.iterator();
381 while (iterator.hasNext()) {
382 final ServiceProcess process = (ServiceProcess) iterator.next();
383 if (process != null) {
384 result.append("id ").append(process.getId());
385 String tip = "";
386 if (process.isBlocked()) {
387 tip = "Process is waiting";
388 } else if (process.isRunning()) {
389 tip = "Process is running";
390 } else if (process.wasFailure()) {
391 tip = "Process was stopped.";
392 } else if (process.wasSuccess()) {
393 tip = "Process has finished";
394 }
395 result.append("\n\tStatus: ").append(tip);
396 result.append("\n\tAction: ").append(process.getActionName());
397 result.append("\n\tModule: ").append(process.getQedeqName());
398 result.append("\n\tURL: ").append(process.getQedeqUrl());
399 result.append("\n\tStart: ").append(DateUtility.getIsoTime(process.getStart()));
400 result.append("\n\tStop: ").append((process.getStop() != 0
401 ? DateUtility.getIsoTime(process.getStop()) : ""));
402 result.append("\n\tPercentage: ").append(process.getExecutionPercentage());
403 result.append("\n\tDescription:").append(process.getExecutionActionDescription());
404 result.append("\n\tBlocked: ");
405 final QedeqBoSet blocked = process.getBlockedModules();
406 Iterator bIterator = blocked.iterator();
407 while (bIterator.hasNext()) {
408 final QedeqBo qedeq = (QedeqBo) bIterator.next();
409 result.append(" ").append(qedeq.getName());
410 }
411 result.append("\n\tCalls: ");
412 PluginCall parent = process.getPluginCall();
413 while (parent != null) {
414 parent = parent.getParentPluginCall();
415 if (parent != null) {
416 result.append("\n\t\t ").append(parent.getId());
417 tip = "";
418 if (parent.isFinished()) {
419 tip = "Call is finished";
420 } else {
421 tip = "Call is running";
422 }
423 result.append("\n\t\t Status: ").append(tip);
424 result.append("\n\t\t Module: ").append(parent.getQedeq().getName());
425 result.append("\n\t\t Plugin: ").append(parent.getPlugin().getPluginActionName());
426 result.append("\n\t\t Start: ").append(DateUtility.getIsoTime(parent.getStart()));
427 result.append("\n\t\t Stop: ").append((parent.getStop() != 0
428 ? DateUtility.getIsoTime(parent.getStop()) : ""));
429 result.append("\n\t\t Percentage: ").append(parent.getExecutionPercentage());
430 result.append("\n\t\t Description:").append(parent.getExecutionActionDescription());
431 result.append("\n\t\t Parameter: ").append(parent.getParameterString());
432 }
433 }
434 result.append("\n");
435 }
436 }
437 (new TextPaneWindow("Service Process Details",
438 GuiHelper.readImageIcon("tango/16x16/devices/video-display.png"),
439 result.toString())).setVisible(true);
440 }
441 }
442
443
444 }
|