
org.apache.asterix.result.ResultUtils Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.asterix.result;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.http.ParseException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.apache.asterix.api.common.SessionConfig;
import org.apache.asterix.api.common.SessionConfig.OutputFormat;
import org.apache.asterix.api.http.servlet.APIServlet;
import org.apache.asterix.om.types.ARecordType;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.api.comm.IFrame;
import org.apache.hyracks.api.comm.IFrameTupleAccessor;
import org.apache.hyracks.api.comm.VSizeFrame;
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.control.nc.resources.memory.FrameManager;
import org.apache.hyracks.dataflow.common.comm.util.ByteBufferInputStream;
public class ResultUtils {
private static final Charset UTF_8 = Charset.forName("UTF-8");
static Map HTML_ENTITIES = new HashMap();
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 displayCSVHeader(ARecordType recordType, SessionConfig conf) {
// If HTML-ifying, we have to output this here before the header -
// pretty ugly
if (conf.is(SessionConfig.FORMAT_HTML)) {
conf.out().println("Results:
");
conf.out().println("");
}
String[] fieldNames = recordType.getFieldNames();
boolean notfirst = false;
for (String name : fieldNames) {
if (notfirst) {
conf.out().print(',');
}
notfirst = true;
conf.out().print('"');
conf.out().print(name.replace("\"", "\"\""));
conf.out().print('"');
}
conf.out().print("\r\n");
}
public static FrameManager resultDisplayFrameMgr = new FrameManager(ResultReader.FRAME_SIZE);
public static void displayResults(ResultReader resultReader, SessionConfig conf)
throws HyracksDataException {
IFrameTupleAccessor fta = resultReader.getFrameTupleAccessor();
IFrame frame = new VSizeFrame(resultDisplayFrameMgr);
int bytesRead = resultReader.read(frame);
ByteBufferInputStream bbis = new ByteBufferInputStream();
// Whether we need to separate top-level ADM instances with commas
boolean need_commas = true;
// Whether this is the first instance being output
boolean notfirst = false;
// If we're outputting CSV with a header, the HTML header was already
// output by displayCSVHeader(), so skip it here
if (conf.is(SessionConfig.FORMAT_HTML) &&
! (conf.fmt() == OutputFormat.CSV && conf.is(SessionConfig.FORMAT_CSV_HEADER))) {
conf.out().println("Results:
");
conf.out().println("");
}
switch (conf.fmt()) {
case CSV:
need_commas = false;
break;
case JSON:
case ADM:
// Conveniently, JSON and ADM have the same syntax for an
// "ordered list", and our representation of the result of a
// statement is an ordered list of instances.
conf.out().print("[ ");
break;
}
if (bytesRead > 0) {
do {
try {
fta.reset(frame.getBuffer());
int last = fta.getTupleCount();
String result;
for (int tIndex = 0; tIndex < last; tIndex++) {
int start = fta.getTupleStartOffset(tIndex);
int length = fta.getTupleEndOffset(tIndex) - start;
bbis.setByteBuffer(frame.getBuffer(), start);
byte[] recordBytes = new byte[length];
int numread = bbis.read(recordBytes, 0, length);
if (conf.fmt() == OutputFormat.CSV) {
if ( (numread > 0) && (recordBytes[numread-1] == '\n') ) {
numread--;
}
}
result = new String(recordBytes, 0, numread, UTF_8);
if (need_commas && notfirst) {
conf.out().print(", ");
}
notfirst = true;
conf.out().print(result);
if (conf.fmt() == OutputFormat.CSV) {
conf.out().print("\r\n");
}
}
frame.getBuffer().clear();
} finally {
try {
bbis.close();
} catch (IOException e) {
throw new HyracksDataException(e);
}
}
} while (resultReader.read(frame) > 0);
}
conf.out().flush();
switch (conf.fmt()) {
case JSON:
case ADM:
conf.out().println(" ]");
break;
case CSV:
// Nothing to do
break;
}
if (conf.is(SessionConfig.FORMAT_HTML)) {
conf.out().println("
");
}
}
public static JSONObject getErrorResponse(int errorCode, String errorMessage, String errorSummary,
String errorStackTrace) {
JSONObject errorResp = new JSONObject();
JSONArray errorArray = new JSONArray();
errorArray.put(errorCode);
errorArray.put(errorMessage);
try {
errorResp.put("error-code", errorArray);
if (!errorSummary.equals(""))
errorResp.put("summary", errorSummary);
if (!errorStackTrace.equals(""))
errorResp.put("stacktrace", errorStackTrace);
} catch (JSONException e) {
// TODO(madhusudancs): Figure out what to do when JSONException occurs while building the results.
}
return errorResp;
}
public static void webUIErrorHandler(PrintWriter out, Exception e) {
String errorTemplate = readTemplateFile("/webui/errortemplate.html", "%s\n%s\n%s");
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", "%s\n
");
String errorOutput = String.format(errorTemplate, buildParseExceptionMessage(e, query));
out.println(errorOutput);
}
public static void apiErrorHandler(PrintWriter out, Exception e) {
int errorCode = 99;
if (e instanceof ParseException) {
errorCode = 2;
} else if (e instanceof AlgebricksException) {
errorCode = 3;
} else if (e instanceof HyracksDataException) {
errorCode = 4;
}
JSONObject errorResp = ResultUtils.getErrorResponse(errorCode, extractErrorMessage(e), extractErrorSummary(e),
extractFullStackTrace(e));
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) {
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(message);
if (m.find(pos)) {
int lineNo = Integer.parseInt(message.substring(m.start(), m.end()));
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;
}
/**
* Extract the message in the root cause of the stack trace:
*
* @param e
* @return error message string.
*/
private static String extractErrorMessage(Throwable e) {
Throwable cause = getRootCause(e);
String fullyQualifiedExceptionClassName = cause.getClass().getName();
String[] hierarchySplits = fullyQualifiedExceptionClassName.split("\\.");
//try returning the class without package qualification
String exceptionClassName = hierarchySplits[hierarchySplits.length - 1];
String localizedMessage = cause.getLocalizedMessage();
if(localizedMessage == null){
localizedMessage = "Internal error. Please check instance logs for further details.";
}
return localizedMessage + " [" + exceptionClassName + "]";
}
/**
* Extract the meaningful part of a stack trace:
* a. the causes in the stack trace hierarchy
* b. the top exception for each cause
*
* @param e
* @return the contacted message containing a and b.
*/
private static String extractErrorSummary(Throwable e) {
StringBuilder errorMessageBuilder = new StringBuilder();
Throwable cause = e;
errorMessageBuilder.append(cause.getLocalizedMessage());
while (cause != null) {
StackTraceElement[] stackTraceElements = cause.getStackTrace();
errorMessageBuilder.append(stackTraceElements.length > 0 ? "\n caused by: " + stackTraceElements[0] : "");
cause = cause.getCause();
}
return errorMessageBuilder.toString();
}
/**
* Extract the full stack trace:
*
* @param e
* @return the string containing the full stack trace of the error.
*/
private static String extractFullStackTrace(Throwable e) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
return stringWriter.toString();
}
/**
* Read the template file which is stored as a resource and return its content. If the file does not exist or is
* not readable return the default template string.
*
* @param path
* The path to the resource template file
* @param defaultTemplate
* The default template string if the template file does not exist or is not readable
* @return The template string to be used to render the output.
*/
private static String readTemplateFile(String path, String defaultTemplate) {
String errorTemplate = defaultTemplate;
try {
String resourcePath = "/webui/errortemplate_message.html";
InputStream is = APIServlet.class.getResourceAsStream(resourcePath);
InputStreamReader isr = new InputStreamReader(is);
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(isr);
String line = br.readLine();
while (line != null) {
sb.append(line);
line = br.readLine();
}
errorTemplate = sb.toString();
} catch (IOException ioe) {
// If there is an IOException reading the error template html file, default value of error template is used.
}
return errorTemplate;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy