# HG changeset patch # User Mario Torre # Date 1375696580 -7200 # Node ID 3d2faeefba5f21e4cf461600989be9a1b8be35a8 # Parent 27c09390b3ce3662c1bb9bff0ba4467592c9cc25 Progress API second part review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-August/007760.html reviewed-by: diff -r 27c09390b3ce -r 3d2faeefba5f client/core/src/main/java/com/redhat/thermostat/client/core/progress/ProgressHandle.java --- a/client/core/src/main/java/com/redhat/thermostat/client/core/progress/ProgressHandle.java Mon Aug 05 11:56:19 2013 +0200 +++ b/client/core/src/main/java/com/redhat/thermostat/client/core/progress/ProgressHandle.java Mon Aug 05 11:56:20 2013 +0200 @@ -38,40 +38,127 @@ import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.shared.locale.LocalizedString; + import java.util.Objects; import java.util.UUID; /** + * Handle that represents the current progress in performing a certain task. + * The progress can be undefined or defined. + * + *

+ * + * A {@link ProgressHandle} has a name property and a task property. the name + * is fixed for the whole running time of the progress and identify the + * {@link ProgressHandle} action as a whole. Task name represent a single step + * and can be changed. + * + * For example, a UI client that downloads a file over the network and then + * copy its content into a local database may set the {@link ProgressHandle} + * UI clients as "Performing File Copy" and the task as "Downloading file". + * Once the first task is complete, it may then change the task to + * "Copying file". + * + *

+ * + * UI clients are free to decide how to use this information to better match + * their User Interface Framework specification, for example the task or even + * progress may be hidden until the user expand the status notification area. */ public class ProgressHandle { public enum Status { STARTED, STOPPED, + TASK_CHANGED, + DETERMINATE_STATUS_CHANGED, + PROGRESS_CHANGED, + BOUNDS_CHANGED, } private UUID id; private final ActionNotifier notifier; - private String name; + private LocalizedString name; + private LocalizedString task; + private boolean indeterminate; - public ProgressHandle(String name) { + private int currentProgress; + private Range range; + + /** + * Create a new {@link ProgressHandle} with the given name and task set + * as {@link LocalizedString#EMPTY_STRING}. + */ + public ProgressHandle(LocalizedString name) { id = UUID.randomUUID(); + this.name = name; + this.task = LocalizedString.EMPTY_STRING; + this.range = new Range<>(0, 100); + this.indeterminate = true; + this.currentProgress = 0; + notifier = new ActionNotifier<>(this); } + /** + * Gets the task {@link LocalizedString} currently associated with this + * {@link ProgressHandle}. + */ + public LocalizedString getTask() { + return task; + } + + /** + * Sets the task currently associated + */ + public void setTask(LocalizedString task) { + this.task = task; + notifier.fireAction(Status.TASK_CHANGED, task); + } + public void setIndeterminate(boolean indeterminate) { this.indeterminate = indeterminate; + notifier.fireAction(Status.DETERMINATE_STATUS_CHANGED, Boolean.valueOf(this.indeterminate)); } + public Range getRange() { + return range; + } + + public void setRange(Range range) { + this.range = range; + notifier.fireAction(Status.BOUNDS_CHANGED, this.range); + } + + public void setProgress(int currentProgress) { + int min = range.getMin().intValue(); + int max = range.getMax().intValue(); + + if (currentProgress < min) { + currentProgress = min; + } else if (currentProgress > max) { + currentProgress = max; + } + + this.currentProgress = currentProgress; + notifier.fireAction(Status.PROGRESS_CHANGED, Integer.valueOf(this.currentProgress)); + } + public boolean isIndeterminate() { return indeterminate; } - public String getName() { + public int getProgress() { + return currentProgress; + } + + public LocalizedString getName() { return name; } @@ -82,7 +169,7 @@ public void stop() { notifier.fireAction(Status.STOPPED); } - + public void addProgressListener(ActionListener listener) { notifier.addActionListener(listener); } @@ -93,7 +180,7 @@ @Override public String toString() { - return name; + return name.getContents(); } @Override diff -r 27c09390b3ce -r 3d2faeefba5f client/core/src/test/java/com/redhat/thermostat/client/core/progress/ProgressHandleTest.java --- a/client/core/src/test/java/com/redhat/thermostat/client/core/progress/ProgressHandleTest.java Mon Aug 05 11:56:19 2013 +0200 +++ b/client/core/src/test/java/com/redhat/thermostat/client/core/progress/ProgressHandleTest.java Mon Aug 05 11:56:20 2013 +0200 @@ -39,23 +39,30 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.times; - import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import org.junit.Test; import org.mockito.ArgumentCaptor; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.shared.locale.LocalizedString; public class ProgressHandleTest { @SuppressWarnings({ "rawtypes", "unchecked" }) @Test - public void testProgressHandle() { + public void testProgressHandleStartStop() { ActionListener listener = mock(ActionListener.class); - ProgressHandle handle = new ProgressHandle("test #1"); + LocalizedString name = new LocalizedString("test #1"); + ProgressHandle handle = new ProgressHandle(name); + + assertEquals(name, handle.getName()); + handle.addProgressListener(listener); ArgumentCaptor captor = @@ -74,4 +81,110 @@ event = captor.getValue(); assertEquals(ProgressHandle.Status.STOPPED, event.getActionId()); } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testProgressHandleStatusChange() { + ActionListener listener = mock(ActionListener.class); + + LocalizedString name = new LocalizedString("test #1"); + ProgressHandle handle = new ProgressHandle(name); + + assertEquals(name, handle.getName()); + + handle.addProgressListener(listener); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(ActionEvent.class); + + handle.setIndeterminate(true); + + verify(listener).actionPerformed(captor.capture()); + + ActionEvent event = captor.getValue(); + assertEquals(ProgressHandle.Status.DETERMINATE_STATUS_CHANGED, event.getActionId()); + assertTrue(handle.isIndeterminate()); + assertEquals(Boolean.TRUE, event.getPayload()); + + handle.setIndeterminate(false); + + verify(listener, times(2)).actionPerformed(captor.capture()); + + event = captor.getValue(); + assertEquals(ProgressHandle.Status.DETERMINATE_STATUS_CHANGED, event.getActionId()); + assertFalse(handle.isIndeterminate()); + assertEquals(Boolean.FALSE, event.getPayload()); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testProgressHandleProgressChange() { + ActionListener listener = mock(ActionListener.class); + + LocalizedString name = new LocalizedString("test #1"); + ProgressHandle handle = new ProgressHandle(name); + + assertEquals(name, handle.getName()); + + handle.addProgressListener(listener); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(ActionEvent.class); + + handle.setProgress(5); + + verify(listener).actionPerformed(captor.capture()); + + ActionEvent event = captor.getValue(); + assertEquals(ProgressHandle.Status.PROGRESS_CHANGED, event.getActionId()); + assertEquals(5, handle.getProgress()); + assertEquals(Integer.valueOf(5), event.getPayload()); + + handle.setProgress(15); + + verify(listener, times(2)).actionPerformed(captor.capture()); + + event = captor.getValue(); + assertEquals(ProgressHandle.Status.PROGRESS_CHANGED, event.getActionId()); + assertEquals(15, handle.getProgress()); + assertEquals(Integer.valueOf(15), event.getPayload()); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testProgressHandleBoundsChange() { + ActionListener listener = mock(ActionListener.class); + + LocalizedString name = new LocalizedString("test #1"); + ProgressHandle handle = new ProgressHandle(name); + + assertEquals(name, handle.getName()); + + handle.addProgressListener(listener); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(ActionEvent.class); + + Range range = new Range(10, 20); + + handle.setRange(range); + + verify(listener).actionPerformed(captor.capture()); + + ActionEvent event = captor.getValue(); + assertEquals(ProgressHandle.Status.BOUNDS_CHANGED, event.getActionId()); + assertEquals(range, handle.getRange()); + assertEquals(range, event.getPayload()); + + range = new Range(0xCAFE, 42); + handle.setRange(range); + + verify(listener, times(2)).actionPerformed(captor.capture()); + + event = captor.getValue(); + assertEquals(ProgressHandle.Status.BOUNDS_CHANGED, event.getActionId()); + assertEquals(range, handle.getRange()); + assertEquals(range, event.getPayload()); + + } } diff -r 27c09390b3ce -r 3d2faeefba5f client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/progress/AggregateProgressComponent.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/progress/AggregateProgressComponent.java Mon Aug 05 11:56:19 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/progress/AggregateProgressComponent.java Mon Aug 05 11:56:20 2013 +0200 @@ -37,11 +37,14 @@ package com.redhat.thermostat.client.swing.internal.progress; import java.awt.BasicStroke; +import java.awt.BorderLayout; import java.awt.Component; +import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridLayout; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; @@ -51,30 +54,53 @@ import com.redhat.thermostat.client.swing.components.GradientPanel; import com.redhat.thermostat.client.swing.components.ShadowLabel; import com.redhat.thermostat.client.ui.Palette; -import com.redhat.thermostat.shared.locale.LocalizedString; @SuppressWarnings("serial") -public class AggregateProgressComponent extends GradientPanel { - +class AggregateProgressComponent extends GradientPanel { + + private JProgressBar progressBar; + private ShadowLabel taskStatus; public AggregateProgressComponent(ProgressHandle handle) { super(Palette.WHITE.getColor(), Palette.PALE_GRAY.getColor()); + setLayout(new BorderLayout()); + setBorder(new AggregateProgressComponentBorder()); JPanel panel = new JPanel(new GridLayout()); panel.setOpaque(false); - add(panel); + add(panel, BorderLayout.CENTER); - ShadowLabel text = new ShadowLabel(new LocalizedString(handle.getName())); + ShadowLabel text = new ShadowLabel(handle.getName()); panel.add(text); - JProgressBar progressBar = new JProgressBar(); - progressBar.setString(handle.getName()); - progressBar.setStringPainted(true); + progressBar = new JProgressBar(); + progressBar.setName(handle.getName().getContents()); + progressBar.setStringPainted(false); progressBar.setIndeterminate(handle.isIndeterminate()); panel.add(progressBar); + + JPanel currentTaskStatusPane = new JPanel(new GridLayout()); + currentTaskStatusPane.setOpaque(false); + + taskStatus = new ShadowLabel(); + + Font defaultFont = taskStatus.getFont(); + taskStatus.setFont(defaultFont.deriveFont(defaultFont.getSize2D() - 2.5f)); + taskStatus.setText(handle.getTask().getContents()); + currentTaskStatusPane.add(taskStatus); + + add(currentTaskStatusPane, BorderLayout.SOUTH); + } + + public JProgressBar getProgressBar() { + return progressBar; + } + + public JLabel getTaskStatus() { + return taskStatus; } private static class AggregateProgressComponentBorder extends DebugBorder { diff -r 27c09390b3ce -r 3d2faeefba5f client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/progress/ProgressNotificationArea.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/progress/ProgressNotificationArea.java Mon Aug 05 11:56:19 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/progress/ProgressNotificationArea.java Mon Aug 05 11:56:20 2013 +0200 @@ -41,11 +41,16 @@ import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; import com.redhat.thermostat.client.core.progress.ProgressHandle; +import com.redhat.thermostat.client.core.progress.ProgressHandle.Status; import com.redhat.thermostat.client.swing.components.FontAwesomeIcon; import com.redhat.thermostat.client.swing.components.Icon; import com.redhat.thermostat.client.swing.components.ShadowLabel; +import com.redhat.thermostat.common.ActionEvent; +import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.model.Range; @SuppressWarnings("serial") public class ProgressNotificationArea extends JPanel { @@ -66,10 +71,33 @@ moreTasksIcon = new FontAwesomeIcon('\uf0d8', 12); } + private void handleAction(ActionEvent actionEvent, JProgressBar progressBar) { + switch(actionEvent.getActionId()) { + case DETERMINATE_STATUS_CHANGED: + progressBar.setIndeterminate(((Boolean) actionEvent.getPayload()).booleanValue()); + break; + + case BOUNDS_CHANGED: { + @SuppressWarnings("unchecked") + Range range = (Range) actionEvent.getPayload(); + progressBar.setMinimum(range.getMin().intValue()); + progressBar.setMaximum(range.getMax().intValue()); + + } break; + + case PROGRESS_CHANGED: + progressBar.setValue(((Integer) actionEvent.getPayload()).intValue()); + break; + + default: + break; + } + } + public void setRunningTask(final ProgressHandle handle) { removeAll(); - - taskLabel.setText(handle.getName()); + + taskLabel.setText(handle.getName().getContents()); if (hasMore) { taskLabel.setIcon(moreTasksIcon); } else { @@ -78,11 +106,23 @@ add(taskLabel, BorderLayout.CENTER); - JProgressBar progressBar = new JProgressBar(); + final JProgressBar progressBar = new JProgressBar(); progressBar.setIndeterminate(handle.isIndeterminate()); add(progressBar, BorderLayout.EAST); - progressBar.setName(handle.getName()); + progressBar.setName(handle.getName().getContents()); + + handle.addProgressListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent actionEvent) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + handleAction(actionEvent, progressBar); + } + }); + } + }); runningTask = handle; diff -r 27c09390b3ce -r 3d2faeefba5f client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/progress/SwingProgressNotifier.java --- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/progress/SwingProgressNotifier.java Mon Aug 05 11:56:19 2013 +0200 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/progress/SwingProgressNotifier.java Mon Aug 05 11:56:20 2013 +0200 @@ -36,17 +36,19 @@ package com.redhat.thermostat.client.swing.internal.progress; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.swing.SwingUtilities; + import com.redhat.thermostat.client.core.progress.ProgressHandle; import com.redhat.thermostat.client.core.progress.ProgressNotifier; import com.redhat.thermostat.client.swing.SwingComponent; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.swing.SwingUtilities; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.shared.locale.LocalizedString; public class SwingProgressNotifier implements ProgressNotifier, SwingComponent { @@ -79,6 +81,10 @@ this(aggregateNotificationArea, notificationArea, glassPane, true); } + /** + * For test only, allows to build a notifier that runs update outside + * the EDT. + */ SwingProgressNotifier(AggregateNotificationPanel aggregateNotificationArea, ProgressNotificationArea notificationArea, ThermostatGlassPane glassPane, boolean runInEDT) @@ -92,6 +98,14 @@ this.runInEDT = runInEDT; } + /** + * For test only, access the internal map containing handles and + * progress components currently tracked by this notifier. + */ + Map __getTasks() { + return tasks; + } + private void handleTask(ActionEvent status, ProgressHandle handle) { switch (status.getActionId()) { case STARTED: { @@ -107,7 +121,9 @@ case STOPPED: { AggregateProgressComponent progressBar = tasks.remove(handle); - aggregateNotificationArea.removeProgress(progressBar); + if (progressBar != null) { + aggregateNotificationArea.removeProgress(progressBar); + } if (tasks.isEmpty()) { notificationArea.reset(); @@ -125,9 +141,46 @@ } } break; + + case TASK_CHANGED: { + AggregateProgressComponent progressBar = tasks.get(handle); + if (progressBar != null) { + String text = ((LocalizedString) status.getPayload()).getContents(); + progressBar.getTaskStatus().setText(text); + } + + } break; + + case DETERMINATE_STATUS_CHANGED: { + AggregateProgressComponent progressBar = tasks.get(handle); + if (progressBar != null) { + boolean state = ((Boolean) status.getPayload()).booleanValue(); + progressBar.getProgressBar().setIndeterminate(state); + } + } break; + + case BOUNDS_CHANGED: { + AggregateProgressComponent progressBar = tasks.get(handle); + if (progressBar != null) { + + @SuppressWarnings("unchecked") + Range range = (Range) status.getPayload(); + progressBar.getProgressBar().setMinimum(range.getMin().intValue()); + progressBar.getProgressBar().setMaximum(range.getMax().intValue()); + } + + } break; + + case PROGRESS_CHANGED: { + AggregateProgressComponent progressBar = tasks.get(handle); + if (progressBar != null) { + int value = ((Integer) status.getPayload()).intValue(); + progressBar.getProgressBar().setValue(value); + } + } break; default: - throw new UnsupportedOperationException("Case not implemented"); + // nothing here } } diff -r 27c09390b3ce -r 3d2faeefba5f client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/progress/SwingProgressNotifierTest.java --- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/progress/SwingProgressNotifierTest.java Mon Aug 05 11:56:19 2013 +0200 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/progress/SwingProgressNotifierTest.java Mon Aug 05 11:56:20 2013 +0200 @@ -37,9 +37,12 @@ package com.redhat.thermostat.client.swing.internal.progress; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; import static org.junit.Assert.assertTrue; +import javax.swing.JLabel; +import javax.swing.JProgressBar; import javax.swing.RepaintManager; import org.junit.Before; @@ -53,6 +56,8 @@ import com.redhat.thermostat.client.swing.internal.progress.SwingProgressNotifier.PropertyChange; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; +import com.redhat.thermostat.common.model.Range; +import com.redhat.thermostat.shared.locale.LocalizedString; public class SwingProgressNotifierTest { @@ -61,7 +66,7 @@ private ThermostatGlassPane glassPane; @BeforeClass - public void setUpOnce() { + public static void setUpOnce() { // This is needed because some other test may have installed the // EDT violation checker repaint manager. // We don't need this check here, since we are not testing Swing @@ -78,12 +83,14 @@ @SuppressWarnings({ "unchecked", "rawtypes" }) @Test - public void testNotifier() throws InterruptedException { + public void testNotifierStartStop() throws InterruptedException { ProgressNotifier notifier = new SwingProgressNotifier(aggregateNotificationArea, notificationArea, glassPane, false); ProgressHandle handle = mock(ProgressHandle.class); + when(handle.getName()).thenReturn(LocalizedString.EMPTY_STRING); + when(handle.getTask()).thenReturn(LocalizedString.EMPTY_STRING); notifier.register(handle); final boolean [] result = new boolean[1]; @@ -112,15 +119,89 @@ assertTrue(notifier.hasTasks()); - AggregateProgressComponent aggregateComponent = notificationAreaCaptor.getValue(); - event = new ActionEvent(handle, Status.STOPPED); listener.actionPerformed(event); + AggregateProgressComponent aggregateComponent = notificationAreaCaptor.getValue(); verify(aggregateNotificationArea).removeProgress(aggregateComponent); verify(notificationArea).reset(); verify(notificationArea).setHasMore(false); assertTrue(result[0]); } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test + public void testHandleStatusChanges() throws InterruptedException { + SwingProgressNotifier notifier = + new SwingProgressNotifier(aggregateNotificationArea, + notificationArea, glassPane, false); + + JProgressBar progressBar = mock(JProgressBar.class); + JLabel label = mock(JLabel.class); + AggregateProgressComponent progressComponent = + mock(AggregateProgressComponent.class); + when(progressComponent.getProgressBar()).thenReturn(progressBar); + when(progressComponent.getTaskStatus()).thenReturn(label); + + ProgressHandle handle = mock(ProgressHandle.class); + when(handle.getName()).thenReturn(LocalizedString.EMPTY_STRING); + when(handle.getTask()).thenReturn(LocalizedString.EMPTY_STRING); + notifier.register(handle); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(ActionListener.class); + verify(handle).addProgressListener(captor.capture()); + + notifier.__getTasks().put(handle, progressComponent); + ActionListener listener = captor.getValue(); + + LocalizedString textPayload = new LocalizedString("test"); + ActionEvent event = + new ActionEvent(handle, Status.TASK_CHANGED); + event.setPayload(textPayload); + listener.actionPerformed(event); + + verify(label).setText(textPayload.getContents()); + + event = new ActionEvent(handle, Status.DETERMINATE_STATUS_CHANGED); + event.setPayload(Boolean.TRUE); + listener.actionPerformed(event); + + verify(progressBar).setIndeterminate(true); + + event = new ActionEvent(handle, Status.DETERMINATE_STATUS_CHANGED); + event.setPayload(Boolean.FALSE); + listener.actionPerformed(event); + + verify(progressBar).setIndeterminate(false); + + event = new ActionEvent(handle, Status.PROGRESS_CHANGED); + event.setPayload(Integer.valueOf(10)); + listener.actionPerformed(event); + + verify(progressBar).setValue(10); + + event = new ActionEvent(handle, Status.PROGRESS_CHANGED); + event.setPayload(Integer.valueOf(99)); + listener.actionPerformed(event); + + verify(progressBar).setValue(99); + + Range range = new Range(5, 20); + event = new ActionEvent(handle, Status.BOUNDS_CHANGED); + event.setPayload(range); + listener.actionPerformed(event); + + verify(progressBar).setMinimum(5); + verify(progressBar).setMaximum(20); + + range = new Range(99, 101); + event = new ActionEvent(handle, Status.BOUNDS_CHANGED); + event.setPayload(range); + listener.actionPerformed(event); + + verify(progressBar).setMinimum(99); + verify(progressBar).setMaximum(101); + } } diff -r 27c09390b3ce -r 3d2faeefba5f vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/Activator.java --- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/Activator.java Mon Aug 05 11:56:19 2013 +0200 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/Activator.java Mon Aug 05 11:56:20 2013 +0200 @@ -46,6 +46,7 @@ import org.osgi.framework.ServiceRegistration; import com.redhat.thermostat.client.core.InformationService; +import com.redhat.thermostat.client.core.progress.ProgressNotifier; import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.common.Constants; import com.redhat.thermostat.common.MultipleServiceTracker; @@ -80,12 +81,17 @@ ObjectDetailsViewProvider.class, ObjectRootsViewProvider.class, HeapDumpListViewProvider.class, + ProgressNotifier.class, }; tracker = new MultipleServiceTracker(context, deps, new Action() { @Override public void dependenciesAvailable(Map services) { + + ProgressNotifier notifier = (ProgressNotifier) services.get(ProgressNotifier.class.getName()); + Objects.requireNonNull(notifier); + ApplicationService appSvc = (ApplicationService) services.get(ApplicationService.class.getName()); Objects.requireNonNull(appSvc); VmInfoDAO vmInfoDao = Objects.requireNonNull((VmInfoDAO) services.get(VmInfoDAO.class.getName())); @@ -115,7 +121,7 @@ vmInfoDao, vmMemoryStatDao, heapDao, viewProvider, detailsViewProvider, histogramViewProvider, objectDetailsViewProvider, objectRootsViewProvider, - heapDumpListViewProvider); + heapDumpListViewProvider, notifier); Dictionary properties = new Hashtable<>(); properties.put(Constants.GENERIC_SERVICE_CLASSNAME, VmRef.class.getName()); properties.put(InformationService.KEY_SERVICE_ID, HeapDumperService.SERVICE_ID); diff -r 27c09390b3ce -r 3d2faeefba5f vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java --- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java Mon Aug 05 11:56:19 2013 +0200 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpController.java Mon Aug 05 11:56:20 2013 +0200 @@ -50,6 +50,8 @@ import java.util.logging.Logger; import com.redhat.thermostat.client.core.controllers.InformationServiceController; +import com.redhat.thermostat.client.core.progress.ProgressHandle; +import com.redhat.thermostat.client.core.progress.ProgressNotifier; import com.redhat.thermostat.client.core.views.BasicView.Action; import com.redhat.thermostat.client.core.views.UIComponent; import com.redhat.thermostat.common.ActionEvent; @@ -104,6 +106,8 @@ private ObjectRootsViewProvider objectRootsViewProvider; private HeapDumpListViewProvider heapDumpListViewProvider; + private ProgressNotifier notifier; + public HeapDumpController(final VmMemoryStatDAO vmMemoryStatDao, final VmInfoDAO vmInfoDao, final HeapDAO heapDao, final VmRef ref, @@ -112,11 +116,13 @@ HeapHistogramViewProvider histogramProvider, ObjectDetailsViewProvider objectDetailsProvider, ObjectRootsViewProvider objectRootsProvider, - HeapDumpListViewProvider heapDumpListViewProvider) + HeapDumpListViewProvider heapDumpListViewProvider, + ProgressNotifier notifier) { this(vmMemoryStatDao, vmInfoDao, heapDao, ref, appService, viewProvider, detailsViewProvider, histogramProvider, objectDetailsProvider, - objectRootsProvider, heapDumpListViewProvider, new HeapDumper(ref)); + objectRootsProvider, heapDumpListViewProvider, new HeapDumper(ref), + notifier); } HeapDumpController(final VmMemoryStatDAO vmMemoryStatDao, @@ -129,8 +135,10 @@ ObjectDetailsViewProvider objectDetailsProvider, ObjectRootsViewProvider objectRootsProvider, HeapDumpListViewProvider heapDumpListViewProvider, - final HeapDumper heapDumper) + final HeapDumper heapDumper, + ProgressNotifier notifier) { + this.notifier = notifier; this.objectDetailsViewProvider = objectDetailsProvider; this.objectRootsViewProvider = objectRootsProvider; this.histogramViewProvider = histogramProvider; @@ -223,9 +231,7 @@ view.openExportDialog(localHeapDump); } break; - case SAVE_HEAP_DUMP: { - // FIXME: we really need some indicator that something is - // going on here, same for dumping requests + case SAVE_HEAP_DUMP: { DumpFile localHeapDump = (DumpFile) actionEvent.getPayload(); saveHeapDump(localHeapDump); } break; @@ -246,6 +252,14 @@ appService.getApplicationExecutor().execute(new Runnable() { @Override public void run() { + + LocalizedString taskName = translator.localize(LocaleResources.HEAP_DUMP_IN_PROGRESS); + + final ProgressHandle handle = new ProgressHandle(taskName); + handle.setTask(taskName); + handle.setIndeterminate(true); + notifier.register(handle); + HeapDump dump = localHeapDump.getDump(); File file = localHeapDump.getFile(); if (dump == null || file == null) { @@ -254,6 +268,7 @@ return; } + handle.start(); try (InputStream in = heapDAO.getHeapDumpData(dump.getInfo())) { Files.copy(in, file.toPath()); @@ -262,6 +277,8 @@ view.displayWarning(message); Logger.getLogger(HeapDumpController.class.getSimpleName()). log(Level.WARNING, message.getContents(), e); + } finally { + handle.stop(); } } }); diff -r 27c09390b3ce -r 3d2faeefba5f vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumperServiceImpl.java --- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumperServiceImpl.java Mon Aug 05 11:56:19 2013 +0200 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumperServiceImpl.java Mon Aug 05 11:56:20 2013 +0200 @@ -39,6 +39,7 @@ import com.redhat.thermostat.client.core.Filter; import com.redhat.thermostat.client.core.NameMatchingRefFilter; import com.redhat.thermostat.client.core.controllers.InformationServiceController; +import com.redhat.thermostat.client.core.progress.ProgressNotifier; import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.storage.core.VmRef; import com.redhat.thermostat.storage.dao.VmInfoDAO; @@ -68,6 +69,8 @@ private ObjectDetailsViewProvider objectDetailsViewProvider; private ObjectRootsViewProvider objectRootsViewProvider; + private ProgressNotifier notifier; + private HeapDumpListViewProvider heapDumpListViewProvider; public HeapDumperServiceImpl(ApplicationService appService, @@ -77,7 +80,8 @@ HeapHistogramViewProvider histogramViewProvider, ObjectDetailsViewProvider objectDetailsViewProvider, ObjectRootsViewProvider objectRootsViewProvider, - HeapDumpListViewProvider heapDumpListViewProvider) { + HeapDumpListViewProvider heapDumpListViewProvider, + ProgressNotifier notifier) { this.vmInfoDao = vmInfoDao; this.vmMemoryStatDao = vmMemoryStatDao; this.heapDao = heapDao; @@ -88,13 +92,14 @@ this.objectDetailsViewProvider = objectDetailsViewProvider; this.objectRootsViewProvider = objectRootsViewProvider; this.heapDumpListViewProvider = heapDumpListViewProvider; + this.notifier = notifier; } @Override public InformationServiceController getInformationServiceController(VmRef ref) { return new HeapDumpController(vmMemoryStatDao, vmInfoDao, heapDao, ref, appService, viewProvider, detailsViewProvider, histogramViewProvider, objectDetailsViewProvider, - objectRootsViewProvider, heapDumpListViewProvider); + objectRootsViewProvider, heapDumpListViewProvider, notifier); } @Override diff -r 27c09390b3ce -r 3d2faeefba5f vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/ActivatorTest.java --- a/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/ActivatorTest.java Mon Aug 05 11:56:19 2013 +0200 +++ b/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/ActivatorTest.java Mon Aug 05 11:56:20 2013 +0200 @@ -44,6 +44,7 @@ import org.junit.Test; import com.redhat.thermostat.client.core.InformationService; +import com.redhat.thermostat.client.core.progress.ProgressNotifier; import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.storage.dao.VmInfoDAO; import com.redhat.thermostat.testutils.StubBundleContext; @@ -88,6 +89,7 @@ ObjectDetailsViewProvider objectDetailsViewProvider = mock(ObjectDetailsViewProvider.class); ObjectRootsViewProvider objectRootsViewProvider = mock(ObjectRootsViewProvider.class); HeapDumpListViewProvider heapDumpListViewProvider = mock(HeapDumpListViewProvider.class); + ProgressNotifier progressNotifier = mock(ProgressNotifier.class); context.registerService(VmInfoDAO.class, vmInfoDao, null); context.registerService(VmMemoryStatDAO.class, vmMemoryStatDAO, null); @@ -100,6 +102,7 @@ context.registerService(ObjectDetailsViewProvider.class, objectDetailsViewProvider, null); context.registerService(ObjectRootsViewProvider.class, objectRootsViewProvider, null); context.registerService(HeapDumpListViewProvider.class, heapDumpListViewProvider, null); + context.registerService(ProgressNotifier.class, progressNotifier, null); Activator activator = new Activator(); @@ -110,7 +113,7 @@ activator.stop(context); assertEquals(0, context.getServiceListeners().size()); - assertEquals(10, context.getAllServices().size()); + assertEquals(11, context.getAllServices().size()); } } diff -r 27c09390b3ce -r 3d2faeefba5f vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java --- a/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java Mon Aug 05 11:56:19 2013 +0200 +++ b/vm-heap-analysis/client-core/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/core/internal/HeapDumpControllerTest.java Mon Aug 05 11:56:20 2013 +0200 @@ -38,7 +38,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; - import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.isA; @@ -68,6 +67,7 @@ import org.mockito.stubbing.Answer; import com.lowagie.text.pdf.codec.Base64.InputStream; +import com.redhat.thermostat.client.core.progress.ProgressNotifier; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ApplicationCache; @@ -130,6 +130,8 @@ private HeapDumpListViewProvider heapDumpListViewProvider; + private ProgressNotifier notifier; + @Before public void setUp() { heapDao = mock(HeapDAO.class); @@ -141,6 +143,8 @@ appService = mock(ApplicationService.class); heapDumper = mock(HeapDumper.class); + notifier = mock(ProgressNotifier.class); + heapDumpListViewProvider = mock(HeapDumpListViewProvider.class); setUpView(); @@ -207,7 +211,7 @@ controller = new HeapDumpController(vmDao, vmInfoDao, heapDao, ref, appService, viewProvider, detailsViewProvider, histogramProvider, objectDetailsProvider, objectRootsProvider, heapDumpListViewProvider, - heapDumper); + heapDumper, notifier); } @After @@ -292,7 +296,7 @@ controller = new HeapDumpController(vmDao, vmInfoDao, heapDao, ref, appService, viewProvider, detailsViewProvider, histogramProvider, objectDetailsProvider, objectRootsProvider, heapDumpListViewProvider, - heapDumper); + heapDumper, notifier); verify(view, times(1)).setChildView(any(HeapView.class)); verify(view, times(1)).openDumpView(); @@ -318,7 +322,7 @@ controller = new HeapDumpController(vmDao, vmInfoDao, heapDao, ref, appService, viewProvider, detailsViewProvider, histogramProvider, objectDetailsProvider, objectRootsProvider, heapDumpListViewProvider, - heapDumper); + heapDumper, notifier); verify(view, times(0)).openDumpView(); } @@ -339,7 +343,7 @@ controller = new HeapDumpController(vmDao, vmInfoDao, heapDao, ref, appService, viewProvider, detailsViewProvider, histogramProvider, objectDetailsProvider, objectRootsProvider, heapDumpListViewProvider, - heapDumper); + heapDumper, notifier); verify(view).disableHeapDumping(DumpDisabledReason.PROCESS_DEAD); }