package act.util;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* Licensed 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.
* #L%
*/
import act.Act;
import act.app.*;
import act.asm.AsmContext;
import act.cli.CliContext;
import act.handler.DelegateRequestHandler;
import act.handler.RequestHandler;
import act.handler.builtin.controller.ControllerAction;
import act.handler.builtin.controller.RequestHandlerProxy;
import act.handler.builtin.controller.impl.ReflectedHandlerInvoker;
import act.i18n.I18n;
import org.osgl.$;
import org.osgl.http.H;
import org.osgl.mvc.MvcConfig;
import org.osgl.util.E;
import org.osgl.util.S;
import java.lang.annotation.ElementType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public interface ActError {
Throwable getCauseOrThis();
Throwable getCause();
SourceInfo sourceInfo();
List stackTrace();
String getMessage();
String getLocalizedMessage();
boolean isErrorSpot(String traceLine, String nextTraceLine);
class Util {
public static String errorMessage(H.Status status) {
return errorMessage(status, null);
}
public static String errorMessage(H.Status status, String message, Object ... args) {
if (S.notBlank(message)) {
return S.fmt(message, args);
}
if (Act.isProd()) {
return MvcConfig.errorMessage(status);
}
ActionContext ctx = ActionContext.current();
if (null == ctx) {
return MvcConfig.errorMessage(status);
}
RequestHandler handler = ctx.handler();
if (null == handler) {
return MvcConfig.errorMessage(status);
}
if (H.Status.NOT_FOUND == status) {
return I18n.i18n(Act.class, "e404.null_value_returned", handler);
}
return I18n.i18n(Act.class, "error.on_invoking", MvcConfig.errorMessage(status), handler);
}
public static SourceInfo loadSourceInfo(Throwable cause, Class errorClass) {
return _loadSourceInfo(cause.getStackTrace(), errorClass);
}
private static SourceInfo _loadSourceInfo(StackTraceElement[] sa, Class errorClass) {
int len = sa.length;
StackTraceElement[] caller = new StackTraceElement[len - 1];
System.arraycopy(sa, 1, caller, 0, len - 1);
ActContext ctx = ActContext.Base.currentContext();
if (null == ctx) {
return loadSourceInfo(caller, errorClass);
}
if (ctx instanceof ActionContext) {
ActionContext actionContext = $.cast(ctx);
RequestHandler handler = actionContext.handler();
String actionName = handler.toString();
for (StackTraceElement element : caller) {
if (actionName.contains(element.getMethodName()) && actionName.contains(element.getClassName())) {
return loadSourceInfo(caller, errorClass);
}
}
if (handler instanceof DelegateRequestHandler) {
handler = ((DelegateRequestHandler) handler).realHandler();
}
if (handler instanceof RequestHandlerProxy) {
RequestHandlerProxy proxy = $.cast(handler);
ControllerAction action = proxy.actionHandler();
ReflectedHandlerInvoker invoker = $.cast(action.invoker());
return loadSourceInfo(invoker.method());
}
return loadSourceInfo(caller, errorClass);
} else if (ctx instanceof CliContext) {
CliContext cliContext = $.cast(ctx);
Method method = cliContext.handlerMethod();
for (StackTraceElement element : caller) {
if (method.getName().equals(element.getMethodName()) && method.getDeclaringClass().getName().equals(element.getClassName())) {
return loadSourceInfo(caller, errorClass);
}
}
return loadSourceInfo(method);
}
return loadSourceInfo(caller, errorClass);
}
public static SourceInfo loadSourceInfo(Class errorClass) {
return _loadSourceInfo(new RuntimeException().getStackTrace(), errorClass);
}
public static List stackTraceOf(ActError error) {
Throwable cause = error.getCause();
ActError root = error;
if (null == cause) {
cause = (Throwable) error;
root = null;
}
return stackTraceOf(cause, root);
}
public static List stackTraceOf(Throwable t, ActError root) {
List l = new ArrayList<>();
while (null != t) {
StackTraceElement[] a = t.getStackTrace();
for (StackTraceElement e : a) {
String line = S.concat("at ", e.toString());
if (line.contains("org.osgl.util.E.")) {
// skip E util class
continue;
}
if (l.contains(line)) {
l.add(line);
// caused by stack trace stop at here
break;
}
l.add(line);
}
t = t.getCause();
if (t == root) {
break;
}
if (null != t) {
l.add("Caused by " + t.toString());
}
}
return l;
}
public static SourceInfo loadSourceInfo(StackTraceElement[] stackTraceElements, Class errClz) {
E.illegalStateIf(Act.isProd());
DevModeClassLoader cl = (DevModeClassLoader) App.instance().classLoader();
String errClzName = errClz.getName();
for (StackTraceElement stackTraceElement : stackTraceElements) {
int line = stackTraceElement.getLineNumber();
if (line <= 0) {
continue;
}
String className = stackTraceElement.getClassName();
if (S.eq(className, errClzName)) {
continue;
}
Source source = cl.source(className);
if (null == source) {
continue;
}
return new SourceInfoImpl(source, line);
}
return null;
}
public static SourceInfo loadSourceInfo(Method method) {
return loadSourceInfo(method.getDeclaringClass().getName(), method.getName(), true, null);
}
public static SourceInfo loadSourceInfo(AsmContext asmContext) {
return loadSourceInfo(asmContext.className(), asmContext.name(), ElementType.METHOD == asmContext.type(), asmContext.lineNo());
}
private static SourceInfo loadSourceInfo(String className, String elementName, boolean isMethod, Integer lineNo) {
E.illegalStateIf(Act.isProd());
DevModeClassLoader cl = (DevModeClassLoader) App.instance().classLoader();
Source source = cl.source(className);
if (null == source) {
return null;
}
List lines = source.lines();
Line candidate = null;
String pattern = isMethod ?
S.concat("^\\s*.*", elementName, "\\s*\\(.*") :
S.concat("^\\s*.*", elementName, "[^\\(\\{]*");
if (null != lineNo) {
return new SourceInfoImpl(source, lineNo);
}
for (int i = 0; i < lines.size(); ++i) {
String line = lines.get(i);
if (line.matches(pattern)) {
candidate = new Line(line, i + 1);
if (candidate.forSure) {
return new SourceInfoImpl(source, candidate.no);
}
}
}
if (null != candidate) {
return new SourceInfoImpl(source, candidate.no);
}
return new SourceInfoImpl(source, 1);
}
private static class Line {
String line;
int no;
boolean forSure;
Line(String line, int no) {
this.line = line;
this.no = no;
forSure = line.contains("public ");
}
}
}
}