package org.zstack.core.errorcode;
import org.zstack.core.errorcode.schema.Error;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.errorcode.ErrorCodeList;
import org.zstack.header.errorcode.SysErrors;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.utils.DebugUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import org.zstack.utils.path.PathUtil;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*/
public class ErrorFacadeImpl implements ErrorFacade {
private static final CLogger logger = Utils.getLogger(ErrorFacadeImpl.class);
private Map<String, ErrorCodeInfo> codes = new HashMap<>();
private boolean dumpOnError = Boolean.valueOf(System.getProperty("ErrorFacade.dumpOnError"));
@Override
public ErrorCode instantiateErrorCode(Enum code, ErrorCode cause) {
return instantiateErrorCode(code.toString(), cause);
}
@Override
public ErrorCode instantiateErrorCode(String code, ErrorCode cause) {
return instantiateErrorCode(code, cause.getDetails(), cause);
}
@Override
public ErrorCode instantiateErrorCode(Enum code, String details, ErrorCode cause) {
return instantiateErrorCode(code.toString(), details, cause);
}
@Override
public ErrorCode instantiateErrorCode(String code, String details, ErrorCode cause) {
ErrorCode err = instantiateErrorCode(code, details);
err.setCause(cause);
return err;
}
@Override
public ErrorCode instantiateErrorCode(Enum code, String details) {
return instantiateErrorCode(code.toString(), details);
}
private ErrorCode doInstantiateErrorCode(String code, String details, List<ErrorCode> causes) {
ErrorCodeInfo info = codes.get(code);
if (info == null) {
throw new CloudRuntimeException(String.format("cannot find error code[%s]", code));
}
if (details != null && details.length() > 4096) {
details = details.substring(0, Math.min(details.length(), 4096));
}
ErrorCodeList err = (ErrorCodeList) info.code.copy();
err.setDetails(details);
err.setCauses(causes);
if (dumpOnError) {
DebugUtils.dumpStackTrace(String.format("An error code%s is instantiated," +
" for tracing the place error happened, dump stack as below", err));
}
return err;
}
@Override
public ErrorCode instantiateErrorCode(String code, String details) {
return doInstantiateErrorCode(code, details, null);
}
@Override
public ErrorCode stringToInternalError(String details) {
return instantiateErrorCode(SysErrors.INTERNAL.toString(), details);
}
@Override
public ErrorCode throwableToInternalError(Throwable t) {
return instantiateErrorCode(SysErrors.INTERNAL.toString(), t.getMessage());
}
@Override
public ErrorCode stringToTimeoutError(String details) {
return instantiateErrorCode(SysErrors.TIMEOUT.toString(), details);
}
@Override
public ErrorCode throwableToTimeoutError(Throwable t) {
return instantiateErrorCode(SysErrors.TIMEOUT.toString(), t.getMessage());
}
@Override
public ErrorCode stringToOperationError(String details) {
return instantiateErrorCode(SysErrors.OPERATION_ERROR, details);
}
@Override
public ErrorCode stringToOperationError(String details, ErrorCode cause) {
return instantiateErrorCode(SysErrors.OPERATION_ERROR, details, cause);
}
public ErrorCode stringToExternalError(String details, ErrorCode cause) {
return instantiateErrorCode(SysErrors.EXTERNAL_ERROR, details, cause);
}
@Override
public ErrorCodeList instantiateErrorCode(Enum code, List<ErrorCode> causes) {
return instantiateErrorCode(code.toString(), causes);
}
@Override
public ErrorCodeList instantiateErrorCode(String code, List<ErrorCode> causes) {
return instantiateErrorCode(code, null, causes);
}
@Override
public ErrorCodeList instantiateErrorCode(Enum code, String details, List<ErrorCode> causes) {
return instantiateErrorCode(code.toString(), details, causes);
}
@Override
public ErrorCodeList instantiateErrorCode(String code, String details, List<ErrorCode> causes) {
return (ErrorCodeList) doInstantiateErrorCode(code, details, causes);
}
@Override
public ErrorCodeList stringToOperationError(String details, List<ErrorCode> causes) {
return instantiateErrorCode(SysErrors.OPERATION_ERROR, details, causes);
}
@Override
public ErrorCode throwableToOperationError(Throwable t) {
return instantiateErrorCode(SysErrors.OPERATION_ERROR, t.getMessage());
}
@Override
public ErrorCode stringToInvalidArgumentError(String details) {
return instantiateErrorCode(SysErrors.INVALID_ARGUMENT_ERROR, details);
}
@Override
public ErrorCode throwableToInvalidArgumentError(Throwable t) {
return instantiateErrorCode(SysErrors.INVALID_ARGUMENT_ERROR, t.getMessage());
}
private class ErrorCodeInfo {
ErrorCode code;
String path;
}
private void createErrorCode(org.zstack.core.errorcode.schema.Error error, String path) {
for (Error.Code code : error.getCode()) {
String codeId = String.format("%s.%s", error.getPrefix(), code.getId());
ErrorCodeInfo info = codes.get(codeId);
if (info != null) {
throw new CloudRuntimeException(String.format("duplicate definition of ErrorCode[%s]," +
" file[%s] and file[%s] both define it", codeId, info.path, path));
}
ErrorCodeList errorCode = new ErrorCodeList();
errorCode.setCode(codeId);
errorCode.setDescription(code.getDescription());
errorCode.setElaboration(code.getElaboration());
info = new ErrorCodeInfo();
info.code = errorCode;
info.path = path;
codes.put(codeId, info);
}
}
void init() {
try {
JAXBContext context = JAXBContext.newInstance("org.zstack.core.errorcode.schema");
List<String> paths = PathUtil.scanFolderOnClassPath("errorCodes");
for (String p : paths) {
if (!p.endsWith(".xml")) {
logger.warn(String.format("ignore %s which is not ending with .xml", p));
continue;
}
File cfg = new File(p);
Unmarshaller unmarshaller = context.createUnmarshaller();
org.zstack.core.errorcode.schema.Error error =
(org.zstack.core.errorcode.schema.Error) unmarshaller.unmarshal(cfg);
createErrorCode(error, p);
}
} catch (Exception e) {
throw new CloudRuntimeException(e);
}
}
}