add error locations to exceptions in the ADM parser
escape HTML entities in error messages for the WebUI
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 18ed62a..e1b098b 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
@@ -21,6 +21,8 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
@@ -35,6 +37,24 @@
import edu.uci.ics.hyracks.dataflow.common.comm.util.ByteBufferInputStream;
public class ResultUtils {
+ static Map<Character, String> HTML_ENTITIES = new HashMap<Character, String>();
+
+ static {
+ HTML_ENTITIES.put('"', """);
+ HTML_ENTITIES.put('&', "&");
+ HTML_ENTITIES.put('<', "<");
+ HTML_ENTITIES.put('>', ">");
+ }
+
+ public static String escapeHTML(String s) {
+ for (Character c : HTML_ENTITIES.keySet()) {
+ if (s.indexOf(c) >= 0) {
+ s = s.replace(c.toString(), HTML_ENTITIES.get(c));
+ }
+ }
+ return s;
+ }
+
public static void getJSONFromBuffer(ByteBuffer buffer, IFrameTupleAccessor fta, JSONArray resultRecords)
throws HyracksDataException {
ByteBufferInputStream bbis = new ByteBufferInputStream();
@@ -90,11 +110,11 @@
public static void webUIErrorHandler(PrintWriter out, Exception e) {
String errorTemplate = readTemplateFile("/webui/errortemplate.html", "%s\n%s\n%s");
- String errorOutput = String.format(errorTemplate, extractErrorMessage(e), extractErrorSummary(e),
- extractFullStackTrace(e));
+ String errorOutput = String.format(errorTemplate, escapeHTML(extractErrorMessage(e)),
+ escapeHTML(extractErrorSummary(e)), escapeHTML(extractFullStackTrace(e)));
out.println(errorOutput);
}
-
+
public static void webUIParseExceptionHandler(PrintWriter out, Throwable e, String query) {
String errorTemplate = readTemplateFile("/webui/errortemplate_message.html", "<pre class=\"error\">%s\n</pre>");
diff --git a/asterix-maven-plugins/lexer-generator-maven-plugin/src/main/resources/Lexer.java b/asterix-maven-plugins/lexer-generator-maven-plugin/src/main/resources/Lexer.java
index dae1fb1..4ce840d 100644
--- a/asterix-maven-plugins/lexer-generator-maven-plugin/src/main/resources/Lexer.java
+++ b/asterix-maven-plugins/lexer-generator-maven-plugin/src/main/resources/Lexer.java
@@ -96,6 +96,14 @@
new String(buffer, 0, bufpos);
}
+ public int getColumn() {
+ return column;
+ }
+
+ public int getLine() {
+ return line;
+ }
+
public static String tokenKindToString(int token) {
return tokenImage[token];
}
@@ -108,22 +116,14 @@
// Parse error management
// ================================================================================
- protected int parseError(String reason) throws [LEXER_NAME]Exception {
- StringBuilder message = new StringBuilder();
- message.append(reason).append("\n");
- message.append("Line: ").append(line).append("\n");
- message.append("Row: ").append(column).append("\n");
- throw new [LEXER_NAME]Exception(message.toString());
- }
-
protected int parseError(int ... tokens) throws [LEXER_NAME]Exception {
StringBuilder message = new StringBuilder();
- message.append("Error while parsing. ");
- message.append(" Line: ").append(line);
- message.append(" Row: ").append(column);
- message.append(" Expecting:");
- for (int tokenId : tokens){
- message.append(" ").append([LEXER_NAME].tokenKindToString(tokenId));
+ message.append("Parse error at (").append(line).append(", ").append(column).append(")");
+ if (tokens.length > 0) {
+ message.append(" expecting:");
+ for (int tokenId : tokens){
+ message.append(" ").append([LEXER_NAME].tokenKindToString(tokenId));
+ }
}
throw new [LEXER_NAME]Exception(message.toString());
}
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/operators/file/ADMDataParser.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/operators/file/ADMDataParser.java
index 1d7429b..735bf2a5 100644
--- a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/operators/file/ADMDataParser.java
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/operators/file/ADMDataParser.java
@@ -77,12 +77,57 @@
private String mismatchErrorMessage = "Mismatch Type, expecting a value of type ";
private String mismatchErrorMessage2 = " got a value of type ";
+ static class ParseException extends AsterixException {
+ private static final long serialVersionUID = 1L;
+ private int line = -1;
+ private int column = -1;
+
+ public ParseException(String message) {
+ super(message);
+ }
+
+ public ParseException(Throwable cause) {
+ super(cause);
+ }
+
+ public ParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ParseException(Throwable cause, int line, int column) {
+ super(cause);
+ setLocation(line, column);
+ }
+
+ public void setLocation(int line, int column) {
+ this.line = line;
+ this.column = column;
+ }
+
+ public String getMessage() {
+ StringBuilder msg = new StringBuilder("Parse error");
+ if (line >= 0) {
+ if (column >= 0) {
+ msg.append(" at (" + line + ", " + column + ")");
+ } else {
+ msg.append(" in line " + line);
+ }
+ }
+ return msg.append(": " + super.getMessage()).toString();
+ }
+ }
+
@Override
public boolean parse(DataOutput out) throws AsterixException {
try {
return parseAdmInstance((IAType) recordType, datasetRec, out);
- } catch (IOException | AdmLexerException e) {
+ } catch (IOException e) {
+ throw new ParseException(e, admLexer.getLine(), admLexer.getColumn());
+ } catch (AdmLexerException e) {
throw new AsterixException(e);
+ } catch (ParseException e) {
+ e.setLocation(admLexer.getLine(), admLexer.getColumn());
+ throw e;
}
}
@@ -93,7 +138,7 @@
try {
admLexer = new AdmLexer(new java.io.InputStreamReader(in));
} catch (IOException e) {
- throw new AsterixException(e);
+ throw new ParseException(e);
}
}
@@ -116,14 +161,14 @@
if (checkType(ATypeTag.NULL, objectType, out)) {
nullSerde.serialize(ANull.NULL, out);
} else
- throw new AsterixException(" This field can not be null ");
+ throw new ParseException("This field can not be null");
break;
}
case AdmLexer.TOKEN_TRUE_LITERAL: {
if (checkType(ATypeTag.BOOLEAN, objectType, out)) {
booleanSerde.serialize(ABoolean.TRUE, out);
} else
- throw new AsterixException(mismatchErrorMessage + objectType.getTypeName());
+ throw new ParseException(mismatchErrorMessage + objectType.getTypeName());
break;
}
case AdmLexer.TOKEN_BOOLEAN_CONS: {
@@ -134,7 +179,7 @@
if (checkType(ATypeTag.BOOLEAN, objectType, out)) {
booleanSerde.serialize(ABoolean.FALSE, out);
} else
- throw new AsterixException(mismatchErrorMessage + objectType.getTypeName());
+ throw new ParseException(mismatchErrorMessage + objectType.getTypeName());
break;
}
case AdmLexer.TOKEN_DOUBLE_LITERAL: {
@@ -195,7 +240,7 @@
admLexer.getLastTokenImage().length() - 1));
stringSerde.serialize(aString, out);
} else
- throw new AsterixException(mismatchErrorMessage + objectType.getTypeName());
+ throw new ParseException(mismatchErrorMessage + objectType.getTypeName());
break;
}
case AdmLexer.TOKEN_STRING_CONS: {
@@ -226,7 +271,7 @@
}
}
}
- throw new AsterixException("Wrong interval data parsing for date interval.");
+ throw new ParseException("Wrong interval data parsing for date interval.");
}
case AdmLexer.TOKEN_INTERVAL_TIME_CONS: {
if (checkType(ATypeTag.INTERVAL, objectType, out)) {
@@ -240,7 +285,7 @@
}
}
}
- throw new AsterixException("Wrong interval data parsing for time interval.");
+ throw new ParseException("Wrong interval data parsing for time interval.");
}
case AdmLexer.TOKEN_INTERVAL_DATETIME_CONS: {
if (checkType(ATypeTag.INTERVAL, objectType, out)) {
@@ -254,7 +299,7 @@
}
}
}
- throw new AsterixException("Wrong interval data parsing for datetime interval.");
+ throw new ParseException("Wrong interval data parsing for datetime interval.");
}
case AdmLexer.TOKEN_DURATION_CONS: {
parseConstructor(ATypeTag.DURATION, objectType, out);
@@ -297,7 +342,7 @@
objectType = getComplexType(objectType, ATypeTag.UNORDEREDLIST);
parseUnorderedList((AUnorderedListType) objectType, out);
} else
- throw new AsterixException(mismatchErrorMessage + objectType.getTypeTag());
+ throw new ParseException(mismatchErrorMessage + objectType.getTypeTag());
break;
}
@@ -306,7 +351,7 @@
objectType = getComplexType(objectType, ATypeTag.ORDEREDLIST);
parseOrderedList((AOrderedListType) objectType, out);
} else
- throw new AsterixException(mismatchErrorMessage + objectType.getTypeTag());
+ throw new ParseException(mismatchErrorMessage + objectType.getTypeTag());
break;
}
case AdmLexer.TOKEN_START_RECORD: {
@@ -314,14 +359,14 @@
objectType = getComplexType(objectType, ATypeTag.RECORD);
parseRecord((ARecordType) objectType, out, datasetRec);
} else
- throw new AsterixException(mismatchErrorMessage + objectType.getTypeTag());
+ throw new ParseException(mismatchErrorMessage + objectType.getTypeTag());
break;
}
case AdmLexer.TOKEN_EOF: {
break;
}
default: {
- throw new AsterixException("Unexpected ADM token kind: " + AdmLexer.tokenKindToString(token) + ".");
+ throw new ParseException("Unexpected ADM token kind: " + AdmLexer.tokenKindToString(token) + ".");
}
}
}
@@ -401,7 +446,7 @@
switch (token) {
case AdmLexer.TOKEN_END_RECORD: {
if (expectingRecordField) {
- throw new AsterixException("Found END_RECORD while expecting a record field.");
+ throw new ParseException("Found END_RECORD while expecting a record field.");
}
inRecord = false;
break;
@@ -418,7 +463,7 @@
admLexer.getLastTokenImage().length() - 1);
fieldId = recBuilder.getFieldId(fldName);
if (fieldId < 0 && !recType.isOpen()) {
- throw new AsterixException("This record is closed, you can not add extra fields !!");
+ throw new ParseException("This record is closed, you can not add extra fields !!");
} else if (fieldId < 0 && recType.isOpen()) {
aStringFieldName.setValue(admLexer.getLastTokenImage().substring(1,
admLexer.getLastTokenImage().length() - 1));
@@ -441,7 +486,7 @@
token = admLexer.next();
if (token != AdmLexer.TOKEN_COLON) {
- throw new AsterixException("Unexpected ADM token kind: " + AdmLexer.tokenKindToString(token)
+ throw new ParseException("Unexpected ADM token kind: " + AdmLexer.tokenKindToString(token)
+ " while expecting \":\".");
}
@@ -464,16 +509,16 @@
}
case AdmLexer.TOKEN_COMMA: {
if (first) {
- throw new AsterixException("Found COMMA before any record field.");
+ throw new ParseException("Found COMMA before any record field.");
}
if (expectingRecordField) {
- throw new AsterixException("Found COMMA while expecting a record field.");
+ throw new ParseException("Found COMMA while expecting a record field.");
}
expectingRecordField = true;
break;
}
default: {
- throw new AsterixException("Unexpected ADM token kind: " + AdmLexer.tokenKindToString(token)
+ throw new ParseException("Unexpected ADM token kind: " + AdmLexer.tokenKindToString(token)
+ " while parsing record fields.");
}
}
@@ -483,7 +528,7 @@
if (recType != null) {
nullableFieldId = checkNullConstraints(recType, nulls);
if (nullableFieldId != -1)
- throw new AsterixException("Field " + nullableFieldId + " can not be null");
+ throw new ParseException("Field " + nullableFieldId + " can not be null");
}
recBuilder.write(out, true);
returnRecordBuilder(recBuilder);
@@ -531,15 +576,15 @@
token = admLexer.next();
if (token == AdmLexer.TOKEN_END_ORDERED_LIST) {
if (expectingListItem) {
- throw new AsterixException("Found END_COLLECTION while expecting a list item.");
+ throw new ParseException("Found END_COLLECTION while expecting a list item.");
}
inList = false;
} else if (token == AdmLexer.TOKEN_COMMA) {
if (first) {
- throw new AsterixException("Found COMMA before any list item.");
+ throw new ParseException("Found COMMA before any list item.");
}
if (expectingListItem) {
- throw new AsterixException("Found COMMA while expecting a list item.");
+ throw new ParseException("Found COMMA while expecting a list item.");
}
expectingListItem = true;
} else {
@@ -576,19 +621,19 @@
if (token == AdmLexer.TOKEN_END_RECORD) {
if (admLexer.next() == AdmLexer.TOKEN_END_RECORD) {
if (expectingListItem) {
- throw new AsterixException("Found END_COLLECTION while expecting a list item.");
+ throw new ParseException("Found END_COLLECTION while expecting a list item.");
} else {
inList = false;
}
} else {
- throw new AsterixException("Found END_RECORD while expecting a list item.");
+ throw new ParseException("Found END_RECORD while expecting a list item.");
}
} else if (token == AdmLexer.TOKEN_COMMA) {
if (first) {
- throw new AsterixException("Found COMMA before any list item.");
+ throw new ParseException("Found COMMA before any list item.");
}
if (expectingListItem) {
- throw new AsterixException("Found COMMA while expecting a list item.");
+ throw new ParseException("Found COMMA while expecting a list item.");
}
expectingListItem = true;
} else {
@@ -657,7 +702,7 @@
IOException {
final ATypeTag targetTypeTag = getTargetTypeTag(typeTag, objectType);
if (targetTypeTag == null || !parseValue(admLexer.getLastTokenImage(), targetTypeTag, out)) {
- throw new AsterixException(mismatchErrorMessage + objectType.getTypeName() + mismatchErrorMessage2
+ throw new ParseException(mismatchErrorMessage + objectType.getTypeName() + mismatchErrorMessage2
+ typeTag);
}
}
@@ -672,7 +717,7 @@
}
if (targetTypeTag == null || !parseValue(admLexer.getLastTokenImage(), typeTag, dataOutput)) {
- throw new AsterixException(mismatchErrorMessage + objectType.getTypeName() + mismatchErrorMessage2
+ throw new ParseException(mismatchErrorMessage + objectType.getTypeName() + mismatchErrorMessage2
+ typeTag);
}
@@ -702,7 +747,7 @@
final String unquoted = admLexer.getLastTokenImage().substring(1,
admLexer.getLastTokenImage().length() - 1);
if (!parseValue(unquoted, typeTag, dataOutput)) {
- throw new AsterixException("Missing deserializer method for constructor: "
+ throw new ParseException("Missing deserializer method for constructor: "
+ AdmLexer.tokenKindToString(token) + ".");
}
token = admLexer.next();
@@ -721,7 +766,7 @@
}
}
}
- throw new AsterixException(mismatchErrorMessage + objectType.getTypeName() + ". Got " + typeTag + " instead.");
+ throw new ParseException(mismatchErrorMessage + objectType.getTypeName() + ". Got " + typeTag + " instead.");
}
private boolean parseValue(final String unquoted, ATypeTag typeTag, DataOutput out) throws AsterixException,
@@ -802,7 +847,7 @@
else if (bool.equals("false"))
booleanSerde.serialize(ABoolean.FALSE, out);
else
- throw new AsterixException(errorMessage);
+ throw new ParseException(errorMessage);
}
private void parseInt8(String int8, DataOutput out) throws AsterixException, HyracksDataException {
@@ -823,10 +868,10 @@
else if (int8.charAt(offset) == 'i' && int8.charAt(offset + 1) == '8' && offset + 2 == int8.length())
break;
else
- throw new AsterixException(errorMessage);
+ throw new ParseException(errorMessage);
}
if (value < 0)
- throw new AsterixException(errorMessage);
+ throw new ParseException(errorMessage);
if (value > 0 && !positive)
value *= -1;
aInt8.setValue(value);
@@ -852,10 +897,10 @@
&& offset + 3 == int16.length())
break;
else
- throw new AsterixException(errorMessage);
+ throw new ParseException(errorMessage);
}
if (value < 0)
- throw new AsterixException(errorMessage);
+ throw new ParseException(errorMessage);
if (value > 0 && !positive)
value *= -1;
aInt16.setValue(value);
@@ -881,10 +926,10 @@
&& offset + 3 == int32.length())
break;
else
- throw new AsterixException(errorMessage);
+ throw new ParseException(errorMessage);
}
if (value < 0)
- throw new AsterixException(errorMessage);
+ throw new ParseException(errorMessage);
if (value > 0 && !positive)
value *= -1;
@@ -911,10 +956,10 @@
&& offset + 3 == int64.length())
break;
else
- throw new AsterixException(errorMessage);
+ throw new ParseException(errorMessage);
}
if (value < 0)
- throw new AsterixException(errorMessage);
+ throw new ParseException(errorMessage);
if (value > 0 && !positive)
value *= -1;