Merged API-STRIKES-BACK (5.2.0) to HEAD (5.2)

125761 gjames: RA-652: new stack trace message and logId property


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@127564 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jamal Kaabi-Mofrad
2016-06-02 21:38:35 +00:00
parent b76f006c7f
commit f9eb72c7ef
5 changed files with 34 additions and 18 deletions

View File

@@ -13,4 +13,5 @@ framework.exception.UnsupportedResourceOperation=The operation is unsupported
framework.exception.DeletedResource=In this version of the REST API resource {0} has been deleted framework.exception.DeletedResource=In this version of the REST API resource {0} has been deleted
framework.exception.RequestEntityTooLarge=Request entity too large framework.exception.RequestEntityTooLarge=Request entity too large
framework.exception.InsufficientStorage=Content storage quota exceeded framework.exception.InsufficientStorage=Content storage quota exceeded
framework.no.stacktrace=For security reasons the stack trace is no longer displayed, but the property is kept for previous versions.

View File

@@ -9,6 +9,7 @@ import javax.servlet.http.HttpServletResponse;
*/ */
public class DefaultExceptionResolver implements ExceptionResolver<Exception> public class DefaultExceptionResolver implements ExceptionResolver<Exception>
{ {
public static final String STACK_MESSAGE_ID = "framework.no.stacktrace";
public static final String ERROR_URL = "https://api-explorer.alfresco.com"; public static final String ERROR_URL = "https://api-explorer.alfresco.com";
public static final String DEFAULT_MESSAGE_ID = "framework.exception.ApiDefault"; public static final String DEFAULT_MESSAGE_ID = "framework.exception.ApiDefault";

View File

@@ -11,13 +11,14 @@ import java.util.Map;
*/ */
public class ErrorResponse public class ErrorResponse
{ {
final private String errorKey; private final String errorKey;
final private int statusCode; private final int statusCode;
final private String briefSummary; private final String briefSummary;
final private String stackTrace; private final String stackTrace;
final private Map<String,Object> additionalState; private final Map<String,Object> additionalState;
final private String descriptionURL; private final String descriptionURL;
private final String logId;
public ErrorResponse(String errorKey, int statusCode, String briefSummary, public ErrorResponse(String errorKey, int statusCode, String briefSummary,
StackTraceElement[] stackTrace, Map<String,Object> additionalState) StackTraceElement[] stackTrace, Map<String,Object> additionalState)
{ {
@@ -28,16 +29,18 @@ public class ErrorResponse
this.stackTrace = Arrays.toString(stackTrace); this.stackTrace = Arrays.toString(stackTrace);
this.additionalState = additionalState==null?null:Collections.unmodifiableMap(additionalState); this.additionalState = additionalState==null?null:Collections.unmodifiableMap(additionalState);
this.descriptionURL = null; this.descriptionURL = null;
this.logId = null;
} }
public ErrorResponse(String errorKey, int statusCode, String briefSummary, public ErrorResponse(String errorKey, int statusCode, String briefSummary,
String stackTrace, Map<String,Object> additionalState, String descriptionURL) String stackMessage, String logId, Map<String,Object> additionalState, String descriptionURL)
{ {
super(); super();
this.errorKey = errorKey; this.errorKey = errorKey;
this.statusCode = statusCode; this.statusCode = statusCode;
this.briefSummary = briefSummary; this.briefSummary = briefSummary;
this.stackTrace = stackTrace; this.stackTrace = stackMessage;
this.logId = logId;
this.additionalState = additionalState==null?null:Collections.unmodifiableMap(additionalState); this.additionalState = additionalState==null?null:Collections.unmodifiableMap(additionalState);
this.descriptionURL = descriptionURL; this.descriptionURL = descriptionURL;
} }
@@ -72,6 +75,10 @@ public class ErrorResponse
return this.additionalState; return this.additionalState;
} }
public String getLogId() {
return logId;
}
@Override @Override
public String toString() public String toString()
{ {
@@ -79,6 +86,7 @@ public class ErrorResponse
builder.append("ErrorResponse [errorKey=").append(this.errorKey).append(", statusCode=") builder.append("ErrorResponse [errorKey=").append(this.errorKey).append(", statusCode=")
.append(this.statusCode).append(", briefSummary=").append(this.briefSummary) .append(this.statusCode).append(", briefSummary=").append(this.briefSummary)
.append(", descriptionURL=").append(this.descriptionURL) .append(", descriptionURL=").append(this.descriptionURL)
.append(", logId=").append(this.logId)
.append(", stackTrace=").append(this.stackTrace).append(", additionalState=") .append(", stackTrace=").append(this.stackTrace).append(", additionalState=")
.append(this.additionalState).append("]"); .append(this.additionalState).append("]");
return builder.toString(); return builder.toString();

View File

@@ -43,6 +43,7 @@ import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectMapper;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.extensions.webscripts.*; import org.springframework.extensions.webscripts.*;
import org.springframework.extensions.webscripts.Description.RequiredCache; import org.springframework.extensions.webscripts.Description.RequiredCache;
import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse; import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse;
@@ -202,18 +203,21 @@ public abstract class ApiWebScript extends AbstractWebScript
*/ */
public void renderErrorResponse(ErrorResponse errorResponse, final WebScriptResponse res) throws IOException { public void renderErrorResponse(ErrorResponse errorResponse, final WebScriptResponse res) throws IOException {
String logKey = " "; String logId = "";
if (Status.STATUS_INTERNAL_SERVER_ERROR == errorResponse.getStatusCode() || logger.isDebugEnabled()) if (Status.STATUS_INTERNAL_SERVER_ERROR == errorResponse.getStatusCode() || logger.isDebugEnabled())
{ {
logKey = GUID.generate(); logId = GUID.generate();
logger.error(logKey+" : "+errorResponse.getStackTrace()); logger.error(logId+" : "+errorResponse.getStackTrace());
} }
String stackMessage = I18NUtil.getMessage(DefaultExceptionResolver.STACK_MESSAGE_ID);
final ErrorResponse errorToWrite = new ErrorResponse(errorResponse.getErrorKey(), final ErrorResponse errorToWrite = new ErrorResponse(errorResponse.getErrorKey(),
errorResponse.getStatusCode(), errorResponse.getStatusCode(),
errorResponse.getBriefSummary(), errorResponse.getBriefSummary(),
logKey, stackMessage,
logId,
errorResponse.getAdditionalState(), errorResponse.getAdditionalState(),
DefaultExceptionResolver.ERROR_URL); DefaultExceptionResolver.ERROR_URL);

View File

@@ -287,20 +287,22 @@ public class ExecutionTests extends AbstractContextTest
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
executor.renderErrorResponse(defaultError, mockResponse(out)); executor.renderErrorResponse(defaultError, mockResponse(out));
String errorMessage = out.toString(); String errorMessage = out.toString();
//System.out.println(errorMessage); // System.out.println(errorMessage);
assertTrue(errorMessage.contains("\"errorKey\":\"framework.exception.ApiDefault\"")); assertTrue(errorMessage.contains("\"errorKey\":\"framework.exception.ApiDefault\""));
assertTrue(errorMessage.contains("\"statusCode\":500")); assertTrue(errorMessage.contains("\"statusCode\":500"));
assertTrue(errorMessage.contains("\"stackTrace\":\"")); assertTrue(errorMessage.contains("\"logId\":\""));
assertTrue(errorMessage.contains("\"stackTrace\":\"For security reasons the stack trace is no longer displayed"));
assertTrue(errorMessage.contains("\"descriptionURL\":\""+DefaultExceptionResolver.ERROR_URL+"\"")); assertTrue(errorMessage.contains("\"descriptionURL\":\""+DefaultExceptionResolver.ERROR_URL+"\""));
ErrorResponse anError = simpleMappingExceptionResolver.resolveException(new ApiException("nothing")); ErrorResponse anError = simpleMappingExceptionResolver.resolveException(new ApiException("nothing"));
out = new ByteArrayOutputStream(); out = new ByteArrayOutputStream();
executor.renderErrorResponse(anError, mockResponse(out)); executor.renderErrorResponse(anError, mockResponse(out));
errorMessage = out.toString(); errorMessage = out.toString();
System.out.println(errorMessage); // System.out.println(errorMessage);
assertTrue(errorMessage.contains("\"errorKey\":\"nothing\"")); assertTrue(errorMessage.contains("\"errorKey\":\"nothing\""));
assertTrue(errorMessage.contains("\"statusCode\":500")); assertTrue(errorMessage.contains("\"statusCode\":500"));
assertTrue(errorMessage.contains("\"stackTrace\":\"")); assertTrue(errorMessage.contains("\"stackTrace\":\"For security reasons the stack trace is no longer displayed"));
assertTrue(errorMessage.contains("\"logId\":\""));
anError = simpleMappingExceptionResolver.resolveException(new EntityNotFoundException("2")); anError = simpleMappingExceptionResolver.resolveException(new EntityNotFoundException("2"));
out = new ByteArrayOutputStream(); out = new ByteArrayOutputStream();
@@ -309,7 +311,7 @@ public class ExecutionTests extends AbstractContextTest
System.out.println(errorMessage); System.out.println(errorMessage);
assertTrue(errorMessage.contains("\"errorKey\":\"framework.exception.EntityNotFound\"")); assertTrue(errorMessage.contains("\"errorKey\":\"framework.exception.EntityNotFound\""));
assertTrue(errorMessage.contains("\"statusCode\":404")); assertTrue(errorMessage.contains("\"statusCode\":404"));
assertTrue("Only 500 errors should have a stracktrace", errorMessage.contains("\"stackTrace\":\" \"")); assertFalse("Only 500 errors should have a logId", errorMessage.contains("\"logId\":\" \""));
} }
private WebScriptResponse mockResponse() throws IOException private WebScriptResponse mockResponse() throws IOException