001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002 *
003 * Copyright 2000-2014, 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.ModuleServiceCall;
052 import org.qedeq.kernel.bo.common.QedeqBo;
053 import org.qedeq.kernel.bo.common.QedeqBoSet;
054 import org.qedeq.kernel.bo.common.ServiceJob;
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 ServiceJob 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.getModuleServiceCall() != null
110 ? process.getModuleServiceCall().getService().getServiceDescription()
111 : process.getExecutionActionDescription());
112 break;
113 case 2:
114 case 3:
115 case 4:
116 case 5: tip = getUrlList(process.getCurrentlyProcessedModules());
117 break;
118 case 7: tip = GuiHelper.getToolTipText(process.getExecutionActionDescription());
119 break;
120 case 8: tip = GuiHelper.getToolTipText(process.getModuleServiceCall() != null
121 ? process.getModuleServiceCall().getParametersString() + "; "
122 + process.getModuleServiceCall().getParametersString() : "");
123 break;
124 default: tip = "";
125 }
126 } catch (RuntimeException ex) {
127 return super.getToolTipText(e);
128 }
129 return tip;
130 }
131
132 };
133
134 private String getUrlList(final QedeqBo[] list) {
135 final StringBuffer result = new StringBuffer();
136 for (int i = 0; i < list.length; i++) {
137 if (i > 0) {
138 result.append(", ");
139 }
140 result.append(list[i].getUrl());
141 }
142 return result.toString();
143 }
144
145 /** Write with this font attributes. */
146 private final SimpleAttributeSet errorAttrs = new SimpleAttributeSet();
147
148 /** Our scroll area. */
149 private JScrollPane scrollPane;
150
151 /**
152 * Creates new panel.
153 */
154 public ProcessListPane() {
155 super(false);
156 selectedProcesses = new TreeSet();
157 setupView();
158 }
159
160 /**
161 * Send selection events.
162 */
163 // private void selectLine() {
164 // Trace.param(CLASS, this, "selectLine", "selectedLine", selectedLine);
165 // }
166
167 /**
168 * Assembles the GUI components of the panel.
169 */
170 private final void setupView() {
171 list.setFont(new Font("Lucida Sans Unicode", Font.PLAIN, list.getFont().getSize()));
172 list.addComponentListener(new ComponentAdapter() {
173 public void componentResized(final ComponentEvent e) {
174 list.scrollRectToVisible(list.getCellRect(list.getRowCount() - 1, 0, true));
175 }
176 });
177
178 final FormLayout layout = new FormLayout(
179 "min:grow",
180 "0:grow");
181 final DefaultFormBuilder builder = new DefaultFormBuilder(layout, this);
182 builder.setBorder(BorderFactory.createEmptyBorder());
183 builder.setRowGroupingEnabled(true);
184
185 final CellConstraints cc = new CellConstraints();
186 builder.appendRow(new RowSpec("0:grow"));
187
188 list.setDefaultRenderer(Icon.class, new IconCellRenderer());
189
190 final ListSelectionModel rowSM = list.getSelectionModel();
191 rowSM.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
192
193 // if selection changes remember the process number (= selected row (starting with 0))
194 rowSM.addListSelectionListener(new ListSelectionListener() {
195 public void valueChanged(final ListSelectionEvent e) {
196 if (e.getValueIsAdjusting()) {
197 return;
198 }
199 selectedProcesses.clear();
200 if (!rowSM.isSelectionEmpty()) {
201 int minIndex = rowSM.getMinSelectionIndex();
202 int maxIndex = rowSM.getMaxSelectionIndex();
203 for (int i = minIndex; i <= maxIndex; i++) {
204 if (rowSM.isSelectedIndex(i)) {
205 final ServiceJob process = model.getServiceProcess(i);
206 if (process != null) {
207 selectedProcesses.add(process);
208 }
209 }
210 }
211 }
212 }
213 });
214 /*
215 // doing a click shall open the edit window
216 list.addMouseListener(new MouseAdapter() {
217 public void mouseClicked(final MouseEvent e) {
218 if (e.getClickCount() == 2) {
219 Trace.trace(CLASS, this, "setupView$vmouseClicked", "doubleClick");
220 }
221 selectLine();
222 }
223 });
224
225 // pressing the enter key shall open the edit window
226 list.getActionMap().put("selectNextRowCell", new AbstractAction() {
227 public void actionPerformed(final ActionEvent event) {
228 Trace.param(CLASS, this, "setupView$actionPerformed", "event" , event);
229 selectLine();
230 }
231 });
232 */
233 scrollPane = new JScrollPane(list);
234 builder.add(scrollPane,
235 cc.xywh(builder.getColumn(), builder.getRow(), 1, 2, "fill, fill"));
236
237 StyleConstants.setForeground(this.errorAttrs, Color.red);
238 // because we override getScrollableTracksViewportWidth:
239 scrollPane.getViewport().setBackground(Color.white);
240 StyleConstants.setBackground(this.errorAttrs, Color.white); // TODO mime 20080124: testing
241
242
243 // the default table cell renderer uses a JLabel to render the heading and we want to
244 // left align the header columns
245 // TODO mime 20080415: left align with small spaces would be better
246 final JTableHeader header = list.getTableHeader();
247 ((JLabel) header.getDefaultRenderer()).setHorizontalAlignment(JLabel.LEFT);
248 //set height of header correctly
249 header.setPreferredSize(new Dimension(list.getTableHeader().getWidth(),
250 (int) (1.1 * this.getFontMetrics(getFont()).getHeight())));
251
252 changeHeaderWidth();
253 final Thread refresh = new Thread() {
254 public void run() {
255 while (automaticRefresh) {
256 updateView();
257 IoUtility.sleep(5000); // refresh every 5 seconds
258 }
259 }
260 };
261 refresh.setDaemon(true);
262 refresh.start();
263 }
264
265 /**
266 * Make 2. column smaller.
267 */
268 private void changeHeaderWidth() {
269 TableColumnModel columnModel = list.getColumnModel();
270 columnModel.getColumn(0).setPreferredWidth(24);
271 columnModel.getColumn(0).setMinWidth(24);
272 columnModel.getColumn(1).setPreferredWidth(200);
273 columnModel.getColumn(1).setMinWidth(150);
274 columnModel.getColumn(2).setPreferredWidth(200);
275 columnModel.getColumn(2).setMinWidth(150);
276 columnModel.getColumn(3).setPreferredWidth(100);
277 columnModel.getColumn(3).setMinWidth(100);
278 columnModel.getColumn(4).setPreferredWidth(100);
279 columnModel.getColumn(4).setMinWidth(100);
280 columnModel.getColumn(5).setPreferredWidth(100);
281 columnModel.getColumn(5).setMinWidth(100);
282 columnModel.getColumn(6).setPreferredWidth(60);
283 columnModel.getColumn(6).setMinWidth(60);
284 columnModel.getColumn(7).setPreferredWidth(2000);
285 columnModel.getColumn(7).setMinWidth(100);
286 columnModel.getColumn(8).setPreferredWidth(200);
287 columnModel.getColumn(8).setMinWidth(100);
288 }
289
290 /**
291 * Update from model.
292 */
293 public synchronized void updateView() {
294 final String method = "updateView";
295 Trace.begin(CLASS, this, method);
296 ((AbstractTableModel) list.getModel()).fireTableDataChanged();
297 list.invalidate();
298 this.revalidate();
299 // TODO 20130228 remove me if refresh works
300 // list.repaint();
301 // this.repaint();
302 }
303
304 public void refreshStates() {
305 Runnable stateChanged = new Runnable() {
306 public void run() {
307 updateView();
308 }
309 };
310 SwingUtilities.invokeLater(stateChanged);
311 }
312
313 /**
314 * We render our cells with this class.
315 *
316 * @author Michael Meyling
317 */
318 private static class IconCellRenderer extends DefaultTableCellRenderer {
319
320 protected void setValue(final Object value) {
321 if (value instanceof Icon) {
322 setIcon((Icon) value);
323 super.setValue(null);
324 } else {
325 setIcon(null);
326 super.setValue(value);
327 }
328 }
329
330 }
331
332 public void setRunningOnly(final boolean runningOnly) {
333 model.setOnlyRunning(runningOnly);
334 selectedProcesses.clear();
335 list.getSelectionModel().clearSelection();
336 refreshStates();
337 }
338
339 public void stopSelected() {
340 Iterator iterator = selectedProcesses.iterator();
341 while (iterator.hasNext()) {
342 final ServiceJob process = (ServiceJob) iterator.next();
343 if (process != null && process.isRunning()) {
344 process.interrupt();
345 }
346 }
347 }
348
349 /**
350 * Print stack trace of selected service processes to new window if the
351 * method <code>Thread.getStackTrace()</code> is supported form the currently running java
352 * version.
353 */
354 public void stackTraceSelected() {
355 if (!selectedProcesses.isEmpty()) {
356 StringBuffer result = new StringBuffer();
357 Iterator iterator = selectedProcesses.iterator();
358 while (iterator.hasNext()) {
359 final ServiceJob process = (ServiceJob) iterator.next();
360 if (process != null && process.isRunning()
361 && YodaUtility.existsMethod(Thread.class, "getStackTrace", new Class[] {})) {
362 StackTraceElement[] trace = new StackTraceElement[] {};
363 result.append("id ").append(process.getId());
364 try {
365 trace = (StackTraceElement[]) YodaUtility.executeMethod(
366 process.getThread(), "getStackTrace", new Class[] {}, new Object[] {});
367 } catch (NoSuchMethodException e) {
368 // ignore
369 } catch (InvocationTargetException e) {
370 // ignore
371 }
372 for (int i = 0; i < trace.length; i++) {
373 result.append(trace[i]);
374 result.append("\n");
375 }
376 result.append("\n\n");
377 }
378 }
379 (new TextPaneWindow("Stacktrace",
380 GuiHelper.readImageIcon("tango/16x16/devices/video-display.png"),
381 result.toString())).setVisible(true);
382 }
383 }
384
385
386 /**
387 * Print details of selected service processes new window.
388 */
389 public void detailsSelected() {
390 if (!selectedProcesses.isEmpty()) {
391 StringBuffer result = new StringBuffer();
392 Iterator iterator = selectedProcesses.iterator();
393 while (iterator.hasNext()) {
394 final ServiceJob process = (ServiceJob) iterator.next();
395 if (process != null) {
396 result.append("id ").append(process.getId());
397 String tip = "";
398 if (process.isBlocked()) {
399 tip = "Process is waiting";
400 } else if (process.isRunning()) {
401 tip = "Process is running";
402 } else if (process.wasFailure()) {
403 tip = "Process was stopped.";
404 } else if (process.wasSuccess()) {
405 tip = "Process has finished";
406 }
407 result.append("\n\tStatus: ").append(tip);
408 result.append("\n\tAction: ").append(process.getActionName());
409 result.append("\n\tModule: ").append(process.getQedeqName());
410 result.append("\n\tURL: ").append(process.getQedeqUrl());
411 result.append("\n\tStart: ").append(DateUtility.getIsoTime(process.getStart()));
412 result.append("\n\tStop: ").append((process.getStop() != 0
413 ? DateUtility.getIsoTime(process.getStop()) : ""));
414 result.append("\n\tPercentage: ").append(process.getExecutionPercentage());
415 result.append("\n\tDescription:").append(process.getExecutionActionDescription());
416 result.append("\n\tBlocked: ");
417 final QedeqBoSet blocked = process.getBlockedModules();
418 Iterator bIterator = blocked.iterator();
419 while (bIterator.hasNext()) {
420 final QedeqBo qedeq = (QedeqBo) bIterator.next();
421 result.append(" ").append(qedeq.getName());
422 }
423 result.append("\n\tCalls: ");
424 ModuleServiceCall parent = process.getModuleServiceCall();
425 while (parent != null) {
426 result.append("\n\t\t ").append(parent.getId());
427 tip = "";
428 if (!parent.isRunning()) {
429 tip = "Call is finished";
430 } else if (parent.isPaused()) {
431 tip = "Call is paused";
432 } else {
433 tip = "Call is running";
434 }
435 result.append("\n\t\t Status: ").append(tip);
436 result.append("\n\t\t Module: ").append(parent.getQedeq().getName());
437 result.append("\n\t\t Plugin: ").append(parent.getService().getServiceAction());
438 result.append("\n\t\t Begin: ").append(DateUtility.getIsoTime(parent.getBeginTime()));
439 result.append("\n\t\t End: ").append((!parent.isRunning()
440 ? DateUtility.getIsoTime(parent.getEndTime()) : ""));
441 result.append("\n\t\t Percentage: ").append(parent.getExecutionPercentage());
442 result.append("\n\t\t Description:").append(parent.getAction());
443 result.append("\n\t\t Config Par.:").append(parent.getConfigParametersString());
444 result.append("\n\t\t Parameter: ").append(parent.getParametersString());
445
446 parent = parent.getParentServiceCall();
447 }
448 result.append("\n");
449 }
450 }
451 (new TextPaneWindow("Service Process Details",
452 GuiHelper.readImageIcon("tango/16x16/devices/video-display.png"),
453 result.toString())).setVisible(true);
454 }
455 }
456
457
458 }
|