Issue #2302 Fixed simultaneous save issue. Cleaned up AutoSave.

Change-Id: I3d9b8b2441a0b79d393ab4591450b084d24dafc3

Former-commit-id: 559e27a75f [formerly 66032fcfd7] [formerly f75dc91293] [formerly ab4c1ebe5d [formerly f75dc91293 [formerly 29e2ea5d61af71a366d3a81908b16b4c6a6850f6]]]
Former-commit-id: ab4c1ebe5d
Former-commit-id: 2ba0e690bd1e8ad733aed2a649ff316d58215905 [formerly 2670db83fd]
Former-commit-id: 877f9b9bc3
This commit is contained in:
Ron Anderson 2013-08-27 18:54:14 -05:00
parent 8e26902f09
commit a5ed604867
6 changed files with 350 additions and 222 deletions

View file

@ -20,10 +20,12 @@
package com.raytheon.viz.gfe.actions;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
@ -42,8 +44,9 @@ import com.raytheon.viz.ui.dialogs.ICloseCallback;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jan 23, 2008 Eric Babin Initial Creation
* Jul 8, 2008 randerso reworked
* Jul 8, 2008 randerso reworked
* Oct 23, 2012 1287 rferrel Changes for non-blocking AutoSaveIntervalDialog.
* Aug 27, 2013 2302 randerso Code clean up for AutoSaveJob changes
*
* </pre>
*
@ -51,7 +54,7 @@ import com.raytheon.viz.ui.dialogs.ICloseCallback;
* @version 1.0
*/
public class ShowAutoSaveIntervalDialog extends AbstractHandler {
private AutoSaveIntervalDialog dialog;
final private Map<IWorkbenchWindow, AutoSaveIntervalDialog> dialogMap = new HashMap<IWorkbenchWindow, AutoSaveIntervalDialog>();
/**
*
@ -69,47 +72,28 @@ public class ShowAutoSaveIntervalDialog extends AbstractHandler {
*/
@Override
public Object execute(ExecutionEvent arg0) throws ExecutionException {
final IWorkbenchWindow window = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow();
DataManager dm = DataManagerUIFactory.findInstance(window);
if (dm == null) {
return null;
}
AutoSaveIntervalDialog dialog = dialogMap.get(window);
if (dialog == null || dialog.getShell() == null || dialog.isDisposed()) {
final IWorkbenchWindow window = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow();
Shell shell = window.getShell();
int interval = AutoSaveJob.getInterval();
boolean autoSaveEnabled = interval > 0;
if (!autoSaveEnabled) {
interval = AutoSaveJob.MAX_INTERVAL;
}
dialog = new AutoSaveIntervalDialog(shell, interval,
autoSaveEnabled);
AutoSaveJob autoSaveJob = dm.getAutoSaveJob();
Shell shell = window.getShell();
dialog = new AutoSaveIntervalDialog(shell, autoSaveJob);
dialogMap.put(window, dialog);
dialog.setBlockOnOpen(false);
dialog.setCloseCallback(new ICloseCallback() {
@Override
public void dialogClosed(Object returnValue) {
if (returnValue instanceof Integer) {
int returnCode = (Integer) returnValue;
if (returnCode == Window.OK) {
DataManager dm = DataManagerUIFactory
.findInstance(window);
// update
if (dialog.isAutoSaveEnabled()) {
int interval = dialog.getCurrentInterval();
AutoSaveJob.setInterval(interval);
if (dm != null) {
dm.enableAutoSave();
}
DataManagerUIFactory.findInstance(window)
.enableAutoSave();
} else {
AutoSaveJob.setInterval(0);
if (dm != null) {
dm.disableAutoSave();
}
}
}
}
dialogMap.remove(window);
}
});
dialog.open();

View file

@ -96,6 +96,7 @@ import com.raytheon.viz.gfe.textformatter.TextProductManager;
* initialized by constructor.
* 04/24/2013 1936 dgilling Move initialization of TextProductMgr
* to GFE startup.
* 08/27/2013 2302 randerso Code cleanup for AutoSaveJob
*
* </pre>
*
@ -271,18 +272,10 @@ public class DataManager {
}
/**
* Start auto save jobs
* @return the autoSaveJob
*/
public void enableAutoSave() {
autoSaveJob.cancel();
autoSaveJob.reSchedule();
}
/**
* Stop auto save jobs
*/
public void disableAutoSave() {
autoSaveJob.cancel();
public AutoSaveJob getAutoSaveJob() {
return autoSaveJob;
}
/**

View file

@ -84,6 +84,7 @@ import com.raytheon.viz.gfe.GFEServerException;
import com.raytheon.viz.gfe.core.DataManager;
import com.raytheon.viz.gfe.core.griddata.AbstractGridData;
import com.raytheon.viz.gfe.core.griddata.IGridData;
import com.raytheon.viz.gfe.core.parm.ParmSaveJob.ParmSaveStatus;
import com.raytheon.viz.gfe.core.parm.ParmState.InterpMode;
import com.raytheon.viz.gfe.core.wxvalue.DiscreteWxValue;
import com.raytheon.viz.gfe.core.wxvalue.ScalarWxValue;
@ -182,6 +183,8 @@ import com.vividsolutions.jts.geom.Coordinate;
* Mar 13, 2013 1792 bsteffen Improve performance of gfe parm average
* ant time weighted average.
* Apr 02, 2013 #1774 randerso Fixed a possible deadlock issue.
* Aug 27, 2013 #2302 randerso Fix simultaneous save issue
*
* </pre>
*
* @author chammack
@ -229,6 +232,8 @@ public abstract class Parm implements Comparable<Parm> {
protected boolean disposed;
private ParmSaveJob saveJob;
/**
* The create from scratch mode
*/
@ -360,6 +365,7 @@ public abstract class Parm implements Comparable<Parm> {
}
this.disposed = false;
this.saveJob = new ParmSaveJob(this);
}
public void dispose() {
@ -708,7 +714,7 @@ public abstract class Parm implements Comparable<Parm> {
* Save a parameter
*
* @param all
* @return
* @return true if save successful
*/
public boolean saveParameter(boolean all) {
if (inParmEdit) {
@ -729,14 +735,16 @@ public abstract class Parm implements Comparable<Parm> {
/**
* Save a parameter
*
* @param tr
* @return
* @param times
* TimeRanges to be saved
* @return true if save successful
*/
public boolean saveParameter(List<TimeRange> tr) {
public boolean saveParameter(List<TimeRange> times) {
if (inParmEdit) {
return false;
} else {
return saveParameterSubClass(tr);
ParmSaveStatus status = this.saveJob.requestSave(times);
return status.isSuccessful();
}
}
@ -744,7 +752,7 @@ public abstract class Parm implements Comparable<Parm> {
* Save a parameter
*
* @param tr
* @return
* @return true if save successful
*/
public boolean saveParameter(TimeRange tr) {
ArrayList<TimeRange> trs = new ArrayList<TimeRange>();
@ -756,7 +764,8 @@ public abstract class Parm implements Comparable<Parm> {
* Subclass specific save implementation
*
* @param tr
* @return
* TimeRanges to be saved
* @return true if save successful
*/
protected abstract boolean saveParameterSubClass(List<TimeRange> tr);

View file

@ -0,0 +1,135 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.gfe.core.parm;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import com.raytheon.uf.common.time.TimeRange;
/**
* Job to queue up and work of parm save requests
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Aug 27, 2013 #2302 randerso Initial creation
*
* </pre>
*
* @author randerso
* @version 1.0
*/
public class ParmSaveJob extends Job {
public static class ParmSaveStatus {
boolean successful = false;
Semaphore complete = new Semaphore(0);
/**
* @return the successful
*/
public boolean isSuccessful() {
try {
complete.acquire();
return successful;
} catch (InterruptedException e) {
return false;
}
}
/**
* @param successful
* the successful to set
*/
private void setSuccessful(boolean successful) {
this.successful = successful;
complete.release();
}
}
private class ParmSaveRequest {
private List<TimeRange> times;
private ParmSaveStatus status;
public ParmSaveRequest(List<TimeRange> times) {
this.times = times;
this.status = new ParmSaveStatus();
}
}
private Parm parm;
private ConcurrentLinkedQueue<ParmSaveRequest> saveQueue;
/**
* @param parm
*/
public ParmSaveJob(Parm parm) {
super("ParmSaveJob");
this.setSystem(true);
this.parm = parm;
this.saveQueue = new ConcurrentLinkedQueue<ParmSaveJob.ParmSaveRequest>();
}
/**
* Request save.
*
* Caller should call isSuccessful on the returned ParmSaveStatus object to
* await completion and to determine success
*
* @param times
* @return ParmSaveStatus object
*/
public ParmSaveStatus requestSave(List<TimeRange> times) {
ParmSaveRequest request = new ParmSaveRequest(times);
this.saveQueue.add(request);
this.schedule();
return request.status;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.
* IProgressMonitor)
*/
@Override
protected IStatus run(IProgressMonitor monitor) {
ParmSaveRequest req = null;
while ((req = this.saveQueue.poll()) != null) {
boolean successful = parm.saveParameterSubClass(req.times);
req.status.setSuccessful(successful);
}
return Status.OK_STATUS;
}
}

View file

@ -29,20 +29,21 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Shell;
import com.raytheon.viz.gfe.jobs.AutoSaveJob;
import com.raytheon.viz.ui.dialogs.CaveJFACEDialog;
import com.raytheon.viz.ui.widgets.SpinScale;
/**
* The auto save iterval dialog.
* The auto save interval dialog.
*
* <pre>
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jan 30, 2008 Eric Babin Initial Creation
* Jan 30, 2008 Eric Babin Initial Creation
* Aug 27, 2013 #2302 randerso Code cleanup
*
* </pre>
*
@ -52,98 +53,26 @@ import com.raytheon.viz.ui.dialogs.CaveJFACEDialog;
public class AutoSaveIntervalDialog extends CaveJFACEDialog {
private Composite top = null;
private Label intervalMinutes;
private Scale intervalScale;
private int currentInterval = 1;
private boolean autoSaveEnabled = false;
private AutoSaveJob autoSaveJob;
private Button offButton;
private Button onButton;
public AutoSaveIntervalDialog(Shell parent, int currentInterval,
boolean isEnabled) {
private Label intervalLabel;
private SpinScale intervalScale;
/**
* Constructor
*
* @param parent
* @param currentInterval
* @param isEnabled
*/
public AutoSaveIntervalDialog(Shell parent, AutoSaveJob autoSaveJob) {
super(parent);
this.setShellStyle(SWT.TITLE | SWT.MODELESS | SWT.CLOSE);
this.currentInterval = currentInterval;
this.autoSaveEnabled = isEnabled;
}
@Override
protected Control createDialogArea(Composite parent) {
top = (Composite) super.createDialogArea(parent);
top.setLayout(new GridLayout(2, false));
loadConfigData();
initializeComponents();
return top;
}
private void initializeComponents() {
Group g = new Group(top, SWT.NONE);
g.setLayout(new GridLayout(2, true));
offButton = new Button(g, SWT.RADIO);
offButton.setText("Off");
offButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
autoSaveEnabled = false;
}
});
offButton.setSelection(!autoSaveEnabled);
onButton = new Button(g, SWT.RADIO);
onButton.setText("On");
onButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
autoSaveEnabled = true;
}
});
onButton.setSelection(autoSaveEnabled);
Composite c = new Composite(top, SWT.NONE);
GridLayout layout = new GridLayout(2, false);
layout.marginBottom = 10;
c.setLayout(layout);
c.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL));
Label label = new Label(c, SWT.NONE);
label.setText("Save Interval in Minutes");
GridData data = new GridData();
data.horizontalSpan = 2;
label.setLayoutData(data);
intervalScale = new Scale(c, SWT.HORIZONTAL);
intervalScale.setLayoutData(new GridData(120, SWT.DEFAULT));
intervalScale.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
autoSaveEnabled = true;
currentInterval = intervalScale.getSelection();
String s = Integer.toString(currentInterval);
intervalMinutes.setText(s);
intervalMinutes.setToolTipText(s);
onButton.setSelection(autoSaveEnabled);
offButton.setSelection(!autoSaveEnabled);
}
});
intervalScale.setMinimum(AutoSaveJob.MIN_INTERVAL);
intervalScale.setMaximum(AutoSaveJob.MAX_INTERVAL);
intervalScale.setSelection(this.currentInterval);
intervalMinutes = new Label(c, SWT.NONE);
intervalMinutes.setLayoutData(new GridData(20, SWT.DEFAULT));
intervalMinutes.setText(String.valueOf(intervalScale.getSelection()));
this.autoSaveJob = autoSaveJob;
}
/*
@ -154,23 +83,92 @@ public class AutoSaveIntervalDialog extends CaveJFACEDialog {
* .Shell)
*/
@Override
protected void configureShell(Shell shell) {
super.configureShell(shell);
shell.setText("Auto Save Interval Dialog");
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText("Auto Save Interval Dialog");
}
/**
* Method for loading the various config data for the dialog.
@Override
protected Control createDialogArea(Composite parent) {
Composite comp = (Composite) super.createDialogArea(parent);
GridLayout layout = (GridLayout) comp.getLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
Group group = new Group(comp, SWT.NONE);
layout = new GridLayout(2, false);
group.setLayout(layout);
GridData layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false);
group.setLayoutData(layoutData);
group.setText("Auto Save");
int interval = autoSaveJob.getInterval();
offButton = new Button(group, SWT.RADIO);
layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false);
offButton.setLayoutData(layoutData);
offButton.setText("Off");
offButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
if (offButton.getSelection()) {
intervalLabel.setEnabled(false);
intervalScale.setEnabled(false);
}
}
});
offButton.setSelection(interval == 0);
onButton = new Button(group, SWT.RADIO);
layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false);
onButton.setLayoutData(layoutData);
onButton.setText("On");
onButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
if (onButton.getSelection()) {
intervalLabel.setEnabled(true);
intervalScale.setEnabled(true);
}
}
});
onButton.setSelection(interval > 0);
intervalLabel = new Label(group, SWT.CENTER);
intervalLabel.setText("Save Interval in Minutes");
layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false, 2, 1);
intervalLabel.setLayoutData(layoutData);
intervalScale = new SpinScale(group, SWT.HORIZONTAL);
layoutData = new GridData(SWT.FILL, SWT.DEFAULT, true, false, 2, 1);
layoutData.minimumWidth = 240;
intervalScale.setLayoutData(layoutData);
intervalScale.setMinimum(AutoSaveJob.MIN_INTERVAL);
intervalScale.setMaximum(AutoSaveJob.MAX_INTERVAL);
if (interval > 0) {
intervalScale.setSelection(interval);
} else {
intervalLabel.setEnabled(false);
intervalScale.setEnabled(false);
intervalScale.setSelection(AutoSaveJob.MAX_INTERVAL);
}
return comp;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#okPressed()
*/
private void loadConfigData() {
@Override
protected void okPressed() {
if (offButton.getSelection()) {
autoSaveJob.setInterval(0);
} else {
autoSaveJob.setInterval(intervalScale.getSelection());
}
super.okPressed();
}
public int getCurrentInterval() {
return currentInterval;
}
public boolean isAutoSaveEnabled() {
return autoSaveEnabled;
}
}

View file

@ -24,11 +24,14 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import com.raytheon.viz.gfe.Activator;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.time.util.TimeUtil;
import com.raytheon.viz.gfe.GFEPreference;
import com.raytheon.viz.gfe.PreferenceConstants;
import com.raytheon.viz.gfe.core.DataManager;
import com.raytheon.viz.gfe.core.parm.Parm;
import com.raytheon.viz.gfe.ui.HazardUIUtils;
/**
* Job implementing the auto save functionality. Uses user preferences to
@ -38,7 +41,8 @@ import com.raytheon.viz.gfe.core.parm.Parm;
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 25, 2008 Eric Babin Initial Creation
* Jun 25, 2008 Eric Babin Initial Creation
* Aug 27, 2013 #2302 randerso Fixed to behave more like A1, code cleanup
*
* </pre>
*
@ -47,38 +51,34 @@ import com.raytheon.viz.gfe.core.parm.Parm;
*/
public class AutoSaveJob extends Job {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(AutoSaveJob.class);
private static final int MILLISECONDS_PER_MINUTE = 60 * 1000;
/**
* Max auto save interval in minutes
*/
public static final int MAX_INTERVAL = 60;
/**
* Min auto save interval in minutes
*/
public static final int MIN_INTERVAL = 1;
/**
* auto save interval in minutes, 0 = disabled
* auto save interval in milliseconds, 0 = disabled
*/
private static int interval;
static {
int i = 0;
if (GFEPreference.storeAvailable()) {
i = GFEPreference
.getIntPreference(PreferenceConstants.GFE_AUTO_SAVE_INTERVAL);
}
interval = i;
}
private long interval;
/**
* Set the auto save interval
*
* @param i
* @param interval
* desired interval in minutes
*/
public static void setInterval(int i) {
interval = i;
GFEPreference.setPreference(PreferenceConstants.GFE_AUTO_SAVE_INTERVAL,
Integer.toString(i));
public void setInterval(int interval) {
this.interval = interval * TimeUtil.MILLIS_PER_MINUTE;
this.cancel();
this.reSchedule(this.interval);
}
/**
@ -86,8 +86,8 @@ public class AutoSaveJob extends Job {
*
* @return interval in minutes
*/
public static int getInterval() {
return interval;
public int getInterval() {
return (int) (interval / TimeUtil.MILLIS_PER_MINUTE);
}
private DataManager dataManager;
@ -104,17 +104,28 @@ public class AutoSaveJob extends Job {
this.dataManager = dataManager;
this.disposed = false;
this.setSystem(true);
int pref = GFEPreference
.getIntPreference(PreferenceConstants.GFE_AUTO_SAVE_INTERVAL);
if (pref > MAX_INTERVAL) {
pref = MAX_INTERVAL;
} else if (pref < 0) {
pref = 0;
}
reSchedule();
this.interval = pref * TimeUtil.MILLIS_PER_MINUTE;
reSchedule(this.interval);
}
/**
* Schedule this job to run after the desired interval
* Schedule this job to run after the desired
*
* @param delay
* interval in milliseconds
*/
public void reSchedule() {
public void reSchedule(long delay) {
if (!disposed) {
if (interval > 0) {
this.schedule(interval * MILLISECONDS_PER_MINUTE);
if (delay > 0) {
this.schedule(delay);
}
} else {
dataManager = null;
@ -138,53 +149,51 @@ public class AutoSaveJob extends Job {
*/
@Override
protected IStatus run(IProgressMonitor monitor) {
IStatus status = Status.OK_STATUS;
try {
Parm[] parms = this.dataManager.getParmManager().getModifiedParms();
if (parms != null && parms.length != 0) {
String autoSaveResult = "GFE Auto Save called. Save of Parms[";
for (int i = 0; i < parms.length; i++) {
String name = parms[i].getParmID().getParmName();
if (name.indexOf("Hazards") == -1) {
autoSaveResult += saveParm(parms[i]);
} else {
if (!tempHazardsExist()) {
saveParm(parms[i]);
autoSaveResult += saveParm(parms[i]);
}
}
}
autoSaveResult += "]";
long delay = this.interval;
if (delay > 0) {
String msg = "GFE Auto Save called. ";
boolean success = false;
try {
Parm modifiedParm = getModifiedParm();
if (modifiedParm != null) {
msg += "Save of Parm "
+ modifiedParm.getParmID().getParmName() + " = ";
Activator.getDefault().getLog().log(
new Status(IStatus.INFO, Activator.PLUGIN_ID,
autoSaveResult));
// save the data
success = modifiedParm.saveParameter(false);
msg += (success ? "success" : "failure");
delay = 1500;
} else {
msg += "Nothing to save.";
}
statusHandler.info(msg);
} catch (Exception e) {
msg += "failure";
statusHandler.error(msg, e);
}
} catch (Exception e) {
status = new Status(IStatus.ERROR, Activator.PLUGIN_ID,
"GFE auto save failed.", e);
reSchedule(delay);
}
reSchedule();
return status;
return Status.OK_STATUS;
}
/**
* If tempHazards exist, then autosave kicked off in the middle of merge
* hazard. (which creates a "temp" hazard with "haz" in the name.
* Find the first available modified parm. Skip Hazards if temp hazards
* exist
*
* So we will ignore the "Hazard" WE parm save...
*
* @return if temp haz exists.
* @return the first available modified parm or null if none
*/
private boolean tempHazardsExist() {
Parm[] parms = this.dataManager.getParmManager().getDisplayedParms();
for (int i = 0; i < parms.length; i++) {
if (parms[i].getParmID().getParmName().indexOf("haz") != -1) {
return true;
private Parm getModifiedParm() {
boolean tempHazDisplayed = HazardUIUtils
.tempHazardsExist(this.dataManager);
for (Parm p : this.dataManager.getParmManager().getModifiedParms()) {
if (p.getParmID().getParmName().contains("Hazards")
&& tempHazDisplayed) {
continue;
} else {
return p;
}
}
return false;
return null;
}
/**