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("<", "&lt");
-            message = message.replace(">", "&gt");
-            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("===> &ltBLANK LINE&gt");
-                } 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("<", "&lt");
-            message = message.replace(">", "&gt");
-            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("<", "&lt");
+        message = message.replace(">", "&gt");
+        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("===> &ltBLANK LINE&gt \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;
+    }
 }