Skip to content

Commit 4d8a642

Browse files
authored
DM-8208 merge Improve error handling on external task failure, pr #233
DM 8208 Improve error handling on external task failure
2 parents 437aca7 + e9be089 commit 4d8a642

File tree

9 files changed

+162
-58
lines changed

9 files changed

+162
-58
lines changed

docs/firefly-external-task-launcher.md

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,20 @@ A sample javascript, which builds up on the examples below is in
105105
Create an image viewer and place it into the `<div>` id `plotHere`.
106106
107107
```html
108-
<div id="plotHere" style="width: 350px; height: 350px;"></div>
108+
<div id="imageHere" style="width: 350px; height: 350px;"></div>
109109
```
110110
111111
```js
112112
function onFireflyLoaded() {
113-
var iv2= firefly.makeImageViewer("plotHere");
114-
iv2.plot({
115-
"id" :"FileFromExternalTask",
116-
"launcher" :"python",
117-
"task" :"someImageTask",
118-
"taskParams" : {"p1":1,"p2":2},
119-
"Title" :"Example FITS Image'",
120-
"ColorTable" :"16",
121-
"RangeValues":firefly.serializeRangeValues("Sigma",-2,8,"Linear")
122-
});
113+
var req = {
114+
id : 'FileFromExternalTask',
115+
launcher : 'python',
116+
task : 'someImageTask',
117+
taskParams : {p1:1,p2:2},
118+
Title : 'FITS from Python task',
119+
ColorTable : 2
120+
};
121+
firefly.showImage('imageHere', req);
123122
}
124123
```
125124
@@ -135,12 +134,11 @@ The table is plotted in the `<div>` id `tableHere`.
135134
136135
```js
137136
function onFireflyLoaded() {
138-
var tableData= { "processor" : "TableFromExternalTask",
139-
"launcher" : "python",
140-
"task" : "TestTask3",
141-
"taskParams" : { "param1" : "first arg", "param2" : "second arg" }
142-
};
143-
firefly.showTable(tableData, "tableHere");
137+
var tblReq = firefly.util.table.makeTblRequest('TableFromExternalTask', 'Table from Python task',
138+
{ launcher : 'python', task : 'TableTask', taskParams : {p1: 1, p2: 2} }, // search parameters
139+
{ pageSize: 15} // table options
140+
);
141+
firefly.showTable('tableHere', tblReq);
144142
}
145143
```
146144
@@ -157,21 +155,23 @@ In this example, we get the histogram data from an exernal task and feed them to
157155
158156
```js
159157
function onFireflyLoaded() {
160-
var launcher = 'python';
161-
var task = 'JsonTaskToGetHistogramData';
162-
var taskParams = { 'numbins': bins };
163-
firefly.getJsonFromTask(launcher, task, taskParams)
164-
.then(
165-
function (histdata) {
166-
firefly.showHistogram(
167-
{'descr' : 'Histogram data returned from python JSON task',
168-
'binColor' : '#3d3033',
169-
'height' : 350,
170-
'data': histdata}, 'chartHere');
171-
}
172-
).catch(function (reason) {
173-
console.log('Error fetching JSON data from '+launcher+' task '+task+': '+reason);
174-
}
175-
);
158+
var launcher = 'python';
159+
var task = 'JsonTaskToGetHistogramData';
160+
var taskParams = {'numbins': 10};
161+
firefly.getJsonFromTask(launcher, task, taskParams)
162+
.then(function (histdata) {
163+
console.log('Returned JSON: ' + JSON.stringify(histdata));
164+
firefly.util.renderDOM("chartHere", firefly.ui.Histogram,
165+
{
166+
desc: 'Histogram data from Python JSON task',
167+
binColor: '#3d3033',
168+
height: 350,
169+
data: histdata
170+
});
171+
})
172+
.catch(function (reason) {
173+
console.error('Error fetching JSON data from ' + launcher + ' task ' + task + ': ' + reason);
174+
document.getElementById('chartHere').innerHTML = '<p style="color:red">'+reason+'</p>';
175+
});
176176
}
177177
```

src/firefly/html/demo/ffapi-highlevel-charttest.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<meta http-equiv="Pragma" content="no-cache">
1212
<meta http-equiv="Expires" content="0">
1313
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
14-
<title>Demo of Firefly Tools</title>
14+
<title>Charts in Firefly</title>
1515
</head>
1616

1717
<body>
@@ -25,7 +25,7 @@ <h2>
2525

2626
<div id="histogramHere" style="width: 800px; height: 550px; border: solid 1px"></div>
2727
<br/><br/>
28-
<div id="chartHere" style="width: 800px; height: 550px; border: solid 1px;"></div>
28+
<div id="xyplotHere" style="width: 800px; height: 550px; border: solid 1px;"></div>
2929

3030
<script type="text/javascript">
3131
{
@@ -89,12 +89,10 @@ <h2>
8989
xCol: 'ra1',
9090
yCol: 'dec1'
9191
};
92-
firefly.showXYPlot('chartHere', chartParams);
93-
94-
92+
firefly.showXYPlot('xyplotHere', chartParams);
9593

9694
}
97-
}
95+
}
9896

9997

10098
</script>
@@ -103,3 +101,5 @@ <h2>
103101
<script type="text/javascript" src="../firefly_loader.js"></script>
104102

105103

104+
</body>
105+
</html>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<!DOCTYPE html>
2+
3+
<!--
4+
~ License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
5+
-->
6+
7+
<!--
8+
This is a test for Python (external task) launcher.
9+
Firefly supports getting JSON data, table, or image from an external task
10+
-->
11+
<html lang="en">
12+
<head>
13+
<meta charset="UTF-8">
14+
<title>Testing getJSONFromTask</title>
15+
</head>
16+
<body>
17+
<div id="chartHere" style="display:inline-block; width: 800px; height: 350px;
18+
border: solid 1px;"></div>
19+
<br><br>
20+
<div id="tableHere" style="display:inline-block; width: 800px; height: 350px;
21+
border: solid 1px;"></div>
22+
<br><br>
23+
<div id="imageHere" style="width: 350px; height: 350px;"></div>
24+
</body>
25+
26+
<script type="text/javascript">
27+
{
28+
onFireflyLoaded = function (firefly) {
29+
30+
// json - show histogram, generated by firefly/python/SamplePythonLauncher.py
31+
var launcher = 'python';
32+
var task = 'JsonTaskToGetHistogramData';
33+
var taskParams = {'numbins': 10};
34+
firefly.getJsonFromTask(launcher, task, taskParams)
35+
.then(function (histdata) {
36+
console.log('Returned JSON: ' + JSON.stringify(histdata));
37+
firefly.util.renderDOM("chartHere", firefly.ui.Histogram,
38+
{
39+
desc: 'Histogram data returned from Python JSON task',
40+
binColor: '#3d3033',
41+
height: 350,
42+
data: histdata
43+
});
44+
})
45+
.catch(function (reason) {
46+
console.error('Error fetching JSON data from ' + launcher + ' task ' + task + ': ' + reason);
47+
document.getElementById('chartHere').innerHTML = '<p style="color:red">'+reason+'</p>';
48+
});
49+
50+
// table - show table, generated by firefly/python/SamplePythonLauncher.py
51+
var tblReq = firefly.util.table.makeTblRequest('TableFromExternalTask', 'Table from Python task',
52+
{ launcher : 'python', task : 'TableTask', taskParams : {p1: 1, p2: 2} }, // search parameters
53+
{ pageSize: 15} // table options
54+
);
55+
firefly.showTable('tableHere', tblReq);
56+
57+
// image - show FITS image, generated by firefly/python/SamplePythonLauncher.py
58+
var req = {
59+
id : 'FileFromExternalTask',
60+
launcher : 'python',
61+
task : 'someImageTask',
62+
taskParams : {p1:1,p2:2},
63+
Title : 'FITS from Python task',
64+
ColorTable : 2
65+
};
66+
firefly.showImage('imageHere', req);
67+
}
68+
}
69+
</script>
70+
71+
<script type="text/javascript" src="../firefly_loader.js"></script>
72+
73+
74+
</html>

src/firefly/java/edu/caltech/ipac/firefly/server/ExternalTaskHandlerImpl.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,15 @@ public void setup(ExternalTaskLauncher launcher, Map<String, String> env) throws
7575
launcher.addParam("-o", ServerContext.getExternalPermWorkDir());
7676

7777
} catch (Exception e) {
78+
String err = null;
7879
if (e instanceof ParseException) {
7980
ParseException pe = (ParseException)e;
80-
LOGGER.error(e, "invalid JSON in taskParams; position: " + pe.getPosition() + "; " + pe.getMessage() + "; " +taskParams);
81+
err = "Invalid JSON in taskParams: " + pe.toString() + " " +taskParams;
82+
LOGGER.error(e, err);
8183
} else {
8284
LOGGER.error(e);
8385
}
84-
throw new InterruptedException(task+" launcher setup failed: "+e.getMessage());
86+
throw new InterruptedException(task+" launcher setup failed: " + (err==null?e.getMessage():err));
8587
}
8688
}
8789

@@ -146,7 +148,16 @@ public void handleOut(InputStream is) throws InterruptedException {
146148
result = status.toString();
147149
}
148150
}
151+
} else {
152+
setErrorIfEmpty("Unable to parse task status.");
149153
}
154+
} else {
155+
if (collectStatus) {
156+
setErrorIfEmpty("No lines after "+STATUS_KEYWORD+".");
157+
} else {
158+
setErrorIfEmpty("No "+STATUS_KEYWORD+" in external task standard output.");
159+
}
160+
150161
}
151162

152163
}
@@ -158,6 +169,7 @@ public void handleError(InputStream is) throws InterruptedException {
158169
try {
159170
for (String line = reader.readLine(); (line != null); line = reader.readLine()) {
160171
LOGGER.warn(line);
172+
addError(line);
161173
}
162174
} catch (Exception e) {
163175
Logger.getLogger().error(e);
@@ -186,7 +198,7 @@ public File getOutfile() throws DataAccessException {
186198
throw new DataAccessException("Failed to obtain data. " + getError());
187199
} else {
188200
if (outfile == null) {
189-
throw new DataAccessException("Output file is not available");
201+
throw new DataAccessException("Output file is not returned from the task.");
190202
} else {
191203
File ofile = new File(outfile);
192204
if (!ofile.canRead()) {
@@ -211,6 +223,21 @@ public String getResult() throws DataAccessException {
211223
//============================================================================
212224

213225

226+
private void setErrorIfEmpty(String error) {
227+
if (this.error == null || this.error.trim().length() == 0) {
228+
this.error = error;
229+
}
230+
}
231+
232+
private void addError(String error) {
233+
if (this.error == null || this.error.trim().length() == 0) {
234+
this.error = "";
235+
} else {
236+
this.error += " ";
237+
}
238+
this.error += error;
239+
}
240+
214241
private static java.nio.file.Path createWorkDir(String task) throws IOException {
215242
return Files.createTempDirectory(ServerContext.getExternalTempWorkDir().toPath(), task);
216243
}

src/firefly/java/edu/caltech/ipac/firefly/server/query/JsonFromExternalTask.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public String getUniqueID(ServerRequest request) {
3535
for (String p : ExternalTaskHandler.ALL_PARAMS) {
3636
String v = request.getParam(p);
3737
if (v != null) {
38-
uid += "|" + p;
38+
uid += "|" + v;
3939
}
4040
}
4141
return uid;
@@ -44,6 +44,9 @@ public String getUniqueID(ServerRequest request) {
4444
@Override
4545
public String getData(ServerRequest request) throws DataAccessException {
4646
String launcher = request.getParam(ExternalTaskHandler.LAUNCHER);
47+
if (launcher == null) {
48+
throw new DataAccessException(ExternalTaskHandler.LAUNCHER+" parameter is not found in request.");
49+
}
4750
ExternalTaskLauncher taskLauncher = new ExternalTaskLauncher(launcher);
4851

4952
try {
@@ -59,13 +62,13 @@ public String getData(ServerRequest request) throws DataAccessException {
5962
// get result from outfile
6063

6164
if (!ServerContext.isFileInPath(outFile)) {
62-
throw new SecurityException("Access is not permitted.");
65+
throw new SecurityException("Access to "+outFile.getAbsolutePath()+" is not permitted.");
6366
}
6467

6568
return FileUtil.readFile(outFile);
6669
} catch (Exception e) {
67-
LOGGER.error(e, "Unable to data from external task: "+request.toString());
68-
throw new DataAccessException("Unable to data from external task: "+e.getMessage());
70+
LOGGER.error(e, "Unable get to data from external task: "+request.toString());
71+
throw new DataAccessException("Unable to get data from external task: "+e.getMessage());
6972
}
7073
}
7174

src/firefly/java/edu/caltech/ipac/firefly/server/query/SearchManager.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
import edu.caltech.ipac.firefly.server.packagedata.PackageMaster;
1717
import edu.caltech.ipac.firefly.server.util.Logger;
1818
import edu.caltech.ipac.firefly.server.util.QueryUtil;
19-
import edu.caltech.ipac.firefly.server.util.ipactable.*;
19+
import edu.caltech.ipac.firefly.server.util.ipactable.DataGroupPart;
20+
import edu.caltech.ipac.firefly.server.util.ipactable.DataGroupReader;
21+
import edu.caltech.ipac.firefly.server.util.ipactable.IpacTableParser;
22+
import edu.caltech.ipac.firefly.server.util.ipactable.TableDef;
2023
import edu.caltech.ipac.util.Assert;
2124
import edu.caltech.ipac.util.IpacTableUtil;
22-
import org.json.simple.JSONObject;
2325
import org.json.simple.JSONValue;
2426
import org.json.simple.parser.JSONParser;
2527
import org.json.simple.parser.ParseException;
@@ -67,8 +69,8 @@ public String getJSONData(ServerRequest request) throws DataAccessException {
6769
return jsonText;
6870
}
6971
catch(ParseException pe){
70-
LOGGER.error(processor.getUniqueID(request) + " return invalid JSON; position: " + pe.getPosition() + "; " + pe.getMessage() + "; " + jsonText);
71-
throw new DataAccessException("Request failed: invalid JSON; position: " + pe.getPosition() + "; " + pe.getMessage());
72+
LOGGER.error(processor.getUniqueID(request) + " Can not parse returned JSON: " + pe.toString() + "\n" + jsonText);
73+
throw new DataAccessException(request.getRequestId()+" Can not parse returned JSON: " + pe.toString());
7274
}
7375
} else {
7476
throw new DataAccessException("Request fail inspection. Operation aborted.");

src/firefly/js/core/JsonUtils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export const jsonRequest= function(baseUrl, cmd, paramList, doPost) {
8383
return;
8484
}
8585
response.json().then( (result) => {
86-
if (has(result,'0')) {
86+
if (has(result,'0.success')) {
8787
if (toBoolean(result[0].success)) {
8888
resolve(result[0].data ? result[0].data : result[0]);
8989
}

src/firefly/js/rpc/SearchServicesJson.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export const getJsonData = function(request) {
136136
paramList.push({name:ServerParams.REQUEST, value: request.toString()});
137137

138138
return doService(doJsonP(), ServerParams.JSON_DATA, paramList
139-
).then((data) => {return JSON.parse(data); });
139+
).then((data) => {return data; });
140140
};
141141

142142
/**
@@ -253,8 +253,6 @@ export const getDownloadProgress= function(fileKey) {
253253

254254

255255
/**
256-
*
257-
* @param {array|string} ids - one id or an array of ids
258256
* @param {string} email
259257
* @return {Promise}
260258
*/
@@ -273,7 +271,7 @@ export const setEmail= function(email) {
273271
/**
274272
*
275273
* @param {array|string} ids one id or an array of ids
276-
* @param {JobAttributes} job attribute
274+
* @param {JobAttributes} attribute job attribute
277275
* @return {Promise}
278276
*/
279277
export const setAttribute= function(ids, attribute) {

src/firefly/js/tables/TableUtil.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const LSSTQueryPID = 'LSSTCataLogSearch';
3131
* @param {object} [params] the parameters to include with this request.
3232
* @param {TableRequest} [options] more options. see TableRequest for details.
3333
* @returns {TableRequest}
34-
* @pubic
34+
* @public
3535
* @func makeTblRequest
3636
* @memberof firefly.util.table
3737
*/
@@ -52,9 +52,9 @@ export function makeTblRequest(id, title, params={}, options={}) {
5252
* @param {string} [alt_source] use this if source does not exists.
5353
* @param {TableRequest} [options] more options. see TableRequest for details.
5454
* @returns {TableRequest}
55-
* @pubic
55+
* @public
5656
* @func makeFileRequest
57-
* @memberof firefly.util.table
57+
* @memberof firefly.util.table
5858
*/
5959
export function makeFileRequest(title, source, alt_source, options={}) {
6060
const id = 'IpacTableFromSource';

0 commit comments

Comments
 (0)