diff --git a/cave/com.raytheon.viz.pointdata/META-INF/MANIFEST.MF b/cave/com.raytheon.viz.pointdata/META-INF/MANIFEST.MF index d3282cf0cc..92125c0101 100644 --- a/cave/com.raytheon.viz.pointdata/META-INF/MANIFEST.MF +++ b/cave/com.raytheon.viz.pointdata/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Pointdata Plug-in Bundle-SymbolicName: com.raytheon.viz.pointdata;singleton:=true -Bundle-Version: 1.12.1174.qualifier +Bundle-Version: 1.14.0.qualifier Bundle-Activator: com.raytheon.viz.pointdata.Activator Bundle-Vendor: Raytheon Eclipse-RegisterBuddy: com.raytheon.viz.core, com.raytheon.uf.viz.core @@ -20,7 +20,8 @@ Require-Bundle: org.apache.batik, com.raytheon.uf.common.pointdata;bundle-version="1.12.1174", com.raytheon.uf.common.dataplugin.level;bundle-version="1.12.1174", com.raytheon.uf.common.colormap;bundle-version="1.12.1174", - com.raytheon.uf.common.wxmath + com.raytheon.uf.common.wxmath, + com.raytheon.uf.common.python.concurrent Bundle-ActivationPolicy: lazy Export-Package: com.raytheon.viz.pointdata, com.raytheon.viz.pointdata.drawables, diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotDataThreadPool.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotDataThreadPool.java deleted file mode 100644 index 42404ff691..0000000000 --- a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotDataThreadPool.java +++ /dev/null @@ -1,196 +0,0 @@ -/** - * 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.pointdata; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.swt.graphics.RGB; - -import com.raytheon.uf.common.dataquery.requests.RequestConstraint; -import com.raytheon.uf.viz.core.IGraphicsTarget; -import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle; -import com.raytheon.uf.viz.core.exception.VizException; -import com.raytheon.uf.viz.core.map.MapDescriptor; -import com.raytheon.viz.pointdata.thread.GetDataTask; - -/** - * Thread pool for requesting data for plots. Each data request job is tied to a - * plot generator job. - * - *
- * 
- * SOFTWARE HISTORY
- * 
- * Date         Ticket#    Engineer    Description
- * ------------ ---------- ----------- --------------------------
- * Jun 30, 2011            njensen     Initial creation
- * 
- * 
- * - * @author njensen - * @version 1.0 - */ - -public class PlotDataThreadPool { - - private static final int THREADS = 2; - - protected ConcurrentLinkedQueue stationQueue = new ConcurrentLinkedQueue(); - - protected List jobList = new ArrayList(); - - public PlotDataThreadPool(IGraphicsTarget aTarget, - MapDescriptor mapDescriptor, String plotModelFile, String levelKey, - String plugin, HashMap constraintMap, - IPlotModelGeneratorCaller caller) throws VizException { - for (int i = 0; i < THREADS; i++) { - jobList.add(new PlotModelDataRequestJob(aTarget, mapDescriptor, - plotModelFile, levelKey, plugin, constraintMap, caller, - this)); - } - } - - /** - * Adds a batch of stations to the queue - * - * @param station - * - */ - public void queueStation(GetDataTask task) { - List station = task.getStations(); - - Iterator itr = station.iterator(); - while (itr.hasNext()) { - PlotInfo[] infos = itr.next(); - boolean allQueued = true; - for (PlotInfo info : infos) { - switch (task.getRequestType()) { - case PLOT_ONLY: - if (!info.plotQueued) { - allQueued = false; - info.plotQueued = true; - } - - break; - case SAMPLE_ONLY: - if (!info.sampleQueued) { - allQueued = false; - info.sampleQueued = true; - } - break; - case PLOT_AND_SAMPLE: - if (!info.sampleQueued) { - allQueued = false; - info.sampleQueued = true; - } - if (!info.plotQueued) { - allQueued = false; - info.plotQueued = true; - } - } - } - if (allQueued) { - itr.remove(); - } - } - - if (station.size() > 0) { - task.setStations(station); - stationQueue.add(task); - for (PlotModelDataRequestJob job : jobList) { - if (job.getState() != Job.RUNNING) { - job.schedule(); - } - } - } - - } - - public void setPlotModelColor(RGB color) { - for (PlotModelDataRequestJob job : jobList) { - job.setPlotModelColor(color); - } - } - - public void setPlotModelLineStyle(LineStyle lineStyle) { - for (PlotModelDataRequestJob job : jobList) { - job.setPlotModelLineStyle(lineStyle); - } - } - - public void setPlotModelLineWidth(int outlineWidth) { - for (PlotModelDataRequestJob job : jobList) { - job.setPlotModelLineWidth(outlineWidth); - } - } - - public void setPlotMissingData(boolean plotMissingData) { - for (PlotModelDataRequestJob job : jobList) { - job.setPlotMissingData(plotMissingData); - } - } - - public void setLowerLimit(double lowerLimit) { - for (PlotModelDataRequestJob job : jobList) { - job.setLowerLimit(lowerLimit); - } - } - - public void setUpperLimit(double upperLimit) { - for (PlotModelDataRequestJob job : jobList) { - job.setUpperLimit(upperLimit); - } - } - - public double getPlotModelWidth() { - return jobList.get(0).getPlotModelWidth(); - } - - public void setPlotModelSize(long round) { - for (PlotModelDataRequestJob job : jobList) { - job.setPlotModelSize(round); - } - } - - public boolean isDone() { - for (PlotModelDataRequestJob job : jobList) { - if (!job.isDone()) { - return false; - } - } - return true; - } - - public void shutdown() { - List jobListCopy = new ArrayList( - jobList); - jobList.clear(); - stationQueue.clear(); - for (PlotModelDataRequestJob job : jobListCopy) { - job.shutdown(); - } - } - -} diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotModelFactory2.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotModelFactory2.java index 101c86e676..b247391ff5 100644 --- a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotModelFactory2.java +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotModelFactory2.java @@ -29,6 +29,7 @@ import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.Formatter; import java.util.HashMap; @@ -40,8 +41,6 @@ import javax.measure.converter.UnitConverter; import javax.measure.unit.Unit; import javax.measure.unit.UnitFormat; -import jep.JepException; - import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.UserAgentAdapter; @@ -57,10 +56,8 @@ import org.w3c.dom.NodeList; import org.w3c.dom.Text; import com.raytheon.uf.common.localization.IPathManager; -import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType; -import com.raytheon.uf.common.localization.LocalizationFile; import com.raytheon.uf.common.localization.PathManagerFactory; -import com.raytheon.uf.common.python.PythonScript; +import com.raytheon.uf.common.python.concurrent.PythonJobCoordinator; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; @@ -69,12 +66,16 @@ import com.raytheon.uf.viz.core.exception.VizException; import com.raytheon.uf.viz.core.map.IMapDescriptor; import com.raytheon.viz.pointdata.lookup.IAbstractLookupTable; import com.raytheon.viz.pointdata.lookup.LookupUtils; +import com.raytheon.viz.pointdata.python.CheckPlotValidityExecutor; +import com.raytheon.viz.pointdata.python.PlotPythonScript; +import com.raytheon.viz.pointdata.python.PlotPythonScriptFactory; +import com.raytheon.viz.pointdata.python.SampleTextExecutor; import com.raytheon.viz.pointdata.rsc.PlotResource2; import com.raytheon.viz.pointdata.rsc.PlotResourceData; /** - * A singleton that will create a plot model texture based on a passed in - * MetarRecord object. + * A factory for generating plot images and sample messages by parsing the + * associated plotModel SVG file. * *
  * 
@@ -89,6 +90,7 @@ import com.raytheon.viz.pointdata.rsc.PlotResourceData;
  * Sep 05, 2013  2316     bsteffen    Unify pirep and ncpirep.
  * Sep 05, 2013  2307     dgilling    Use better PythonScript constructor.
  * Nov 20, 2013  2033     njensen     Fix detecting plotModels dirs from multiple plugins
+ * Mar 21, 2014  2868     njensen     Refactored python usage to PythonJobCoordinator
  * 
  * 
* @@ -99,7 +101,7 @@ public class PlotModelFactory2 { private static final transient IUFStatusHandler statusHandler = UFStatus .getHandler(PlotModelFactory2.class); - private static final String PLOT_MODEL_DIR = "plotModels"; + public static final String PLOT_MODEL_DIR = "plotModels"; private static final String DM_ATTRIBUTE = "plotMode"; @@ -121,8 +123,6 @@ public class PlotModelFactory2 { private static final String REQUIRED = "required"; - private static String cachedIncludePath; - private final SimpleDateFormat SAMPLE_DATE = new SimpleDateFormat("HHmm"); // Need to include attribute and code to allow for String2String lookups and @@ -137,8 +137,6 @@ public class PlotModelFactory2 { private Document document; - private GraphicsNode theGraphicsNode; - private final GVTBuilder builder; private final BridgeContext bridgeContext; @@ -171,9 +169,9 @@ public class PlotModelFactory2 { private Map imageCache = null; - private ScriptInfo scriptInfo; + protected final String plotModelFile; - private ScriptInfo sampleScriptInfo; + protected PythonJobCoordinator python; public static enum DisplayMode { TEXT, BARB, TABLE, AVAIL, RANGE, NULL, SAMPLE, ARROW @@ -211,6 +209,10 @@ public class PlotModelFactory2 { public Node getPlotNode() { return plotNode; } + + public String getParameter() { + return parameter; + } } public class PlotWindElement { @@ -238,6 +240,7 @@ public class PlotModelFactory2 { byte[] blue = { 0, full }; byte[] green = { 0, zero }; regenerateStyle(); + this.plotModelFile = plotModelFile; this.plotFields = new ArrayList(); this.sampleFields = new ArrayList(); @@ -452,12 +455,16 @@ public class PlotModelFactory2 { imageCache = new HashMap(); } NodeList scriptNodes = document.getElementsByTagName("script"); + // Only one script node supported - if (scriptNodes.getLength() > 0) { + int nScriptNodes = scriptNodes.getLength(); + if (nScriptNodes > 1) { + throw new UnsupportedOperationException( + "Only one script node allowed in plotModel SVG file. Please check and fix " + + plotModelFile); + } else if (nScriptNodes == 1) { Element scriptNode = (Element) scriptNodes.item(0); - scriptInfo = new ScriptInfo(); - scriptInfo.plotDelegateName = scriptNode - .getAttribute("plotDelegate"); + String plotDelegateName = scriptNode.getAttribute("plotDelegate"); NodeList childNodes = scriptNode.getChildNodes(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < childNodes.getLength(); i++) { @@ -466,14 +473,13 @@ public class PlotModelFactory2 { sb.append(((Text) child).getData()); } } - if (sb.length() > 0) { - scriptInfo.scriptText = sb.toString(); + String scriptText = sb.toString().trim(); + if (scriptText.length() > 0) { + PlotPythonScriptFactory pythonFactory = new PlotPythonScriptFactory( + plotModelFile, scriptText, plotDelegateName); + python = PythonJobCoordinator.newInstance(pythonFactory); } - sampleScriptInfo = new ScriptInfo(); - sampleScriptInfo.plotDelegateName = scriptInfo.plotDelegateName; - sampleScriptInfo.scriptText = scriptInfo.scriptText; - // remove the scriptNode in memory so time isn't wasted // later attempting to render it scriptNode.getParentNode().removeChild(scriptNode); @@ -491,8 +497,8 @@ public class PlotModelFactory2 { byte fullr = (byte) color.red; byte fullg = (byte) color.green; byte fullb = (byte) color.blue; - String style = "stroke: rgb(" + color.red + "," + color.green + "," - + color.blue + ");"; + // String style = "stroke: rgb(" + color.red + "," + color.green + "," + // + color.blue + ");"; // this.svgRoot.setAttribute("style", style); // System.out.println(style); byte[] red = { 0, fullr }; @@ -567,8 +573,7 @@ public class PlotModelFactory2 { } /** - * Takes the station name and its MetarRecord object and produces a buffered - * image. + * Takes the station data object and produces a buffered image. * * @param station * The station name @@ -591,27 +596,28 @@ public class PlotModelFactory2 { this.gc.setDestinationGeographicPoint(newWorldLocation[0], newWorldLocation[1]); } - StringBuffer imageId = new StringBuffer(); - PlotPythonScript script = null; + try { boolean discard = false; - - if (scriptInfo != null) { - script = scriptInfo.getScript(); - + if (python != null) { + Boolean result = false; + CheckPlotValidityExecutor task = new CheckPlotValidityExecutor( + stationData); try { - Object result = script.executePlotDelegateMethod("isValid", - "rec", stationData); - if (result instanceof Boolean - && !((Boolean) result).booleanValue()) { + result = python.submitSyncJob(task); + } catch (Exception e) { + statusHandler.handle(Priority.PROBLEM, + "Error checking if plot is valid for plot model " + + getPlotModelFilename(), e); + } finally { + if (result.booleanValue() == false) { return null; } - } catch (JepException e) { - statusHandler.handle(Priority.PROBLEM, - e.getLocalizedMessage(), e); } } + StringBuilder imageId = new StringBuilder(); + for (PlotModelElement element : this.plotFields) { boolean valid = true; boolean required = element.required; @@ -680,13 +686,13 @@ public class PlotModelFactory2 { this.plotModelWidth, this.plotModelHeight, BufferedImage.TYPE_BYTE_INDEXED, tm); - long t0 = System.currentTimeMillis(); - this.theGraphicsNode = builder.build(this.bridgeContext, + // long t0 = System.currentTimeMillis(); + GraphicsNode graphicsNode = builder.build(this.bridgeContext, this.document); Graphics2D g2d = null; try { g2d = bufferedImage.createGraphics(); - this.theGraphicsNode.primitivePaint(g2d); + graphicsNode.primitivePaint(g2d); } finally { if (g2d != null) { g2d.dispose(); @@ -702,9 +708,6 @@ public class PlotModelFactory2 { } catch (Exception e) { statusHandler.handle(Priority.PROBLEM, "Error:" + e.getLocalizedMessage(), e); - } catch (JepException e) { - statusHandler.handle(Priority.PROBLEM, - "Error:" + e.getLocalizedMessage(), e); } return null; @@ -712,20 +715,20 @@ public class PlotModelFactory2 { public synchronized String getStationMessage(PlotData stationData, String dataURI) { - PlotPythonScript script = null; StringBuilder sampleMessage = new StringBuilder(); try { - if (sampleScriptInfo != null) { - script = sampleScriptInfo.getScript(); - - Object result = script.executePlotDelegateMethod("isValid", - "rec", stationData); - if (result instanceof Boolean - && ((Boolean) result).booleanValue()) { - result = script.executePlotDelegateMethod("getSampleText", - "rec", stationData); - if (result instanceof String) { - sampleMessage.append((String) result); + if (python != null) { + String result = null; + SampleTextExecutor task = new SampleTextExecutor(stationData); + try { + result = python.submitSyncJob(task); + } catch (Exception e) { + statusHandler.handle(Priority.PROBLEM, + "Error getting sample text for plot model " + + getPlotModelFilename(), e); + } finally { + if (result != null) { + sampleMessage.append(result); } } } else { @@ -736,11 +739,8 @@ public class PlotModelFactory2 { } } catch (Exception e) { - // TODO - statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e); - } catch (JepException e) { - // TODO Auto-generated catch block. Please revise as appropriate. - statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e); + statusHandler.handle(Priority.PROBLEM, + "Error generating sample text with " + plotModelFile, e); } String message = sampleMessage.toString(); @@ -752,7 +752,7 @@ public class PlotModelFactory2 { } private void addToImageId(PlotData stationData, String parameters, - StringBuffer imageId) { + StringBuilder imageId) { for (String parameter : parameters.split(",")) { switch (stationData.getType(parameter)) { case STRING: @@ -809,6 +809,7 @@ public class PlotModelFactory2 { Formatter testing = new Formatter(sb); testing.format(element.format, displayValue); sValue = sb.toString(); + testing.close(); } else { sValue = Double.toString(displayValue); } @@ -1073,6 +1074,7 @@ public class PlotModelFactory2 { Formatter testing = new Formatter(sb); testing.format(element.format, displayValue); sValue = sb.toString(); + testing.close(); } } else { sValue = Double.toString(displayValue); @@ -1138,6 +1140,7 @@ public class PlotModelFactory2 { Formatter testing = new Formatter(sb); testing.format(element.format, displayValue); sValue = sb.toString(); + testing.close(); } else { sValue = Double.toString(displayValue); } @@ -1197,8 +1200,8 @@ public class PlotModelFactory2 { return major * 5; } - public synchronized List getPlotFields() { - return this.plotFields; + public List getPlotFields() { + return Collections.unmodifiableList(this.plotFields); } /** @@ -1244,140 +1247,6 @@ public class PlotModelFactory2 { this.plotMissingData = b; } - public void disposeScript() { - if (scriptInfo != null) { - try { - scriptInfo.disposeScript(); - } catch (JepException e) { - statusHandler.handle(Priority.ERROR, - "Error disposing plot model script", e); - } - } - } - - public void disposeSampleScript() { - if (sampleScriptInfo != null) { - try { - sampleScriptInfo.disposeScript(); - } catch (JepException e) { - statusHandler.handle(Priority.ERROR, - "Error disposing plot model sample script", e); - } - } - } - - private class ScriptInfo { - public String plotDelegateName; - - public String scriptText; - - private PlotPythonScript script; - - private Thread scriptThread; - - public PlotPythonScript getScript() throws JepException { - if (script != null) { - if (Thread.currentThread() == scriptThread) { - return script; - } else { - statusHandler.handle(Priority.ERROR, - "Plot model scripting was not properly disposed."); - script = null; - scriptThread = null; - } - } - - script = createScript(); - scriptThread = Thread.currentThread(); - return script; - } - - public void disposeScript() throws JepException { - if (script != null) { - try { - if (Thread.currentThread() == scriptThread) { - script.dispose(); - } - } finally { - script = null; - scriptThread = null; - } - } - } - - private PlotPythonScript createScript() throws JepException { - synchronized (PlotModelFactory2.class) { - if (cachedIncludePath == null) { - IPathManager pm = PathManagerFactory.getPathManager(); - LocalizationFile[] files = pm - .listFiles( - pm.getLocalSearchHierarchy(LocalizationType.CAVE_STATIC), - PLOT_MODEL_DIR, null, false, false); - StringBuilder includePath = new StringBuilder(); - for (LocalizationFile lf : files) { - if (lf.exists() && lf.isDirectory()) { - if (includePath.length() > 0) { - includePath.append(File.pathSeparator); - } - includePath.append(lf.getFile().getAbsolutePath()); - } - } - cachedIncludePath = includePath.toString(); - } - } - - File baseFile = PathManagerFactory.getPathManager().getStaticFile( - PLOT_MODEL_DIR + IPathManager.SEPARATOR - + "PlotModelInterface.py"); - PlotPythonScript localScript = new PlotPythonScript( - baseFile.getAbsolutePath(), cachedIncludePath, - plotDelegateName); - - if (scriptText != null) { - localScript.evaluate(scriptText); - } - localScript.executePlotDelegateMethod("init", "plotModelFactory", - PlotModelFactory2.this); - return localScript; - } - } - - private static class PlotPythonScript extends PythonScript { - - private static String CHEAT_RUN = "_cheat_run"; - - private String plotDelegateName; - - public PlotPythonScript(String filePath, String anIncludePath, - String plotDelegateName) throws JepException { - super(filePath, anIncludePath, PlotPythonScript.class - .getClassLoader()); - jep.eval("def " - + CHEAT_RUN - + "(text):\n return eval(compile(text,'string','exec'),globals(),globals())"); - this.plotDelegateName = plotDelegateName; - } - - public Object evaluate(String script) throws JepException { - Object result = jep.invoke(CHEAT_RUN, script); - return result; - } - - public Object executePlotDelegateMethod(String methodName, - String argName, Object argValue) throws JepException { - if (plotDelegateName != null) { - HashMap map = null; - if (argName != null) { - map = new HashMap(); - map.put(argName, argValue); - } - return execute(methodName, plotDelegateName, map); - } else { - return null; - } - } - } - private File getTableFile(String fileName) { File rval = PathManagerFactory.getPathManager().getStaticFile( PLOT_MODEL_DIR + IPathManager.SEPARATOR + fileName); @@ -1385,10 +1254,24 @@ public class PlotModelFactory2 { } public List getSampleFields() { - return this.sampleFields; + return Collections.unmodifiableList(this.sampleFields); } public boolean isCachingImages() { return imageCache != null; } + + public String getPlotModelFilename() { + return this.plotModelFile; + } + + /** + * Disposes of the plot model + */ + public void dispose() { + if (python != null) { + python.shutdown(); + } + } + } diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/AbstractPlotDelegateExecutor.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/AbstractPlotDelegateExecutor.java new file mode 100644 index 0000000000..3fb7b6e6b1 --- /dev/null +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/AbstractPlotDelegateExecutor.java @@ -0,0 +1,50 @@ +/** + * 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.pointdata.python; + +import com.raytheon.viz.pointdata.PlotData; + +/** + * Abstract class for python plot delegates where the subclasses will implement + * IPythonExecutor + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 18, 2014 2868       njensen     Initial creation
+ * 
+ * 
+ * + * @author njensen + * @version 1.0 + */ + +public abstract class AbstractPlotDelegateExecutor { + + protected PlotData plotData; + + public AbstractPlotDelegateExecutor(PlotData data) { + this.plotData = data; + } + +} diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/CheckPlotValidityExecutor.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/CheckPlotValidityExecutor.java new file mode 100644 index 0000000000..69f1ac1c7d --- /dev/null +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/CheckPlotValidityExecutor.java @@ -0,0 +1,62 @@ +/** + * 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.pointdata.python; + +import java.util.HashMap; +import java.util.Map; + +import jep.JepException; + +import com.raytheon.uf.common.python.concurrent.IPythonExecutor; +import com.raytheon.viz.pointdata.PlotData; + +/** + * Task to check if the plot data is valid based on the python method isValid() + * extracted from the SVG file. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 18, 2014 2868       njensen     Initial creation
+ * 
+ * 
+ * + * @author njensen + * @version 1.0 + */ + +public class CheckPlotValidityExecutor extends AbstractPlotDelegateExecutor + implements IPythonExecutor { + + public CheckPlotValidityExecutor(PlotData data) { + super(data); + } + + @Override + public Boolean execute(PlotPythonScript script) throws JepException { + Map args = new HashMap(); + args.put("rec", plotData); + return (Boolean) script.execute("isValid", args); + } + +} diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/PlotPythonScript.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/PlotPythonScript.java new file mode 100644 index 0000000000..377006ba8e --- /dev/null +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/PlotPythonScript.java @@ -0,0 +1,90 @@ +/** + * 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.pointdata.python; + +import java.util.Map; + +import jep.JepException; + +import com.raytheon.uf.common.python.PythonScript; + +/** + * A python interpreter that is expecting to receive and retain a multi-line + * script of plot delegate python extracted from a plot model SVG file. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 14, 2014 2868       njensen     Initial creation
+ * 
+ * 
+ * + * @author njensen + * @version 1.0 + */ + +public class PlotPythonScript extends PythonScript { + + protected String plotDelegateName; + + /** + * Constructor + * + * @param filePath + * the path to the plot model interface python + * @param anIncludePath + * the include path for relevant python modules + * @param scriptText + * the text of the python code extracted from the plot model svg + * @param plotDelegateName + * the instance name of the plot delegate + * @param svgFilename + * the name of the SVG file the python code came from, this is + * used in exception stacktraces + * + * @throws JepException + */ + public PlotPythonScript(String filePath, String anIncludePath, + String scriptText, String plotDelegateName, String svgFilename) + throws JepException { + super(filePath, anIncludePath, PlotPythonScript.class.getClassLoader()); + /* + * jep.eval() won't evaluate more than a single line of python code, + * unless that first line is a python class or method definition, so we + * work around that by compiling it, then eval-ing it + * + * TODO contemplate building the workaround into PythonInterpreter, + * PythonScript, or PythonEval + */ + jep.set("scriptToRun", scriptText); + jep.eval("eval(compile(scriptToRun, '" + svgFilename + "', 'exec'))"); + this.plotDelegateName = plotDelegateName; + } + + @Override + public Object execute(String methodName, Map args) + throws JepException { + return execute(methodName, plotDelegateName, args); + } + +} diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/PlotPythonScriptFactory.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/PlotPythonScriptFactory.java new file mode 100644 index 0000000000..85bdc074bf --- /dev/null +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/PlotPythonScriptFactory.java @@ -0,0 +1,116 @@ +/** + * 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.pointdata.python; + +import java.io.File; + +import jep.JepException; + +import com.raytheon.uf.common.localization.IPathManager; +import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType; +import com.raytheon.uf.common.localization.LocalizationFile; +import com.raytheon.uf.common.localization.PathManagerFactory; +import com.raytheon.uf.common.python.concurrent.AbstractPythonScriptFactory; +import com.raytheon.viz.pointdata.PlotModelFactory2; + +/** + * Builds a plot delegate python script based on the script text extracted from + * a plot model SVG file. The name should be the name of the plot model SVG file + * to optimize reusing the interpreters across different resources that display + * the same plot model. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 14, 2014 2868       njensen     Initial creation
+ * 
+ * 
+ * + * @author njensen + * @version 1.0 + */ + +public class PlotPythonScriptFactory extends + AbstractPythonScriptFactory { + + private static String includePath; + + private static String baseFilePath; + + protected String scriptText; + + protected String plotDelegateName; + + /** + * Constructor + * + * @param plotSvgName + * the name of the svg file that the python code was extracted + * from + * @param scriptText + * the python code that was extracted from the svg file + * @param plotDelegateName + * the name of the instance of the plot delegate inside the + * python code + */ + public PlotPythonScriptFactory(String plotSvgName, String scriptText, + String plotDelegateName) { + super(plotSvgName, 1); + this.scriptText = scriptText; + this.plotDelegateName = plotDelegateName; + } + + @Override + public PlotPythonScript createPythonScript() throws JepException { + synchronized (PlotPythonScriptFactory.class) { + if (includePath == null) { + IPathManager pm = PathManagerFactory.getPathManager(); + LocalizationFile[] files = pm.listFiles(pm + .getLocalSearchHierarchy(LocalizationType.CAVE_STATIC), + PlotModelFactory2.PLOT_MODEL_DIR, null, false, false); + StringBuilder includeBuilder = new StringBuilder(); + for (LocalizationFile lf : files) { + if (lf.exists() && lf.isDirectory()) { + if (includeBuilder.length() > 0) { + includeBuilder.append(File.pathSeparator); + } + includeBuilder.append(lf.getFile().getAbsolutePath()); + } + } + includePath = includeBuilder.toString(); + } + + if (baseFilePath == null) { + File baseFile = PathManagerFactory.getPathManager() + .getStaticFile( + PlotModelFactory2.PLOT_MODEL_DIR + + IPathManager.SEPARATOR + + "PlotModelInterface.py"); + baseFilePath = baseFile.getAbsolutePath(); + } + } + + return new PlotPythonScript(baseFilePath, includePath, scriptText, + plotDelegateName, this.getName()); + } +} diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/SampleTextExecutor.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/SampleTextExecutor.java new file mode 100644 index 0000000000..27de2920ee --- /dev/null +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/python/SampleTextExecutor.java @@ -0,0 +1,69 @@ +/** + * 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.pointdata.python; + +import java.util.HashMap; +import java.util.Map; + +import jep.JepException; + +import com.raytheon.uf.common.python.concurrent.IPythonExecutor; +import com.raytheon.viz.pointdata.PlotData; + +/** + * Task to get the sample data text if the plot data is valid. Uses the python + * code of the plot delegate's isValid() and getSampleText() methods to get the + * sample text. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 18, 2014 2868       njensen     Initial creation
+ * 
+ * 
+ * + * @author njensen + * @version 1.0 + */ + +public class SampleTextExecutor extends AbstractPlotDelegateExecutor implements + IPythonExecutor { + + public SampleTextExecutor(PlotData data) { + super(data); + } + + @Override + public String execute(PlotPythonScript script) throws JepException { + String result = null; + Map args = new HashMap(); + args.put("rec", plotData); + Boolean isValid = (Boolean) script.execute("isValid", args); + if (isValid.booleanValue()) { + result = (String) script.execute("getSampleText", args); + } + + return result; + } + +} diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/rsc/PlotResource2.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/rsc/PlotResource2.java index 54f9ebc75a..6aa51f3e8a 100644 --- a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/rsc/PlotResource2.java +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/rsc/PlotResource2.java @@ -60,7 +60,6 @@ import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability; import com.raytheon.uf.viz.core.rsc.capabilities.OutlineCapability; import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller; import com.raytheon.viz.pointdata.PlotAlertParser; -import com.raytheon.viz.pointdata.PlotDataThreadPool; import com.raytheon.viz.pointdata.PlotInfo; import com.raytheon.viz.pointdata.drawables.IPointImageExtension; import com.raytheon.viz.pointdata.drawables.IPointImageExtension.PointImage; @@ -70,6 +69,7 @@ import com.raytheon.viz.pointdata.rsc.progdisc.DynamicProgDisclosure; import com.raytheon.viz.pointdata.rsc.progdisc.SpiProgDisclosure; import com.raytheon.viz.pointdata.thread.GetDataTask; import com.raytheon.viz.pointdata.thread.GetDataTask.Params; +import com.raytheon.viz.pointdata.thread.PlotThreadOverseer; import com.raytheon.viz.pointdata.units.PlotUnits; import com.vividsolutions.jts.geom.Coordinate; @@ -98,6 +98,7 @@ import com.vividsolutions.jts.geom.Coordinate; * Jun 25, 2013 1869 bsteffen Fix plot sampling. * Sep 04, 2013 16519 kshresth Fix Metar Display Problem * Dec 02, 2013 2473 njensen Prog Disclose paint frames at high priority + * Mar 21, 2014 2868 njensen Use PlotThreadOverseer for increased efficiency * * * @@ -116,7 +117,7 @@ public class PlotResource2 extends private AbstractProgDisclosure progressiveDisclosure; - private PlotDataThreadPool generator; + private PlotThreadOverseer generator; private double plotWidth; @@ -265,7 +266,7 @@ public class PlotResource2 extends Map request = resourceData.getMetadataMap(); RequestConstraint plugin = request.get("pluginName"); PlotUnits.register(); - generator = new PlotDataThreadPool(aTarget, descriptor, + generator = new PlotThreadOverseer(aTarget, descriptor, this.resourceData.getPlotModelFile(), this.resourceData.getLevelKey(), plugin.getConstraintValue(), this.resourceData.getMetadataMap(), this); @@ -279,7 +280,7 @@ public class PlotResource2 extends this.generator.setPlotMissingData(resourceData.isPlotMissingData()); this.generator.setLowerLimit(resourceData.getLowerLimit()); this.generator.setUpperLimit(resourceData.getUpperLimit()); - this.plotWidth = generator.getPlotModelWidth(); + this.plotWidth = generator.getOriginalPlotModelWidth(); this.plotWidth *= getCapability(MagnificationCapability.class) .getMagnification(); generator.setPlotModelSize(Math.round(plotWidth)); @@ -431,7 +432,7 @@ public class PlotResource2 extends list.add(samplePlot); Params params = Params.PLOT_AND_SAMPLE; GetDataTask task = new GetDataTask(list, params); - generator.queueStation(task); + generator.enqueueDataRetrieval(task); // End DR14996 } boolean dup = false; @@ -575,7 +576,7 @@ public class PlotResource2 extends params = Params.SAMPLE_ONLY; } GetDataTask task = new GetDataTask(list, params); - generator.queueStation(task); + generator.enqueueDataRetrieval(task); } } @@ -616,7 +617,7 @@ public class PlotResource2 extends } } else if (object instanceof MagnificationCapability) { if (generator != null) { - this.plotWidth = generator.getPlotModelWidth(); + this.plotWidth = generator.getOriginalPlotModelWidth(); this.plotWidth *= getCapability( MagnificationCapability.class).getMagnification(); generator.setPlotModelSize(Math.round(plotWidth)); @@ -758,7 +759,7 @@ public class PlotResource2 extends } if (toQueue.size() > 0) { GetDataTask task = new GetDataTask(toQueue, Params.PLOT_ONLY); - generator.queueStation(task); + generator.enqueueDataRetrieval(task); } } else { issueRefresh(); diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/AbstractPlotCreationJob.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/AbstractPlotCreationJob.java new file mode 100644 index 0000000000..03a04ba877 --- /dev/null +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/AbstractPlotCreationJob.java @@ -0,0 +1,70 @@ +/** + * 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.pointdata.thread; + +import org.eclipse.core.runtime.jobs.Job; + +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller; + +/** + * Abstract job associated with one of the many details of plot creation. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 21, 2014 2868       njensen     Initial creation
+ * 
+ * 
+ * + * @author njensen + * @version 1.0 + */ + +public abstract class AbstractPlotCreationJob extends Job { + + protected static final transient IUFStatusHandler statusHandler = UFStatus + .getHandler(PlotModelDataRequestJob.class); + + protected PlotThreadOverseer overseer; + + protected IPlotModelGeneratorCaller listener; + + public AbstractPlotCreationJob(String name, PlotThreadOverseer parent, + IPlotModelGeneratorCaller caller) { + super(name); + this.overseer = parent; + this.listener = caller; + this.setSystem(false); + } + + public boolean isDone() { + return getState() != Job.RUNNING && getState() != Job.WAITING; + } + + public boolean shutdown() { + return super.cancel(); + } + +} diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/GetDataTask.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/GetDataTask.java index cedfe482e9..2558ecf17f 100644 --- a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/GetDataTask.java +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/GetDataTask.java @@ -24,7 +24,8 @@ import java.util.List; import com.raytheon.viz.pointdata.PlotInfo; /** - * TODO Add Description + * A task to retrieve plot data for a set of stations, possibly requesting the + * parameters necessary for the plot image or the sample text or both. * *
  * 
@@ -33,6 +34,7 @@ import com.raytheon.viz.pointdata.PlotInfo;
  * Date         Ticket#    Engineer    Description
  * ------------ ---------- ----------- --------------------------
  * Jul 18, 2011            njensen     Initial creation
+ * Mar 21, 2014 2868       njensen     Improved javadoc
  * 
  * 
* diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotModelDataRequestJob.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotModelDataRequestJob.java similarity index 72% rename from cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotModelDataRequestJob.java rename to cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotModelDataRequestJob.java index 1ea761a078..6209fbabd0 100644 --- a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotModelDataRequestJob.java +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotModelDataRequestJob.java @@ -17,7 +17,7 @@ * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ -package com.raytheon.viz.pointdata; +package com.raytheon.viz.pointdata.thread; import java.util.ArrayList; import java.util.HashMap; @@ -27,30 +27,25 @@ import java.util.Map; 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 org.eclipse.swt.graphics.RGB; import com.raytheon.uf.common.dataquery.requests.RequestConstraint; import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType; import com.raytheon.uf.common.pointdata.PointDataContainer; import com.raytheon.uf.common.pointdata.PointDataView; -import com.raytheon.uf.common.status.IUFStatusHandler; -import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.common.time.DataTime; -import com.raytheon.uf.viz.core.IGraphicsTarget; -import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle; import com.raytheon.uf.viz.core.datastructure.DataCubeContainer; import com.raytheon.uf.viz.core.exception.VizException; -import com.raytheon.uf.viz.core.map.IMapDescriptor; +import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller; +import com.raytheon.viz.pointdata.PlotData; +import com.raytheon.viz.pointdata.PlotInfo; import com.raytheon.viz.pointdata.PlotModelFactory2.PlotModelElement; +import com.raytheon.viz.pointdata.PointDataRequest; import com.raytheon.viz.pointdata.rsc.PlotResourceData; -import com.raytheon.viz.pointdata.thread.GetDataTask; -import com.raytheon.viz.pointdata.thread.PlotSampleGeneratorJob; /** - * Job separated from PlotModelGenerator2 that requests plot data and passes it - * on to the PlotModelGeneratorJob. + * Job that requests plot data based on a constraintMap and the parameters + * specified inside the plot model SVG file. * *
  * 
@@ -60,6 +55,7 @@ import com.raytheon.viz.pointdata.thread.PlotSampleGeneratorJob;
  * ------------ ---------- ----------- --------------------------
  * Apr 22, 2011            njensen     Initial creation
  * May 14, 2013 1869       bsteffen    Get plots working without dataURI
+ * Mar 21, 2014 2868       njensen     Major refactor
  * 
  * 
* @@ -67,53 +63,42 @@ import com.raytheon.viz.pointdata.thread.PlotSampleGeneratorJob; * @version 1.0 */ -public class PlotModelDataRequestJob extends Job { - - private static final transient IUFStatusHandler statusHandler = UFStatus - .getHandler(PlotModelDataRequestJob.class); - - private PlotModelFactory2 plotCreator; +public class PlotModelDataRequestJob extends AbstractPlotCreationJob { private Map constraintMap; - private String plugin; + private final String plugin; - private String levelKey; + private final String levelKey; - private PlotModelGeneratorJob generatorJob; + private final List plotFields; - private PlotSampleGeneratorJob sampleJob; + private final List sampleFields; - private PlotDataThreadPool parent; - - public PlotModelDataRequestJob(IGraphicsTarget aTarget, - IMapDescriptor mapDescriptor, String plotModelFile, - String levelKey, String plugin, - Map constraintMap, - IPlotModelGeneratorCaller caller, PlotDataThreadPool parent) + public PlotModelDataRequestJob(PlotThreadOverseer parent, + IPlotModelGeneratorCaller caller, + List plotFields, + List sampleFields, String levelKey, + String plugin, Map constraintMap) throws VizException { - super("Requesting Plot Data..."); - plotCreator = new PlotModelFactory2(mapDescriptor, plotModelFile); + super("Requesting Plot Data...", parent, caller); + this.plotFields = plotFields; + this.sampleFields = sampleFields; this.plugin = plugin; this.levelKey = levelKey; this.constraintMap = constraintMap; - this.generatorJob = new PlotModelGeneratorJob(plotCreator, caller, - aTarget); - this.generatorJob.setSystem(false); - this.sampleJob = new PlotSampleGeneratorJob(plotCreator, caller); - this.sampleJob.setSystem(false); - this.parent = parent; } @Override protected IStatus run(IProgressMonitor monitor) { - while (parent.stationQueue.size() > 0) { + while (overseer.dataRetrievalQueue.size() > 0) { List stationQuery = new ArrayList(); GetDataTask task = null; synchronized (this) { - task = parent.stationQueue.poll(); + task = overseer.dataRetrievalQueue.poll(); if (task == null) { + // possibility another thread got it first continue; } List batch = task.getStations(); @@ -125,15 +110,15 @@ public class PlotModelDataRequestJob extends Job { List pme = null; switch (task.getRequestType()) { case PLOT_ONLY: - pme = this.plotCreator.getPlotFields(); + pme = plotFields; break; case SAMPLE_ONLY: - pme = this.plotCreator.getSampleFields(); + pme = sampleFields; break; case PLOT_AND_SAMPLE: pme = new ArrayList(); - pme.addAll(this.plotCreator.getPlotFields()); - pme.addAll(this.plotCreator.getSampleFields()); + pme.addAll(plotFields); + pme.addAll(sampleFields); default: break; } @@ -161,14 +146,14 @@ public class PlotModelDataRequestJob extends Job { if (infos[0].pdv != null) { switch (task.getRequestType()) { case PLOT_ONLY: - this.generatorJob.enqueue(infos); + overseer.enqueueImageGeneration(infos); break; case SAMPLE_ONLY: - this.sampleJob.enqueue(infos); + overseer.enqueueSamplePlot(infos); break; case PLOT_AND_SAMPLE: - this.generatorJob.enqueue(infos); - this.sampleJob.enqueue(infos); + overseer.enqueueImageGeneration(infos); + overseer.enqueueSamplePlot(infos); break; } } @@ -186,12 +171,13 @@ public class PlotModelDataRequestJob extends Job { List params = new ArrayList(); for (PlotModelElement p : pme) { - if (!p.parameter.equals("") && !p.parameter.contains(",")) { - params.add(p.parameter); - } else if (p.parameter.contains(",")) { - String[] individualParams = p.parameter.split(","); - for (String param : individualParams) { - params.add(param); + String param = p.getParameter(); + if (!param.equals("") && !param.contains(",")) { + params.add(param); + } else if (param.contains(",")) { + String[] individualParams = param.split(","); + for (String paramToRequest : individualParams) { + params.add(paramToRequest); } } } @@ -200,7 +186,7 @@ public class PlotModelDataRequestJob extends Job { .getPluginProperties(plugin).hasDistinctStationId; String uniquePointDataKey = "stationId"; String uniqueQueryKey = "location.stationId"; - if(!hasDistinctStationId){ + if (!hasDistinctStationId) { uniquePointDataKey = "dataURI"; uniqueQueryKey = uniquePointDataKey; @@ -208,7 +194,7 @@ public class PlotModelDataRequestJob extends Job { if (!params.contains(uniquePointDataKey)) { params.add(uniquePointDataKey); } - + Map map = new HashMap(); map.putAll(this.constraintMap); RequestConstraint rc = new RequestConstraint(); @@ -221,7 +207,7 @@ public class PlotModelDataRequestJob extends Job { String key = null; if (hasDistinctStationId) { key = info.stationId; - }else{ + } else { key = info.dataURI; } str.add(key); @@ -347,50 +333,4 @@ public class PlotModelDataRequestJob extends Job { } } - public void setUpperLimit(double upperLimit) { - this.plotCreator.setUpperLimit(upperLimit); - } - - public void setLowerLimit(double lowerLimit) { - this.plotCreator.setLowerLimit(lowerLimit); - } - - public int getPlotModelWidth() { - return this.plotCreator.getDefinedPlotModelWidth(); - } - - public void setPlotModelSize(long width) { - this.plotCreator.setPlotDimensions(width, width); - this.generatorJob.clearImageCache(); - } - - public void setPlotModelColor(RGB color) { - this.plotCreator.setColor(color); - // this.generatorJob.cleanImages(); - } - - public void setPlotModelLineWidth(int width) { - this.plotCreator.setLineWidth(width); - this.generatorJob.clearImageCache(); - } - - public void setPlotModelLineStyle(LineStyle style) { - this.plotCreator.setLineStyle(style); - this.generatorJob.clearImageCache(); - } - - public void setPlotMissingData(boolean b) { - this.plotCreator.setPlotMissingData(b); - } - - public boolean isDone() { - return getState() != Job.RUNNING && getState() != Job.WAITING - && generatorJob.isDone(); - } - - public synchronized void shutdown() { - this.cancel(); - this.generatorJob.shutdown(); - } - } diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotModelGeneratorJob.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotModelGeneratorJob.java similarity index 70% rename from cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotModelGeneratorJob.java rename to cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotModelGeneratorJob.java index 705fdb9947..9b313817ee 100644 --- a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/PlotModelGeneratorJob.java +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotModelGeneratorJob.java @@ -17,29 +17,28 @@ * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ -package com.raytheon.viz.pointdata; +package com.raytheon.viz.pointdata.thread; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.util.Map; -import java.util.concurrent.ConcurrentLinkedQueue; import org.apache.commons.collections.map.LRUMap; 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.status.IUFStatusHandler; -import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.viz.core.IGraphicsTarget; import com.raytheon.uf.viz.core.data.IRenderedImageCallback; import com.raytheon.uf.viz.core.drawables.IImage; import com.raytheon.uf.viz.core.drawables.ext.ISingleColorImageExtension; import com.raytheon.uf.viz.core.exception.VizException; +import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller; +import com.raytheon.viz.pointdata.PlotInfo; +import com.raytheon.viz.pointdata.PlotModelFactory2; /** - * Job separated from PlotModelGenerator2 that creates the plot images. + * Job that generates plot images using a PlotModelFactory2. * *
  * 
@@ -48,6 +47,7 @@ import com.raytheon.uf.viz.core.exception.VizException;
  * Date         Ticket#    Engineer    Description
  * ------------ ---------- ----------- --------------------------
  * Apr 22, 2011            njensen     Initial creation
+ * Mar 21, 2014 2868       njensen     Major refactor
  * 
  * 
* @@ -55,27 +55,20 @@ import com.raytheon.uf.viz.core.exception.VizException; * @version 1.0 */ -public class PlotModelGeneratorJob extends Job { - - private static final transient IUFStatusHandler statusHandler = UFStatus - .getHandler(PlotModelDataRequestJob.class); - - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue(); +public class PlotModelGeneratorJob extends AbstractPlotCreationJob { private PlotModelFactory2 plotCreator; - private IPlotModelGeneratorCaller caller; - private IGraphicsTarget target; @SuppressWarnings("unchecked") private Map imageCache = new LRUMap(1000); - protected PlotModelGeneratorJob(PlotModelFactory2 plotCreator, - IPlotModelGeneratorCaller caller, IGraphicsTarget target) { - super("Creating plots"); + protected PlotModelGeneratorJob(PlotThreadOverseer parent, + IPlotModelGeneratorCaller caller, PlotModelFactory2 plotCreator, + IGraphicsTarget target) { + super("Creating plots", parent, caller); this.plotCreator = plotCreator; - this.caller = caller; this.target = target; } @@ -88,9 +81,14 @@ public class PlotModelGeneratorJob extends Job { @Override protected IStatus run(IProgressMonitor monitor) { long t0 = System.currentTimeMillis(); - while (!taskQueue.isEmpty()) { + long count = 0; + while (!overseer.imageCreationQueue.isEmpty()) { try { - PlotInfo[] infos = taskQueue.poll(); + PlotInfo[] infos = overseer.imageCreationQueue.poll(); + if (infos == null) { + // possibility another thread got it first + continue; + } final BufferedImage bImage = plotCreator.getStationPlot( infos[0].pdv, infos[0].latitude, infos[0].longitude); IImage image = null; @@ -120,46 +118,40 @@ public class PlotModelGeneratorJob extends Job { } synchronized (this) { if (monitor.isCanceled()) { - if(image != null){ + if (image != null) { image.dispose(); } break; } - caller.modelGenerated(infos, image); + count++; + listener.modelGenerated(infos, image); } } catch (Exception e) { - statusHandler.error("Error creating plot", e); + statusHandler.error("Error creating plot with plotModel " + + plotCreator.getPlotModelFilename(), e); } } - plotCreator.disposeScript(); - System.out.println("Time spent creating plots: " - + (System.currentTimeMillis() - t0)); - return Status.OK_STATUS; - } - - protected void enqueue(PlotInfo[] task) { - this.taskQueue.add(task); - if (this.getState() != Job.RUNNING) { - this.schedule(); + if (count > 0) { + /* + * if count is zero it means by the time this job was scheduled and + * run, a different job took care of everything on the queue + */ + System.out.println("Time spent creating " + count + " plots: " + + (System.currentTimeMillis() - t0)); } - } - - protected int getQueueSize() { - return taskQueue.size(); + return Status.OK_STATUS; } protected void clearImageCache() { imageCache.clear(); } - public boolean isDone() { - return getState() != Job.RUNNING && getState() != Job.WAITING; - } - - protected synchronized void shutdown() { - cancel(); - taskQueue.clear(); + @Override + public boolean shutdown() { + boolean result = super.shutdown(); clearImageCache(); + plotCreator.dispose(); + return result; } } diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotSampleGeneratorJob.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotSampleGeneratorJob.java index 957a71314d..f8c408f075 100644 --- a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotSampleGeneratorJob.java +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotSampleGeneratorJob.java @@ -19,22 +19,17 @@ **/ package com.raytheon.viz.pointdata.thread; -import java.util.concurrent.ConcurrentLinkedQueue; - 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.status.IUFStatusHandler; -import com.raytheon.uf.common.status.UFStatus; import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller; import com.raytheon.viz.pointdata.PlotInfo; import com.raytheon.viz.pointdata.PlotModelFactory2; /** - * Job that uses the provided factory to generate a sampling message for a - * particular plot. + * Job that uses the provided plot model factory to generate a sampling message + * for a particular plot. * *
  * 
@@ -44,6 +39,7 @@ import com.raytheon.viz.pointdata.PlotModelFactory2;
  * ------------ ---------- ----------- --------------------------
  * Jul 13, 2011            njensen     Initial creation
  * Jun 25, 2013 1869       bsteffen    Fix plot sampling.
+ * Mar 21, 2014 2868       njensen     Major refactor
  * 
  * 
* @@ -51,22 +47,14 @@ import com.raytheon.viz.pointdata.PlotModelFactory2; * @version 1.0 */ -public class PlotSampleGeneratorJob extends Job { - - private static final transient IUFStatusHandler statusHandler = UFStatus - .getHandler(PlotSampleGeneratorJob.class); +public class PlotSampleGeneratorJob extends AbstractPlotCreationJob { private PlotModelFactory2 plotFactory; - private ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); - - private IPlotModelGeneratorCaller caller; - - public PlotSampleGeneratorJob(PlotModelFactory2 factory, - IPlotModelGeneratorCaller caller) { - super("Generating samples"); + public PlotSampleGeneratorJob(PlotThreadOverseer parent, + IPlotModelGeneratorCaller caller, PlotModelFactory2 factory) { + super("Generating samples", parent, caller); this.plotFactory = factory; - this.caller = caller; } /* @@ -77,27 +65,29 @@ public class PlotSampleGeneratorJob extends Job { */ @Override protected IStatus run(IProgressMonitor monitor) { - while (!queue.isEmpty()) { + while (!overseer.sampleTextQueue.isEmpty()) { try { - PlotInfo[] infos = queue.poll(); + PlotInfo[] infos = overseer.sampleTextQueue.poll(); + if (infos == null) { + // possibility another thread got it first + continue; + } String message = plotFactory.getStationMessage(infos[0].pdv, infos[0].dataURI); - - caller.messageGenerated(infos, message); + listener.messageGenerated(infos, message); } catch (Exception e) { - statusHandler.error("Error creating plot", e); + statusHandler.error("Error creating sample with plotModel " + + plotFactory.getPlotModelFilename(), e); } } - plotFactory.disposeSampleScript(); return Status.OK_STATUS; } - public void enqueue(PlotInfo[] task) { - this.queue.add(task); - if (this.getState() != Job.RUNNING) { - this.schedule(); - } + @Override + public boolean shutdown() { + boolean result = super.shutdown(); + plotFactory.dispose(); + return result; } - } diff --git a/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotThreadOverseer.java b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotThreadOverseer.java new file mode 100644 index 0000000000..7a1a9931de --- /dev/null +++ b/cave/com.raytheon.viz.pointdata/src/com/raytheon/viz/pointdata/thread/PlotThreadOverseer.java @@ -0,0 +1,365 @@ +/** + * 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.pointdata.thread; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.swt.graphics.RGB; + +import com.raytheon.uf.common.dataquery.requests.RequestConstraint; +import com.raytheon.uf.viz.core.IGraphicsTarget; +import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle; +import com.raytheon.uf.viz.core.exception.VizException; +import com.raytheon.uf.viz.core.map.MapDescriptor; +import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller; +import com.raytheon.viz.pointdata.PlotInfo; +import com.raytheon.viz.pointdata.PlotModelFactory2; +import com.raytheon.viz.pointdata.PlotModelFactory2.PlotModelElement; + +/** + * Oversees a variety of threads used to concurrently and quickly get plots on + * screen. There should be only one PlotThreadOverseer for each PlotResource2. + * + * This class oversees the pipeline of threads necessary for requesting plot + * data, generating plot images, and generating plot samples. Note that it does + * not involve itself with progressive disclosure, paint, or python threads. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 21, 2014 2868       njensen     Initial creation
+ * 
+ * 
+ * + * @author njensen + * @version 1.0 + */ + +public class PlotThreadOverseer { + + private static final int DATA_THREADS = 2; + + private static final int IMAGE_THREADS = DATA_THREADS; + + private static final int SAMPLE_THREADS = 1; + + protected ConcurrentLinkedQueue dataRetrievalQueue = new ConcurrentLinkedQueue(); + + protected List dataRetrievalJobList = new ArrayList( + DATA_THREADS); + + protected ConcurrentLinkedQueue imageCreationQueue = new ConcurrentLinkedQueue(); + + protected List imageCreationJobList = new ArrayList( + IMAGE_THREADS); + + protected List plotCreatorList = new ArrayList( + IMAGE_THREADS); + + protected ConcurrentLinkedQueue sampleTextQueue = new ConcurrentLinkedQueue(); + + protected List sampleTextJobList = new ArrayList( + SAMPLE_THREADS); + + private int plotModelWidth; + + /** + * Constructor + * + * @param target + * @param mapDescriptor + * @param plotModelFile + * @param levelKey + * @param plugin + * @param constraintMap + * @param caller + * @throws VizException + */ + public PlotThreadOverseer(IGraphicsTarget target, + MapDescriptor mapDescriptor, String plotModelFile, String levelKey, + String plugin, HashMap constraintMap, + IPlotModelGeneratorCaller caller) throws VizException { + + /* + * Set up image generation jobs first because the PlotModelFactory2 + * initialization will parse the SVG to determine the parameters to + * request + */ + List plotFields = null; + List sampleFields = null; + for (int i = 0; i < IMAGE_THREADS; i++) { + PlotModelFactory2 plotModelFactory = new PlotModelFactory2( + mapDescriptor, plotModelFile); + plotCreatorList.add(plotModelFactory); + imageCreationJobList.add(new PlotModelGeneratorJob(this, caller, + plotModelFactory, target)); + if (plotFields == null) { + /* + * data retrieval jobs need to know the plot and sample fields + * to request, which have now been parsed out of the SVG + */ + plotFields = plotModelFactory.getPlotFields(); + sampleFields = plotModelFactory.getSampleFields(); + plotModelWidth = plotModelFactory.getDefinedPlotModelWidth(); + } + } + + /* + * Set up the data retrieval jobs + */ + for (int i = 0; i < DATA_THREADS; i++) { + dataRetrievalJobList.add(new PlotModelDataRequestJob(this, caller, + plotFields, sampleFields, levelKey, plugin, constraintMap)); + } + + /* + * Set up the sample jobs. We could probably reuse the plot model + * factories of the image jobs, regardless, if it's a sample that uses + * python it will reuse the python job coordinator underneath, and if + * it's not...it's not clear if + * PlotModelFactory2.processSampleDirective() is thread safe + * + * TODO do we care? + */ + for (int i = 0; i < SAMPLE_THREADS; i++) { + PlotModelFactory2 plotModelFactory = new PlotModelFactory2( + mapDescriptor, plotModelFile); + sampleTextJobList.add(new PlotSampleGeneratorJob(this, caller, + plotModelFactory)); + } + } + + /** + * Enqueues a task of a batch of stations to retrieve data for + * + * @param task + * + */ + public void enqueueDataRetrieval(GetDataTask task) { + List station = task.getStations(); + + Iterator itr = station.iterator(); + while (itr.hasNext()) { + PlotInfo[] infos = itr.next(); + boolean allQueued = true; + for (PlotInfo info : infos) { + switch (task.getRequestType()) { + case PLOT_ONLY: + if (!info.plotQueued) { + allQueued = false; + info.plotQueued = true; + } + + break; + case SAMPLE_ONLY: + if (!info.sampleQueued) { + allQueued = false; + info.sampleQueued = true; + } + break; + case PLOT_AND_SAMPLE: + if (!info.sampleQueued) { + allQueued = false; + info.sampleQueued = true; + } + if (!info.plotQueued) { + allQueued = false; + info.plotQueued = true; + } + } + } + if (allQueued) { + itr.remove(); + } + } + + if (station.size() > 0) { + task.setStations(station); + dataRetrievalQueue.add(task); + for (PlotModelDataRequestJob job : dataRetrievalJobList) { + if (job.getState() != Job.RUNNING) { + job.schedule(); + } + } + } + } + + /** + * Adds a batch of PlotInfos to generate images for + * + * @param plotInfo + */ + public void enqueueImageGeneration(PlotInfo[] plotInfo) { + if (plotInfo.length > 0) { + imageCreationQueue.add(plotInfo); + for (PlotModelGeneratorJob job : imageCreationJobList) { + if (job.getState() != Job.RUNNING) { + job.schedule(); + } + } + } + } + + /** + * Adds a batch of PlotInfos to generate sample messages for + * + * @param plotInfo + */ + public void enqueueSamplePlot(PlotInfo[] plotInfo) { + if (plotInfo.length > 0) { + sampleTextQueue.add(plotInfo); + for (PlotSampleGeneratorJob job : sampleTextJobList) { + if (job.getState() != Job.RUNNING) { + job.schedule(); + } + } + } + } + + public void setPlotModelColor(RGB color) { + for (PlotModelFactory2 pmf : plotCreatorList) { + pmf.setColor(color); + } + // don't clear the image cache, it's magic + } + + public void setPlotModelLineStyle(LineStyle lineStyle) { + for (PlotModelFactory2 pmf : plotCreatorList) { + pmf.setLineStyle(lineStyle); + } + for (PlotModelGeneratorJob job : imageCreationJobList) { + job.clearImageCache(); + } + } + + public void setPlotModelLineWidth(int outlineWidth) { + for (PlotModelFactory2 pmf : plotCreatorList) { + pmf.setLineWidth(outlineWidth); + } + for (PlotModelGeneratorJob job : imageCreationJobList) { + job.clearImageCache(); + } + } + + public void setPlotMissingData(boolean plotMissingData) { + for (PlotModelFactory2 pmf : plotCreatorList) { + pmf.setPlotMissingData(plotMissingData); + } + } + + public void setLowerLimit(double lowerLimit) { + for (PlotModelFactory2 pmf : plotCreatorList) { + pmf.setLowerLimit(lowerLimit); + } + } + + public void setUpperLimit(double upperLimit) { + for (PlotModelFactory2 pmf : plotCreatorList) { + pmf.setUpperLimit(upperLimit); + } + } + + public double getOriginalPlotModelWidth() { + return plotModelWidth; + } + + public void setPlotModelSize(long round) { + for (PlotModelFactory2 pmf : plotCreatorList) { + pmf.setPlotDimensions(round, round); + } + for (PlotModelGeneratorJob job : imageCreationJobList) { + job.clearImageCache(); + } + } + + /** + * Checks if all the jobs related to requesting data, creating plot images, + * and creating samples are done + * + * @return + */ + public boolean isDone() { + for (AbstractPlotCreationJob job : dataRetrievalJobList) { + if (!job.isDone()) { + return false; + } + } + + for (AbstractPlotCreationJob job : imageCreationJobList) { + if (!job.isDone()) { + return false; + } + } + + for (AbstractPlotCreationJob job : sampleTextJobList) { + if (!job.isDone()) { + return false; + } + } + + return true; + } + + /** + * Shuts down all the threads and clears their queues in a safe manner + */ + public void shutdown() { + /* + * by clearing the job lists first, nothing else can get queued, but we + * still need a copy of them to ensure they are shut down correctly + */ + List jobListCopy = null; + + // shut down data requests first + jobListCopy = new ArrayList( + dataRetrievalJobList); + dataRetrievalJobList.clear(); + for (AbstractPlotCreationJob job : jobListCopy) { + job.shutdown(); + } + dataRetrievalQueue.clear(); + + // then shut down images + jobListCopy = new ArrayList( + imageCreationJobList); + imageCreationJobList.clear(); + for (AbstractPlotCreationJob job : jobListCopy) { + job.shutdown(); + } + imageCreationQueue.clear(); + + // and finally sampling + jobListCopy = new ArrayList(sampleTextJobList); + sampleTextJobList.clear(); + for (AbstractPlotCreationJob job : jobListCopy) { + job.shutdown(); + } + sampleTextQueue.clear(); + } + +} diff --git a/edexOsgi/com.raytheon.uf.common.python.concurrent/src/com/raytheon/uf/common/python/concurrent/PythonJobCoordinator.java b/edexOsgi/com.raytheon.uf.common.python.concurrent/src/com/raytheon/uf/common/python/concurrent/PythonJobCoordinator.java index da1f314d2e..5da769d194 100644 --- a/edexOsgi/com.raytheon.uf.common.python.concurrent/src/com/raytheon/uf/common/python/concurrent/PythonJobCoordinator.java +++ b/edexOsgi/com.raytheon.uf.common.python.concurrent/src/com/raytheon/uf/common/python/concurrent/PythonJobCoordinator.java @@ -25,6 +25,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; import jep.JepException; @@ -59,6 +60,9 @@ import com.raytheon.uf.common.python.PythonInterpreter; * Jan 31, 2013 mnash Initial creation * Jun 04, 2013 2041 bsteffen Improve exception handling for concurrent * python. + * Mar 21, 2014 2868 njensen Changed getInstance() from throwing + * RuntimeException to IllegalArgumentException + * Added refCount * * * @@ -71,10 +75,17 @@ public class PythonJobCoordinator

{ private ThreadLocal

threadLocal = null; + /** + * Tracks the number of times newInstance() vs shutdown() is called for an + * instance of a PythonJobCoordinator + */ + private AtomicInteger refCount; + private static Map> pools = new ConcurrentHashMap>(); private PythonJobCoordinator(final AbstractPythonScriptFactory

factory) { threadLocal = new ThreadLocal

() { + @Override protected P initialValue() { try { return factory.createPythonScript(); @@ -85,10 +96,11 @@ public class PythonJobCoordinator

{ }; execService = Executors.newFixedThreadPool(factory.getMaxThreads(), new PythonThreadFactory(threadLocal, factory.getName())); + refCount = new AtomicInteger(); } /** - * Gets the instance by name, or throw a {@link RuntimeException}. + * Gets the instance by name, or throw a {@link IllegalArgumentException}. * * @param name * @return @@ -99,7 +111,7 @@ public class PythonJobCoordinator

{ if (pools.containsKey(name)) { return (PythonJobCoordinator) pools.get(name); } else { - throw new RuntimeException( + throw new IllegalArgumentException( "Unable to find instance of PythonJobCoordinator named " + name + ", please call newInstance(AbstractPythonScriptFactory)"); @@ -110,7 +122,10 @@ public class PythonJobCoordinator

{ /** * Creates a new instance of this class for a new application. If the same * name already exists, it assumes that it is the same application and - * returns the existing instance. + * returns the existing instance. Also increments the reference count of + * applications using this PythonJobCoordinator. For each time that + * newInstance() is called, a corresponding call to shutdown() will be + * needed if you truly want to shut the job coordinator down. * * @param name * @param numThreads @@ -119,14 +134,15 @@ public class PythonJobCoordinator

{ public static PythonJobCoordinator newInstance( AbstractPythonScriptFactory factory) { synchronized (pools) { + PythonJobCoordinator pool = null; if (pools.containsKey(factory.getName())) { - return (PythonJobCoordinator) pools.get(factory.getName()); + pool = (PythonJobCoordinator) pools.get(factory.getName()); } else { - PythonJobCoordinator pool = new PythonJobCoordinator( - factory); + pool = new PythonJobCoordinator(factory); pools.put(factory.getName(), pool); - return pool; } + pool.refCount.getAndIncrement(); + return pool; } } @@ -171,15 +187,20 @@ public class PythonJobCoordinator

{ /** * This function should take the {@link PythonInterpreter} on each thread in * the thread pool and dispose of it and then shutdown the - * {@link ExecutorService} + * {@link ExecutorService}. This will reduce the reference count by 1, and + * will only shut down the underlying executor service and python + * interpreters if the refCount is less than 1. * * @param name */ public void shutdown() { synchronized (pools) { - pools.values().remove(this); + int count = refCount.decrementAndGet(); + if (count < 1) { + pools.values().remove(this); + execService.shutdown(); + } } - execService.shutdown(); } /** @@ -191,7 +212,9 @@ public class PythonJobCoordinator

{ */ public void shutdownTask(String name) { /* - * TODO need to add for future functionality + * TODO need to add for future functionality, arg should probably be an + * IPythonExecutor, not a String */ } + }