You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by wu...@apache.org on 2022/11/26 08:08:45 UTC

[ambari] branch trunk updated: AMBARI-25552: Improve stack-trace disablement on HTTP 500 error response from the server (#3573)

This is an automated email from the ASF dual-hosted git repository.

wuzhiguo pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new fb752e20e3 AMBARI-25552: Improve stack-trace disablement on HTTP 500 error response from the server (#3573)
fb752e20e3 is described below

commit fb752e20e3f295d19237b0eef342b3b4df0e0a85
Author: Yu Hou <52...@qq.com>
AuthorDate: Sat Nov 26 16:08:37 2022 +0800

    AMBARI-25552: Improve stack-trace disablement on HTTP 500 error response from the server (#3573)
---
 .../ambari/server/api/AmbariErrorHandler.java      |  35 +++++
 .../server/api/AmbariViewErrorHandlerProxy.java    | 118 ++++++++++++++++
 .../server/controller/AmbariHandlerList.java       |  43 ++++--
 .../ambari/server/controller/AmbariServer.java     |   6 +-
 ...rHandlerTest.java => AmbariErrorHandlerIT.java} |  15 +-
 .../ambari/server/api/AmbariErrorHandlerTest.java  | 151 +++++++++++++--------
 .../api/AmbariViewErrorHandlerProxyTest.java       | 117 ++++++++++++++++
 .../server/controller/AmbariHandlerListTest.java   |   2 +-
 8 files changed, 410 insertions(+), 77 deletions(-)

diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariErrorHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariErrorHandler.java
index 9f69c60c73..b1bd658ea6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariErrorHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariErrorHandler.java
@@ -19,15 +19,20 @@
 package org.apache.ambari.server.api;
 
 import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.UUID;
 
+import javax.servlet.RequestDispatcher;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.ambari.server.security.authentication.jwt.JwtAuthenticationProperties;
 import org.apache.ambari.server.security.authentication.jwt.JwtAuthenticationPropertiesProvider;
 import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.server.HttpChannel;
@@ -73,6 +78,23 @@ public class AmbariErrorHandler extends ErrorHandler {
     }
     errorMap.put("message", message);
 
+
+    Throwable th = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
+    if (th != null) {
+      if (code == org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR) {
+        UUID requestId = UUID.randomUUID();
+        message = "Internal server error, please refer the exception by " + requestId + " in the server log file";
+        errorMap.put("message", message);
+        LOG.error(message + ", requestURI: " + request.getRequestURI(), th);
+      }
+
+      if (this.isShowStacks()) {
+        StringWriter writer = new StringWriter();
+        writeErrorPageStacks(request, writer);
+        errorMap.put("reason:", writer.toString());
+      }
+    }
+
     if ((code == HttpServletResponse.SC_FORBIDDEN) || (code == HttpServletResponse.SC_UNAUTHORIZED)) {
       //if SSO is configured we should provide info about it in case of access error
       JwtAuthenticationProperties jwtProperties = jwtAuthenticationPropertiesProvider.get();
@@ -92,4 +114,17 @@ public class AmbariErrorHandler extends ErrorHandler {
 
     gson.toJson(errorMap, response.getWriter());
   }
+
+  @Override
+  protected void writeErrorPageStacks(HttpServletRequest request, Writer writer)
+    throws IOException {
+    Throwable th = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
+
+    while (th != null) {
+      writer.write("Caused by:\n");
+      write(writer, ExceptionUtils.getStackTrace(th));
+      writer.write("\n");
+      th = th.getCause();
+    }
+  }
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariViewErrorHandlerProxy.java b/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariViewErrorHandlerProxy.java
new file mode 100644
index 0000000000..e4d95d9106
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/AmbariViewErrorHandlerProxy.java
@@ -0,0 +1,118 @@
+/*
+ * 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.ambari.server.api;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpStatus;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javassist.util.proxy.MethodHandler;
+
+/**
+ * Wraps the given ErrorHandler to log the error stacks
+ */
+public class AmbariViewErrorHandlerProxy extends ErrorHandler implements MethodHandler {
+
+  private final static Logger LOGGER = LoggerFactory.getLogger(AmbariViewErrorHandlerProxy.class);
+
+  private final ErrorHandler webAppErrorHandler;
+  private final AmbariErrorHandler ambariErrorHandler;
+
+  public AmbariViewErrorHandlerProxy(ErrorHandler webAppErrorHandler, AmbariErrorHandler ambariErrorHandler) {
+    this.webAppErrorHandler = webAppErrorHandler;
+    this.ambariErrorHandler = ambariErrorHandler;
+  }
+
+
+  @Override
+  public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+    if (isInternalError(request, response)) {
+      //invoke the ambari error handler
+      ambariErrorHandler.handle(target, baseRequest, request, response);
+    } else {
+      //invoke the original errorhandler
+      webAppErrorHandler.handle(target, baseRequest, request, response);
+    }
+  }
+
+  @Override
+  public void doError(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+    if (isInternalError(request, response)) {
+      //invoke the ambari error handler
+      ambariErrorHandler.handle(target, baseRequest, request, response);
+    } else {
+      //invoke the original errorhandler
+      webAppErrorHandler.doError(target, baseRequest, request, response);
+    }
+  }
+
+  private boolean isInternalError(HttpServletRequest request, HttpServletResponse response) {
+    Throwable th = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
+    return null != th && response.getStatus() == HttpStatus.SC_INTERNAL_SERVER_ERROR;
+  }
+
+  @Override
+  public void setShowStacks(boolean showStacks) {
+    ambariErrorHandler.setShowStacks(showStacks);
+    webAppErrorHandler.setShowStacks(showStacks);
+  }
+
+  @Override
+  public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
+    LOGGER.debug("invoked method: " + thisMethod.getName());
+    Method m = findDeclaredMethod(this.getClass(), thisMethod);
+    if (m != null) {
+      return m.invoke(this, args);
+    }
+    m = findMethod(webAppErrorHandler.getClass(), thisMethod);
+    if (m != null) {
+      return m.invoke(webAppErrorHandler, args);
+    }
+    return null;
+  }
+
+  private Method findDeclaredMethod(Class<?> clazz, Method method) {
+    try {
+      return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
+    } catch (NoSuchMethodException e) {
+      return null;
+    }
+  }
+
+  private Method findMethod(Class<?> clazz, Method method) {
+    try {
+      return clazz.getMethod(method.getName(), method.getParameterTypes());
+    } catch (NoSuchMethodException e) {
+      return null;
+    }
+  }
+
+
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariHandlerList.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariHandlerList.java
index 663592e2a9..f8a99f6da4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariHandlerList.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariHandlerList.java
@@ -32,7 +32,9 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.ambari.server.api.AmbariErrorHandler;
 import org.apache.ambari.server.api.AmbariPersistFilter;
+import org.apache.ambari.server.api.AmbariViewErrorHandlerProxy;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.orm.entities.ViewEntity;
 import org.apache.ambari.server.orm.entities.ViewInstanceEntity;
@@ -44,6 +46,7 @@ import org.apache.ambari.view.SystemException;
 import org.apache.ambari.view.ViewContext;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.ErrorHandler;
 import org.eclipse.jetty.server.handler.HandlerCollection;
 import org.eclipse.jetty.server.session.SessionCache;
 import org.eclipse.jetty.server.session.SessionHandler;
@@ -53,6 +56,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.web.filter.DelegatingFilterProxy;
 
+import javassist.util.proxy.ProxyFactory;
+
 /**
  * An Ambari specific extension of the FailsafeHandlerList that allows for the addition
  * of view instances as handlers.
@@ -108,6 +113,10 @@ public class AmbariHandlerList extends HandlerCollection implements ViewInstance
   @Inject
   Configuration configuration;
 
+  @Inject
+  AmbariErrorHandler ambariErrorHandler;
+
+
   /**
    * Mapping of view instance entities to handlers.
    */
@@ -173,7 +182,7 @@ public class AmbariHandlerList extends HandlerCollection implements ViewInstance
     viewHandlerMap.put(viewInstanceDefinition, handler);
     super.addHandler(handler);
     // if this is running then start the handler being added...
-    if(!isStopped() && !isStopping()) {
+    if (!isStopped() && !isStopping()) {
       try {
         handler.start();
       } catch (Exception e) {
@@ -233,17 +242,15 @@ public class AmbariHandlerList extends HandlerCollection implements ViewInstance
   /**
    * Get a Handler for the given view instance.
    *
-   * @param viewInstanceDefinition  the view instance definition
-   *
+   * @param viewInstanceDefinition the view instance definition
    * @return a handler
-   *
    * @throws org.apache.ambari.view.SystemException if an handler can not be obtained for the given view instance
    */
   private WebAppContext getHandler(ViewInstanceEntity viewInstanceDefinition)
     throws SystemException {
 
-    ViewEntity    viewDefinition = viewInstanceDefinition.getViewEntity();
-    WebAppContext webAppContext  = webAppContextProvider.get();
+    ViewEntity viewDefinition = viewInstanceDefinition.getViewEntity();
+    WebAppContext webAppContext = webAppContextProvider.get();
 
     webAppContext.setWar(viewDefinition.getArchive());
     webAppContext.setContextPath(viewInstanceDefinition.getContextPath());
@@ -255,18 +262,36 @@ public class AmbariHandlerList extends HandlerCollection implements ViewInstance
     webAppContext.addFilter(new FilterHolder(springSecurityFilter), "/*", AmbariServer.DISPATCHER_TYPES);
     webAppContext.setAllowNullPathInfo(true);
 
+
     if (webAppContext.getErrorHandler() != null) {
+      ErrorHandler errorHandlerProxy = createAmbariViewErrorHandlerProxy(webAppContext.getErrorHandler());
+      if (errorHandlerProxy != null) {
+        webAppContext.setErrorHandler(errorHandlerProxy);
+      }
       webAppContext.getErrorHandler().setShowStacks(configuration.isServerShowErrorStacks());
     }
 
     return webAppContext;
   }
 
+  private ErrorHandler createAmbariViewErrorHandlerProxy(ErrorHandler errorHandler) {
+    ErrorHandler proxy = null;
+    try {
+      ProxyFactory proxyFactory = new ProxyFactory();
+      proxyFactory.setSuperclass(ErrorHandler.class);
+      proxy = (ErrorHandler) proxyFactory.create(new Class[0],
+        new Object[0],
+        new AmbariViewErrorHandlerProxy(errorHandler, ambariErrorHandler));
+    } catch (Exception e) {
+      LOG.error("An error occurred while instantiating the error handler proxy instance", e);
+    }
+    return proxy;
+  }
+
   /**
    * Get the view that is the target of the request; null if not a view request.
    *
-   * @param target  the target of the request
-   *
+   * @param target the target of the request
    * @return the view target; null if none
    */
   private ViewEntity getTargetView(String target) {
@@ -289,7 +314,7 @@ public class AmbariHandlerList extends HandlerCollection implements ViewInstance
     /**
      * Construct a SharedSessionHandler.
      *
-     * @param sessionHandler  the shared session manager.
+     * @param sessionHandler the shared session manager.
      */
     public SharedSessionHandler(SessionHandler sessionHandler) {
       setSessionIdManager(sessionHandler.getSessionIdManager());
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index a2b66e4a34..6ac11c42e3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -826,7 +826,11 @@ public class AmbariServer {
     configureHandlerCompression(root);
     configureAdditionalContentTypes(root);
     root.setContextPath(CONTEXT_PATH);
-    root.setErrorHandler(injector.getInstance(AmbariErrorHandler.class));
+
+    AmbariErrorHandler ambariErrorHandler = injector.getInstance(AmbariErrorHandler.class);
+    ambariErrorHandler.setShowStacks(configs.isServerShowErrorStacks());
+    root.setErrorHandler(ambariErrorHandler);
+
     root.setMaxFormContentSize(-1);
 
     /* Configure web app context */
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariErrorHandlerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariErrorHandlerIT.java
similarity index 93%
copy from ambari-server/src/test/java/org/apache/ambari/server/api/AmbariErrorHandlerTest.java
copy to ambari-server/src/test/java/org/apache/ambari/server/api/AmbariErrorHandlerIT.java
index 827d0d9e55..d2c23c1038 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariErrorHandlerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariErrorHandlerIT.java
@@ -45,14 +45,8 @@ import com.sun.jersey.api.client.Client;
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.WebResource;
 
-public class AmbariErrorHandlerTest extends EasyMockSupport {
-  Gson gson = new Gson();
-
-
-  @Test
-  public void testHandle() throws Exception {
-
-  }
+public class AmbariErrorHandlerIT extends EasyMockSupport {
+  private Gson gson = new Gson();
 
   @Test
   public void testErrorWithJetty() throws Exception {
@@ -71,7 +65,7 @@ public class AmbariErrorHandlerTest extends EasyMockSupport {
 
     server.start();
 
-    int localPort = ((ServerConnector)server.getConnectors()[0]).getLocalPort();
+    int localPort = ((ServerConnector) server.getConnectors()[0]).getLocalPort();
 
     Client client = new Client();
     WebResource resource = client.resource("http://localhost:" + localPort + "/");
@@ -96,7 +90,7 @@ public class AmbariErrorHandlerTest extends EasyMockSupport {
       fail("Incorrect response");
     }
 
-     server.stop();
+    server.stop();
 
     verifyAll();
   }
@@ -112,4 +106,5 @@ public class AmbariErrorHandlerTest extends EasyMockSupport {
     }
 
   }
+
 }
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariErrorHandlerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariErrorHandlerTest.java
index 827d0d9e55..ed00b0705f 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariErrorHandlerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariErrorHandlerTest.java
@@ -18,98 +18,137 @@
 
 package org.apache.ambari.server.api;
 
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
 
 import java.io.IOException;
-import java.util.Map;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.UUID;
 
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
+import javax.servlet.RequestDispatcher;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.ambari.server.security.authentication.jwt.JwtAuthenticationPropertiesProvider;
+import org.apache.http.HttpStatus;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
 import org.easymock.EasyMockSupport;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpConnection;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.gson.Gson;
-import com.google.gson.JsonSyntaxException;
-import com.sun.jersey.api.client.Client;
-import com.sun.jersey.api.client.ClientResponse;
-import com.sun.jersey.api.client.WebResource;
 
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({AmbariErrorHandler.class, LoggerFactory.class, HttpConnection.class, UUID.class})
 public class AmbariErrorHandlerTest extends EasyMockSupport {
-  Gson gson = new Gson();
+  private Gson gson = new Gson();
 
+  private Logger logger = createNiceMock(Logger.class);
 
-  @Test
-  public void testHandle() throws Exception {
+  private HttpConnection httpConnection = createNiceMock(HttpConnection.class);
+  private HttpChannel httpChannel = createNiceMock(HttpChannel.class);
 
-  }
+  private Response response = createNiceMock(Response.class);
+  private Request request = createNiceMock(Request.class);
 
-  @Test
-  public void testErrorWithJetty() throws Exception {
-    Server server = new Server(0);
-    JwtAuthenticationPropertiesProvider propertiesProvider = createNiceMock(JwtAuthenticationPropertiesProvider.class);
-    expect(propertiesProvider.get()).andReturn(null).anyTimes();
+  private HttpServletResponse httpServletResponse = createNiceMock(HttpServletResponse.class);
+  private HttpServletRequest httpServletRequest = createNiceMock(HttpServletRequest.class);
 
-    replayAll();
+  private JwtAuthenticationPropertiesProvider propertiesProvider = createNiceMock(JwtAuthenticationPropertiesProvider.class);
 
-    ServletContextHandler root = new ServletContextHandler(server, "/",
-      ServletContextHandler.SECURITY | ServletContextHandler.SESSIONS);
+  final String target = "target";
 
-    root.addServlet(HelloServlet.class, "/hello");
-    root.addServlet(DefaultServlet.class, "/");
-    root.setErrorHandler(new AmbariErrorHandler(gson, propertiesProvider));
+  @Test
+  public void testHandleInternalServerError() throws IOException {
+    //given
+    final UUID requestId = UUID.fromString("4db659b2-7902-477b-b8e6-c35261d3334a");
 
-    server.start();
+    mockStatic(HttpConnection.class, UUID.class, LoggerFactory.class);
+    when(HttpConnection.getCurrentConnection()).thenReturn(httpConnection);
+    when(UUID.randomUUID()).thenReturn(requestId);
+    when(LoggerFactory.getLogger(AmbariErrorHandler.class)).thenReturn(logger);
 
-    int localPort = ((ServerConnector)server.getConnectors()[0]).getLocalPort();
+    Throwable th = createNiceMock(Throwable.class);
 
-    Client client = new Client();
-    WebResource resource = client.resource("http://localhost:" + localPort + "/");
+    Capture<String> captureLogMessage = EasyMock.newCapture();
+    logger.error(capture(captureLogMessage), eq(th));
+    expectLastCall();
 
+    expect(httpConnection.getHttpChannel()).andReturn(httpChannel);
+    expect(httpChannel.getRequest()).andReturn(request);
+    expect(httpChannel.getResponse()).andReturn(response).times(2);
+    expect(response.getStatus()).andReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR);
 
-    ClientResponse successResponse = resource.path("hello").get(ClientResponse.class);
-    assertEquals(HttpServletResponse.SC_OK, successResponse.getStatus());
+    final String requestUri = "/path/to/target";
+    expect(httpServletRequest.getRequestURI()).andReturn(requestUri);
+    expect(httpServletRequest.getAttribute(RequestDispatcher.ERROR_EXCEPTION)).andReturn(th);
 
-    ClientResponse failResponse = resource.path("fail").get(ClientResponse.class);
+    final StringWriter writer = new StringWriter();
+    expect(httpServletResponse.getWriter()).andReturn(new PrintWriter(writer));
 
-    assertEquals(HttpServletResponse.SC_NOT_FOUND, failResponse.getStatus());
+    expect(propertiesProvider.get()).andReturn(null).anyTimes();
+
+    replayAll();
 
-    try {
-      String response = failResponse.getEntity(String.class);
-      System.out.println(response);
-      Map map;
-      map = gson.fromJson(response, Map.class);
-      System.out.println(map);
-      assertNotNull("Incorrect response status", map.get("status"));
-      assertNotNull("Incorrect response message", map.get("message"));
-    } catch (JsonSyntaxException e1) {
-      fail("Incorrect response");
-    }
+    final String expectedResponse = "{\"status\":500,\"message\":\"Internal server error, please refer the exception by " + requestId + " in the server log file\"}";
+    final String expectedErrorMessage = "Internal server error, please refer the exception by " + requestId + " in the server log file, requestURI: " + requestUri;
 
-     server.stop();
+    AmbariErrorHandler ambariErrorHandler = new AmbariErrorHandler(gson, propertiesProvider);
+    ambariErrorHandler.setShowStacks(false);
 
+    //when
+    ambariErrorHandler.handle(target, request, httpServletRequest, httpServletResponse);
+
+    //then
+    assertEquals(expectedResponse, writer.toString());
+    assertEquals(expectedErrorMessage, captureLogMessage.getValue());
     verifyAll();
   }
 
+  @Test
+  public void testHandleGeneralError() throws Exception {
 
-  @SuppressWarnings("serial")
-  public static class HelloServlet extends HttpServlet {
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-      response.setContentType("text/html");
-      response.setStatus(HttpServletResponse.SC_OK);
-      response.getWriter().println("hello");
-    }
+    //given
+    mockStatic(HttpConnection.class);
+    when(HttpConnection.getCurrentConnection()).thenReturn(httpConnection);
 
+    expect(httpConnection.getHttpChannel()).andReturn(httpChannel);
+    expect(httpChannel.getRequest()).andReturn(request);
+    expect(httpChannel.getResponse()).andReturn(response).anyTimes();
+    expect(response.getStatus()).andReturn(HttpStatus.SC_BAD_REQUEST);
+
+    final StringWriter writer = new StringWriter();
+    expect(httpServletResponse.getWriter()).andReturn(new PrintWriter(writer));
+
+    expect(propertiesProvider.get()).andReturn(null).anyTimes();
+
+    replayAll();
+
+    final String expectedResponse = "{\"status\":400,\"message\":\"Bad Request\"}";
+
+    AmbariErrorHandler ambariErrorHandler = new AmbariErrorHandler(gson, propertiesProvider);
+
+    //when
+    ambariErrorHandler.handle(target, request, httpServletRequest, httpServletResponse);
+    System.out.println(writer.toString());
+
+    //then
+    assertEquals(expectedResponse, writer.toString());
+    verifyAll();
   }
 }
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariViewErrorHandlerProxyTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariViewErrorHandlerProxyTest.java
new file mode 100644
index 0000000000..8718bbf4f6
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/AmbariViewErrorHandlerProxyTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.ambari.server.api;
+
+import static junit.framework.Assert.assertTrue;
+import static org.easymock.EasyMock.captureBoolean;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpStatus;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.junit.Test;
+
+public class AmbariViewErrorHandlerProxyTest {
+
+  final AmbariErrorHandler ambariErrorHandler = createNiceMock(AmbariErrorHandler.class);
+  final ErrorHandler errorHandler = createNiceMock(ErrorHandler.class);
+
+  final HttpServletRequest httpServletRequest = createNiceMock(HttpServletRequest.class);
+  final HttpServletResponse httpServletResponse = createNiceMock(HttpServletResponse.class);
+  final Request request = createNiceMock(Request.class);
+
+  final String target = "test/target/uri";
+
+  @Test
+  public void testHandleInternalServerError() throws Throwable {
+    //given
+    Throwable th = createNiceMock(Throwable.class);
+    expect(httpServletRequest.getAttribute(RequestDispatcher.ERROR_EXCEPTION)).andReturn(th).anyTimes();
+    expect(httpServletResponse.getStatus()).andReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR).anyTimes();
+
+    ambariErrorHandler.handle(target, request, httpServletRequest, httpServletResponse);
+    expectLastCall();
+
+    replay(ambariErrorHandler, errorHandler, httpServletRequest, httpServletResponse, th);
+
+    //when
+    AmbariViewErrorHandlerProxy proxy = new AmbariViewErrorHandlerProxy(errorHandler, ambariErrorHandler);
+    proxy.handle(target, request, httpServletRequest, httpServletResponse);
+
+    //then
+    verify(ambariErrorHandler, errorHandler, httpServletRequest, httpServletResponse, th);
+  }
+
+  @Test
+  public void testHandleGeneralError() throws Throwable {
+    //given
+    Throwable th = createNiceMock(Throwable.class);
+    expect(httpServletRequest.getAttribute(RequestDispatcher.ERROR_EXCEPTION)).andReturn(th).anyTimes();
+    expect(httpServletResponse.getStatus()).andReturn(HttpStatus.SC_BAD_REQUEST).anyTimes();
+
+    errorHandler.handle(target, request, httpServletRequest, httpServletResponse);
+    expectLastCall();
+
+    replay(ambariErrorHandler, errorHandler, httpServletRequest, httpServletResponse, th);
+
+    //when
+    AmbariViewErrorHandlerProxy proxy = new AmbariViewErrorHandlerProxy(errorHandler, ambariErrorHandler);
+    proxy.handle(target, request, httpServletRequest, httpServletResponse);
+
+    //then
+    verify(ambariErrorHandler, errorHandler, httpServletRequest, httpServletResponse, th);
+  }
+
+  @Test
+  public void testShowStacks() {
+
+    //given
+    Capture<Boolean> captureShowStacksErrorHandler = EasyMock.newCapture();
+    errorHandler.setShowStacks(captureBoolean(captureShowStacksErrorHandler));
+    expectLastCall();
+
+    Capture<Boolean> captureShowStacksAmbariErrorHandler = EasyMock.newCapture();
+    ambariErrorHandler.setShowStacks(captureBoolean(captureShowStacksAmbariErrorHandler));
+    expectLastCall();
+
+    replay(errorHandler, ambariErrorHandler);
+
+
+    //when
+    AmbariViewErrorHandlerProxy proxy = new AmbariViewErrorHandlerProxy(errorHandler, ambariErrorHandler);
+    proxy.setShowStacks(true);
+
+    //then
+    assertTrue(captureShowStacksErrorHandler.getValue());
+    assertTrue(captureShowStacksAmbariErrorHandler.getValue());
+
+    verify(errorHandler, ambariErrorHandler);
+
+  }
+}
\ No newline at end of file
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariHandlerListTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariHandlerListTest.java
index 37e7bb4d3f..b6d5555c30 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariHandlerListTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariHandlerListTest.java
@@ -99,7 +99,7 @@ public class AmbariHandlerListTest {
     Capture<Boolean> showStackCapture = EasyMock.newCapture();
     errorHandler.setShowStacks(EasyMock.captureBoolean(showStackCapture));
 
-    expect(handler.getErrorHandler()).andReturn(errorHandler).times(2);
+    expect(handler.getErrorHandler()).andReturn(errorHandler).times(3);
 
     replay(handler, server, sessionHandler, configuration, errorHandler);
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ambari.apache.org
For additional commands, e-mail: commits-help@ambari.apache.org