Implement root cause based error reporting for API and web UI.
diff --git a/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/APIServlet.java b/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/APIServlet.java
index 12fa51a..02c61c1 100644
--- a/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/APIServlet.java
+++ b/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/APIServlet.java
@@ -23,6 +23,7 @@
import edu.uci.ics.asterix.common.config.GlobalConfig;
import edu.uci.ics.asterix.metadata.MetadataManager;
import edu.uci.ics.asterix.result.ResultReader;
+import edu.uci.ics.asterix.result.ResultUtils;
import edu.uci.ics.hyracks.api.client.IHyracksClientConnection;
import edu.uci.ics.hyracks.api.dataset.IHyracksDataset;
import edu.uci.ics.hyracks.client.dataset.HyracksDataset;
@@ -81,28 +82,12 @@
out.println("<PRE>Duration of all jobs: " + duration + "</PRE>");
} catch (ParseException | TokenMgrError | edu.uci.ics.asterix.aqlplus.parser.TokenMgrError pe) {
out.println("<pre class=\"error\">");
- String message = pe.getMessage();
- message = message.replace("<", "<");
- message = message.replace(">", ">");
- out.println("SyntaxError:" + message);
- int pos = message.indexOf("line");
- if (pos > 0) {
- int columnPos = message.indexOf(",", pos + 1 + "line".length());
- int lineNo = Integer.parseInt(message.substring(pos + "line".length() + 1, columnPos));
- String[] lines = query.split("\n");
- if (lineNo >= lines.length) {
- out.println("===> <BLANK LINE>");
- } else {
- String line = lines[lineNo - 1];
- out.println("==> " + line);
- }
- }
+ String errorMessage = ResultUtils.buildParseExceptionMessage(pe, query);
+ out.println(errorMessage);
out.println("</pre>");
} catch (Exception e) {
GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, e.getMessage(), e);
- out.println("<pre class=\"error\">");
- out.println(e.getMessage());
- out.println("</pre>");
+ ResultUtils.webUIErrorHandler(out, e);
}
}
diff --git a/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/RESTAPIServlet.java b/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/RESTAPIServlet.java
index 29feb5e..48a1a44 100644
--- a/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/RESTAPIServlet.java
+++ b/asterix-app/src/main/java/edu/uci/ics/asterix/api/http/servlet/RESTAPIServlet.java
@@ -31,6 +31,7 @@
import edu.uci.ics.asterix.aql.base.Statement.Kind;
import edu.uci.ics.asterix.aql.parser.AQLParser;
import edu.uci.ics.asterix.aql.parser.ParseException;
+import edu.uci.ics.asterix.aql.parser.TokenMgrError;
import edu.uci.ics.asterix.aql.translator.AqlTranslator;
import edu.uci.ics.asterix.metadata.MetadataManager;
import edu.uci.ics.asterix.result.ResultReader;
@@ -94,26 +95,12 @@
aqlTranslator.compileAndExecute(hcc, hds, asyncResults);
- } catch (ParseException pe) {
- StringBuilder errorMessage = new StringBuilder();
- String message = pe.getMessage();
- message = message.replace("<", "<");
- message = message.replace(">", ">");
- errorMessage.append("SyntaxError:" + message + "\n");
- int pos = message.indexOf("line");
- if (pos > 0) {
- int columnPos = message.indexOf(",", pos + 1 + "line".length());
- int lineNo = Integer.parseInt(message.substring(pos + "line".length() + 1, columnPos));
- String line = query.split("\n")[lineNo - 1];
- errorMessage.append("==> " + line + "\n");
- }
- JSONObject errorResp = ResultUtils.getErrorResponse(2, errorMessage.toString());
+ } catch (ParseException | TokenMgrError | edu.uci.ics.asterix.aqlplus.parser.TokenMgrError pe) {
+ String errorMessage = ResultUtils.buildParseExceptionMessage(pe, query);
+ JSONObject errorResp = ResultUtils.getErrorResponse(2, errorMessage);
out.write(errorResp.toString());
} catch (Exception e) {
- StringBuilder errorMessage = new StringBuilder();
- errorMessage.append(e.getMessage());
- JSONObject errorResp = ResultUtils.getErrorResponse(99, errorMessage.toString());
- out.write(errorResp.toString());
+ ResultUtils.apiErrorHandler(out, e);
}
}
diff --git a/asterix-app/src/main/java/edu/uci/ics/asterix/aql/translator/AqlTranslator.java b/asterix-app/src/main/java/edu/uci/ics/asterix/aql/translator/AqlTranslator.java
index 41dde5d..2d61615 100644
--- a/asterix-app/src/main/java/edu/uci/ics/asterix/aql/translator/AqlTranslator.java
+++ b/asterix-app/src/main/java/edu/uci/ics/asterix/aql/translator/AqlTranslator.java
@@ -112,6 +112,7 @@
import edu.uci.ics.hyracks.api.client.IHyracksClientConnection;
import edu.uci.ics.hyracks.api.dataset.IHyracksDataset;
import edu.uci.ics.hyracks.api.dataset.ResultSetId;
+import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
import edu.uci.ics.hyracks.api.io.FileReference;
import edu.uci.ics.hyracks.api.job.JobId;
import edu.uci.ics.hyracks.api.job.JobSpecification;
@@ -283,6 +284,8 @@
}
}
+ } catch (HyracksDataException e) {
+ throw e;
} catch (Exception e) {
throw new AlgebricksException(e);
}
@@ -1373,33 +1376,47 @@
handle.put(jobId.getId());
handle.put(metadataProvider.getResultSetId().getId());
response.put("handle", handle);
+ out.print(response);
+ out.flush();
} else {
+ if (pdf == DisplayFormat.HTML) {
+ out.println("<pre>");
+ }
+
ByteBuffer buffer = ByteBuffer.allocate(ResultReader.FRAME_SIZE);
ResultReader resultReader = new ResultReader(hcc, hdc);
resultReader.open(jobId, metadataProvider.getResultSetId());
buffer.clear();
- JSONArray results = new JSONArray();
+
while (resultReader.read(buffer) > 0) {
- results.put(ResultUtils.getJSONFromBuffer(buffer, resultReader.getFrameTupleAccessor()));
+ response.put("results", ResultUtils.getJSONFromBuffer(buffer, resultReader.getFrameTupleAccessor()));
buffer.clear();
+ switch(pdf) {
+ case HTML:
+ ResultUtils.prettyPrintHTML(out, response);
+ break;
+ case TEXT:
+ case JSON:
+ out.print(response);
+ break;
+ }
+ out.flush();
}
- response.put("results", results);
- }
- switch (pdf) {
- case HTML:
- out.println("<pre>");
- ResultUtils.prettyPrintHTML(out, response);
+
+ if (pdf == DisplayFormat.HTML) {
out.println("</pre>");
- break;
- case TEXT:
- case JSON:
- out.print(response);
- break;
+ }
+
}
hcc.waitForCompletion(jobId);
}
return queryResult;
+ } catch (HyracksDataException e) {
+ if (bActiveTxn) {
+ MetadataManager.INSTANCE.abortTransaction(mdTxnCtx);
+ }
+ throw e;
} catch (Exception e) {
if (bActiveTxn) {
MetadataManager.INSTANCE.abortTransaction(mdTxnCtx);
diff --git a/asterix-app/src/main/java/edu/uci/ics/asterix/result/ResultUtils.java b/asterix-app/src/main/java/edu/uci/ics/asterix/result/ResultUtils.java
index dec3128..56769d9 100644
--- a/asterix-app/src/main/java/edu/uci/ics/asterix/result/ResultUtils.java
+++ b/asterix-app/src/main/java/edu/uci/ics/asterix/result/ResultUtils.java
@@ -17,11 +17,16 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
+import java.util.logging.Level;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import com.sun.el.parser.ParseException;
+
+import edu.uci.ics.asterix.common.config.GlobalConfig;
+import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
import edu.uci.ics.hyracks.api.comm.IFrameTupleAccessor;
import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
import edu.uci.ics.hyracks.dataflow.common.comm.util.ByteBufferInputStream;
@@ -79,4 +84,70 @@
// TODO(madhusudancs): Figure out what to do when JSONException occurs while building the results.
}
}
+
+ public static void webUIErrorHandler(PrintWriter out, Exception e) {
+ String errPrefix = "";
+ if (e instanceof AlgebricksException) {
+ errPrefix = "Compilation error: ";
+ } else if (e instanceof HyracksDataException) {
+ errPrefix = "Runtime error: ";
+ }
+
+ Throwable cause = getRootCause(e);
+
+ GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ out.println("<pre class=\"error\">");
+ out.println(errPrefix + cause.getMessage());
+ out.println("</pre>");
+ }
+
+ public static void apiErrorHandler(PrintWriter out, Exception e) {
+ int errorCode = 99;
+ String errPrefix = "";
+ if (e instanceof ParseException) {
+ errorCode = 2;
+ } else if (e instanceof AlgebricksException) {
+ errorCode = 3;
+ errPrefix = "Compilation error: ";
+ } else if (e instanceof HyracksDataException) {
+ errorCode = 4;
+ errPrefix = "Runtime error: ";
+ }
+
+ Throwable cause = getRootCause(e);
+
+ GlobalConfig.ASTERIX_LOGGER.log(Level.SEVERE, e.getMessage(), e);
+ JSONObject errorResp = ResultUtils.getErrorResponse(errorCode, errPrefix + cause.getMessage());
+ out.write(errorResp.toString());
+ }
+
+ public static String buildParseExceptionMessage(Throwable e, String query) {
+ StringBuilder errorMessage = new StringBuilder();
+ String message = e.getMessage();
+ message = message.replace("<", "<");
+ message = message.replace(">", ">");
+ errorMessage.append("SyntaxError:" + message + "\n");
+ int pos = message.indexOf("line");
+ if (pos > 0) {
+ int columnPos = message.indexOf(",", pos + 1 + "line".length());
+ int lineNo = Integer.parseInt(message.substring(pos + "line".length() + 1, columnPos));
+ String[] lines = query.split("\n");
+ if (lineNo >= lines.length) {
+ errorMessage.append("===> <BLANK LINE> \n");
+ } else {
+ String line = lines[lineNo - 1];
+ errorMessage.append("==> " + line);
+ }
+ }
+ return errorMessage.toString();
+ }
+
+ private static Throwable getRootCause(Throwable cause) {
+ Throwable nextCause = cause.getCause();
+ while (nextCause != null) {
+ cause = nextCause;
+ nextCause = cause.getCause();
+ }
+ return cause;
+ }
}