You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2017/02/24 14:19:52 UTC

[45/50] ambari git commit: AMBARI-20050. Issue while importing workflow with insufficient permissions.(Madhan Mohan Reddy via gauravn7)

AMBARI-20050. Issue while importing workflow with insufficient permissions.(Madhan Mohan Reddy via gauravn7)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/9fdeec1a
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/9fdeec1a
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/9fdeec1a

Branch: refs/heads/branch-feature-AMBARI-12556
Commit: 9fdeec1afecf1983dfdb633bca0ab07b15c6d24c
Parents: bb7b83f
Author: Gaurav Nagar <gr...@gmail.com>
Authored: Thu Feb 23 19:24:00 2017 +0530
Committer: Gaurav Nagar <gr...@gmail.com>
Committed: Thu Feb 23 19:24:31 2017 +0530

----------------------------------------------------------------------
 .../apache/oozie/ambari/view/OozieDelegate.java |  12 +-
 .../ambari/view/OozieProxyImpersonator.java     | 381 +++++++------------
 .../oozie/ambari/view/assets/AssetResource.java |  93 +++--
 .../oozie/ambari/view/exception/ErrorCode.java  |  58 +++
 .../ambari/view/exception/WfmException.java     |  46 +++
 .../ambari/view/exception/WfmWebException.java  | 115 ++++++
 .../WorkflowsManagerResource.java               |  36 +-
 .../ui/app/components/bundle-config.js          |   8 +-
 .../resources/ui/app/components/coord-config.js |   6 +-
 .../ui/app/components/designer-errors.js        |  49 +++
 .../ui/app/components/flow-designer.js          |  69 +---
 .../ui/app/components/stack-trace-dialog.js     |  26 ++
 .../src/main/resources/ui/app/styles/app.less   |  13 +-
 .../app/templates/components/bundle-config.hbs  |   4 +-
 .../app/templates/components/coord-config.hbs   |   2 +-
 .../templates/components/designer-errors.hbs    |  17 +
 .../app/templates/components/flow-designer.hbs  |  18 +-
 .../templates/components/stack-trace-dialog.hbs |  33 ++
 .../components/stack-trace-dialog-test.js       |  40 ++
 19 files changed, 648 insertions(+), 378 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieDelegate.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieDelegate.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieDelegate.java
index 55c4312..6f3c4d2 100644
--- a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieDelegate.java
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieDelegate.java
@@ -32,6 +32,8 @@ import javax.ws.rs.core.Response;
 
 import org.apache.ambari.view.ViewContext;
 import org.apache.commons.io.IOUtils;
+import org.apache.oozie.ambari.view.exception.ErrorCode;
+import org.apache.oozie.ambari.view.exception.WfmException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -90,10 +92,16 @@ public class OozieDelegate {
 
     LOGGER.info("Resp from oozie status entity=="
       + serviceResponse.getEntity());
+    String oozieResp=null;
     if (serviceResponse.getEntity() instanceof String) {
-      return (String) serviceResponse.getEntity();
+      oozieResp= (String) serviceResponse.getEntity();
     } else {
-      return "success";
+      oozieResp= serviceResponse.getEntity().toString();
+    }
+    if (oozieResp != null && oozieResp.trim().startsWith("{")) {
+      return  oozieResp;
+    }else{
+      throw new WfmException(oozieResp,ErrorCode.OOZIE_SUBMIT_ERROR);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieProxyImpersonator.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieProxyImpersonator.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieProxyImpersonator.java
index 6603a9c..c4e5bbd 100644
--- a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieProxyImpersonator.java
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/OozieProxyImpersonator.java
@@ -49,9 +49,10 @@ import javax.ws.rs.core.UriInfo;
 import org.apache.ambari.view.ViewContext;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang.exception.ExceptionUtils;
-import org.apache.hadoop.security.AccessControlException;
 import org.apache.oozie.ambari.view.assets.AssetResource;
+import org.apache.oozie.ambari.view.exception.ErrorCode;
+import org.apache.oozie.ambari.view.exception.WfmException;
+import org.apache.oozie.ambari.view.exception.WfmWebException;
 import org.apache.oozie.ambari.view.workflowmanager.WorkflowManagerService;
 import org.apache.oozie.ambari.view.workflowmanager.WorkflowsManagerResource;
 import org.slf4j.Logger;
@@ -83,30 +84,7 @@ public class OozieProxyImpersonator {
   private final OozieUtils oozieUtils = new OozieUtils();
   private final AssetResource assetResource;
 
-  private enum ErrorCodes {
-    OOZIE_SUBMIT_ERROR("error.oozie.submit", "Oozie Submit error"), OOZIE_IO_ERROR(
-      "error.oozie.io", "Oozie I/O error"), FILE_ACCESS_ACL_ERROR(
-      "error.file.access.control",
-      "Access Error to file due to access control"), FILE_ACCESS_UNKNOWN_ERROR(
-      "error.file.access", "Error accessing file"), WORKFLOW_PATH_EXISTS(
-      "error.workflow.path.exists", "Workflow Path exists"), WORKFLOW_XML_DOES_NOT_EXIST(
-      "error.workflow.xml.not.exists", "Workflow Xml does not exist");
-    private String errorCode;
-    private String description;
-
-    ErrorCodes(String errorCode, String description) {
-      this.errorCode = errorCode;
-      this.description = description;
-    }
-
-    public String getErrorCode() {
-      return errorCode;
-    }
 
-    public String getDescription() {
-      return description;
-    }
-  }
   private static enum WorkflowFormat{
     XML("xml"),
     DRAFT("draft");
@@ -139,15 +117,23 @@ public class OozieProxyImpersonator {
   @GET
   @Path("hdfsCheck")
   public Response hdfsCheck(){
-    hdfsFileUtils.hdfsCheck();
-    return Response.ok().build();
+    try {
+      hdfsFileUtils.hdfsCheck();
+      return Response.ok().build();
+    }catch (Exception e){
+      throw new WfmWebException(e);
+    }
   }
 
   @GET
   @Path("homeDirCheck")
   public Response homeDirCheck(){
-    hdfsFileUtils.homeDirCheck();
-    return Response.ok().build();
+    try{
+      hdfsFileUtils.homeDirCheck();
+      return Response.ok().build();
+    }catch (Exception e){
+      throw new WfmWebException(e);
+    }
   }
 
   @Path("/fileServices")
@@ -189,57 +175,89 @@ public class OozieProxyImpersonator {
                             @QueryParam("projectId") String projectId,
                             @DefaultValue("false") @QueryParam("overwrite") Boolean overwrite,
                             @QueryParam("description") String description,
-                            @QueryParam("jobType") String jobType) {
+                            @QueryParam("jobType") String jobTypeString) {
     LOGGER.info("submit workflow job called");
-    return submitJobInternal(postBody, headers, ui, appPath, overwrite,
-      JobType.valueOf(jobType), projectId, description);
+    JobType jobType = JobType.valueOf(jobTypeString);
+    if (StringUtils.isEmpty(appPath)) {
+      throw new WfmWebException(ErrorCode.INVALID_EMPTY_INPUT);
+    }
+    appPath = workflowFilesService.getWorkflowFileName(appPath.trim(), jobType);
+    try {
+      if (!overwrite) {
+        boolean fileExists = hdfsFileUtils.fileExists(appPath);
+        if (fileExists) {
+          throw new WfmWebException(ErrorCode.WORKFLOW_PATH_EXISTS);
+        }
+      }
+      postBody = utils.formatXml(postBody);
+
+      String filePath = workflowFilesService.createFile(appPath, postBody, overwrite);
+      LOGGER.info(String.format("submit workflow job done. filePath=[%s]", filePath));
+
+      if (PROJ_MANAGER_ENABLED) {
+        String name = oozieUtils.deduceWorkflowNameFromXml(postBody);
+        workflowManagerService.saveWorkflow(projectId, appPath, jobType,
+          null, viewContext.getUsername(), name);
+      }
+      String response = oozieDelegate.submitWorkflowJobToOozie(headers,
+        appPath, ui.getQueryParameters(), jobType);
+      return Response.status(Status.OK).entity(response).build();
+    } catch (WfmWebException ex) {
+      throw ex;
+    } catch(WfmException ex){
+      throw new WfmWebException(ex,ex.getErrorCode());
+    } catch(Exception ex) {
+      throw new WfmWebException(ex);
+    }
   }
 
   @POST
   @Path("/saveWorkflow")
   @Consumes({MediaType.TEXT_PLAIN + "," + MediaType.TEXT_XML})
   public Response saveWorkflow(String postBody, @Context HttpHeaders headers,
-                               @Context UriInfo ui, @QueryParam("app.path") String appPath, @QueryParam("jobType") String jobTypeStr,
+                               @Context UriInfo ui, @QueryParam("app.path") String appPath,
+                               @QueryParam("jobType") String jobTypeStr,
                                @DefaultValue("false") @QueryParam("overwrite") Boolean overwrite) {
     LOGGER.info("save workflow  called");
     if (StringUtils.isEmpty(appPath)) {
-      throw new RuntimeException("app path can't be empty.");
+      throw new WfmWebException(ErrorCode.INVALID_EMPTY_INPUT);
     }
     JobType jobType = StringUtils.isEmpty(jobTypeStr) ? JobType.WORKFLOW : JobType.valueOf(jobTypeStr);
     String workflowFilePath = workflowFilesService.getWorkflowFileName(appPath.trim(), jobType);
-    if (!overwrite) {
-      boolean fileExists = hdfsFileUtils.fileExists(workflowFilePath);
-      if (fileExists) {
-        return getFileExistsResponse();
-      }
-    }
-
     try {
+      if (!overwrite) {
+        boolean fileExists = hdfsFileUtils.fileExists(workflowFilePath);
+        if (fileExists) {
+          throw new WfmWebException(ErrorCode.WORKFLOW_PATH_EXISTS);
+        }
+      }
       if (utils.isXml(postBody)) {
         saveWorkflowXml(jobType, appPath, postBody, overwrite);
       } else {
         saveDraft(jobType, appPath, postBody, overwrite);
       }
       if (PROJ_MANAGER_ENABLED) {
-        workflowManagerService.saveWorkflow(null, workflowFilePath,
-          jobType, null,
+        workflowManagerService.saveWorkflow(null, workflowFilePath, jobType, null,
           viewContext.getUsername(), getWorkflowName(postBody));
       }
-    } catch (IOException ex) {
-      return getRespCodeForException(ex);
+    } catch (WfmWebException ex) {
+      throw ex;
+    } catch (Exception ex) {
+       throw new WfmWebException(ex);
     }
-
     return Response.ok().build();
   }
-  private String getWorkflowName(String postBody){
+
+  private String getWorkflowName(String postBody) {
     if (utils.isXml(postBody)) {
       return oozieUtils.deduceWorkflowNameFromXml(postBody);
-    }else{
+    } else {
       return oozieUtils.deduceWorkflowNameFromJson(postBody);
     }
   }
 
-  private void saveWorkflowXml(JobType jobType, String appPath, String postBody, Boolean overwrite) throws IOException {
+  private void saveWorkflowXml(JobType jobType, String appPath, String postBody,
+                               Boolean overwrite) throws IOException {
     appPath = workflowFilesService.getWorkflowFileName(appPath.trim(), jobType);
     postBody = utils.formatXml(postBody);
     workflowFilesService.createFile(appPath, postBody, overwrite);
@@ -267,49 +285,47 @@ public class OozieProxyImpersonator {
                                @DefaultValue("false") @QueryParam("overwrite") Boolean overwrite) {
     LOGGER.info("publish asset called");
     if (StringUtils.isEmpty(uploadPath)) {
-      throw new RuntimeException("upload path can't be empty.");
+      throw new WfmWebException(ErrorCode.INVALID_EMPTY_INPUT);
     }
     uploadPath = uploadPath.trim();
-    Map<String, String> validateAsset = assetResource.validateAsset(headers, postBody,
-      ui.getQueryParameters());
-    if (!STATUS_OK.equals(validateAsset.get(STATUS_KEY))) {
-      return Response.status(Status.BAD_REQUEST).entity(
-        validateAsset.get(MESSAGE_KEY)).build();
+    try {
+      Map<String, String> validateAsset = assetResource.validateAsset(headers, postBody,
+        ui.getQueryParameters());
+      if (!STATUS_OK.equals(validateAsset.get(STATUS_KEY))) {
+        WfmWebException wfmEx=new WfmWebException(ErrorCode.INVALID_ASSET_INPUT);
+        wfmEx.setAdditionalDetail(validateAsset.get(MESSAGE_KEY));
+        throw wfmEx;
+      }
+      return saveAsset(postBody, uploadPath, overwrite);
+    } catch (WfmWebException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new WfmWebException(ex);
     }
-    return saveAsset(postBody, uploadPath, overwrite);
   }
 
-  private Response saveAsset(String postBody, String uploadPath,
-                             Boolean overwrite) {
+  private Response saveAsset(String postBody, String uploadPath, Boolean overwrite) throws IOException {
     uploadPath = workflowFilesService.getAssetFileName(uploadPath);
     if (!overwrite) {
       boolean fileExists = hdfsFileUtils.fileExists(uploadPath);
       if (fileExists) {
-        return getFileExistsResponse();
+        throw new WfmWebException(ErrorCode.WORKFLOW_PATH_EXISTS);
       }
     }
     postBody = utils.formatXml(postBody);
-    try {
-      String filePath = workflowFilesService.createAssetFile(uploadPath,
-        postBody, overwrite);
-      LOGGER.info(String.format("publish asset job done. filePath=[%s]",
-        filePath));
-      return Response.ok().build();
-    } catch (Exception ex) {
-      LOGGER.error(ex.getMessage(), ex);
-      return getRespCodeForException(ex);
-    }
+    String filePath = workflowFilesService.createAssetFile(uploadPath, postBody, overwrite);
+    LOGGER.info(String.format("publish asset job done. filePath=[%s]", filePath));
+    return Response.ok().build();
   }
+
   @GET
   @Path("/readAsset")
-  public Response readAsset(
-          @QueryParam("assetPath") String assetPath) {
+  public Response readAsset(@QueryParam("assetPath") String assetPath) {
     if (StringUtils.isEmpty(assetPath)) {
-      throw new RuntimeException("assetPath can't be empty.");
+      throw new WfmWebException(ErrorCode.INVALID_EMPTY_INPUT);
     }
     try {
-      final InputStream is = workflowFilesService
-              .readAssset(assetPath);
+      final InputStream is = workflowFilesService.readAssset(assetPath);
       StreamingOutput streamer = new StreamingOutput() {
         @Override
         public void write(OutputStream os) throws IOException,
@@ -320,17 +336,16 @@ public class OozieProxyImpersonator {
         }
       };
       return Response.ok(streamer).status(200).build();
-    } catch (IOException e) {
-      return getRespCodeForException(e);
+    } catch (IOException ex) {
+      throw new WfmWebException(ex);
     }
   }
 
-
   @GET
   @Path("/readWorkflowDraft")
   public Response readDraft(@QueryParam("workflowXmlPath") String workflowPath) {
     if (StringUtils.isEmpty(workflowPath)) {
-      throw new RuntimeException("workflowXmlPath can't be empty.");
+      throw new WfmWebException(ErrorCode.INVALID_EMPTY_INPUT);
     }
     try {
       final InputStream is = workflowFilesService.readDraft(workflowPath);
@@ -344,113 +359,37 @@ public class OozieProxyImpersonator {
         }
       };
       return Response.ok(streamer).status(200).build();
-    } catch (IOException e) {
-      return getRespCodeForException(e);
+    } catch (IOException ex) {
+      throw new WfmWebException(ex);
     }
   }
 
   @POST
   @Path("/discardWorkflowDraft")
   public Response discardDraft(
-    @QueryParam("workflowXmlPath") String workflowPath)
-    throws IOException {
-    workflowFilesService.discardDraft(workflowPath);
-    return Response.ok().build();
-  }
-
-  private Response submitJobInternal(String postBody, HttpHeaders headers,
-                                     UriInfo ui, String appPath, Boolean overwrite, JobType jobType,
-                                     String projectId, String description) {
-    if (StringUtils.isEmpty(appPath)) {
-      throw new RuntimeException("app path can't be empty.");
-    }
-    appPath = workflowFilesService.getWorkflowFileName(appPath.trim(), jobType);
-    if (!overwrite) {
-      boolean fileExists = hdfsFileUtils.fileExists(appPath);
-      if (fileExists) {
-        return getFileExistsResponse();
-      }
-    }
-    postBody = utils.formatXml(postBody);
+    @QueryParam("workflowXmlPath") String workflowPath) {
     try {
-      String filePath = workflowFilesService.createFile(appPath, postBody,
-        overwrite);
-      LOGGER.info(String.format(
-        "submit workflow job done. filePath=[%s]", filePath));
-    } catch (Exception ex) {
-      LOGGER.error(ex.getMessage(), ex);
-      return getRespCodeForException(ex);
-
-    }
-    if (PROJ_MANAGER_ENABLED) {
-      String name = oozieUtils.deduceWorkflowNameFromXml(postBody);
-      workflowManagerService.saveWorkflow(projectId, appPath, jobType,
-        "todo description", viewContext.getUsername(), name);
-    }
-
-    String response = oozieDelegate.submitWorkflowJobToOozie(headers,
-      appPath, ui.getQueryParameters(), jobType);
-    if (response != null && response.trim().startsWith("{")) {
-      // dealing with oozie giving error but with 200 response.
-      return Response.status(Response.Status.OK).entity(response).build();
-    } else {
-      HashMap<String, String> resp = new HashMap<String, String>();
-      resp.put("status", ErrorCodes.OOZIE_SUBMIT_ERROR.getErrorCode());
-      resp.put("message", response);
-      return Response.status(Response.Status.BAD_REQUEST).entity(resp)
-        .build();
+      workflowFilesService.discardDraft(workflowPath);
+      return Response.ok().build();
+    } catch (IOException ex) {
+      throw new WfmWebException(ex);
     }
-
-  }
-
-  private Response getRespCodeForException(Exception ex) {
-    if (ex instanceof AccessControlException) {
-      HashMap<String, String> errorDetails = getErrorDetails(
-        ErrorCodes.FILE_ACCESS_ACL_ERROR.getErrorCode(),
-        ErrorCodes.FILE_ACCESS_ACL_ERROR.getDescription(), ex);
-      return Response.status(Response.Status.BAD_REQUEST)
-        .entity(errorDetails).build();
-    } else if (ex instanceof IOException) {
-      HashMap<String, String> errorDetails = getErrorDetails(
-        ErrorCodes.FILE_ACCESS_UNKNOWN_ERROR.getErrorCode(),
-        ErrorCodes.FILE_ACCESS_UNKNOWN_ERROR.getDescription(), ex);
-      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
-        .entity(errorDetails).build();
-    } else {
-      HashMap<String, String> errorDetails = getErrorDetails(
-        ErrorCodes.FILE_ACCESS_UNKNOWN_ERROR.getErrorCode(),
-        ErrorCodes.FILE_ACCESS_UNKNOWN_ERROR.getDescription(), ex);
-      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
-        .entity(errorDetails).build();
-    }
-
-  }
-
-  private Response getFileExistsResponse() {
-    HashMap<String, String> resp = new HashMap<String, String>();
-    resp.put("status", ErrorCodes.WORKFLOW_PATH_EXISTS.getErrorCode());
-    resp.put("message", ErrorCodes.WORKFLOW_PATH_EXISTS.getDescription());
-    return Response.status(Response.Status.BAD_REQUEST).entity(resp)
-      .build();
   }
 
   @GET
   @Path("/readWorkflow")
   public Response readWorkflow(
     @QueryParam("workflowPath") String workflowPath, @QueryParam("jobType") String jobTypeStr) {
-    String workflowFileName=workflowFilesService.getWorkflowFileName(workflowPath, JobType.valueOf(jobTypeStr));
-    if (!hdfsFileUtils.fileExists(workflowFileName)){
-      HashMap<String,String> response=new HashMap<>();
-      response.put("status", ErrorCodes.WORKFLOW_XML_DOES_NOT_EXIST.getErrorCode());
-      response.put("message", ErrorCodes.WORKFLOW_XML_DOES_NOT_EXIST.getDescription());
-      return Response.status(Status.BAD_REQUEST).entity(response).build();
-    }
+    try {
+      String workflowFileName = workflowFilesService.getWorkflowFileName(workflowPath, JobType.valueOf(jobTypeStr));
+      if (!hdfsFileUtils.fileExists(workflowFileName)) {
+        throw new WfmWebException(ErrorCode.WORKFLOW_XML_DOES_NOT_EXIST);
+      }
+      WorkflowFileInfo workflowDetails = workflowFilesService
+        .getWorkflowDetails(workflowPath, JobType.valueOf(jobTypeStr));
+      if (workflowPath.endsWith(Constants.WF_DRAFT_EXTENSION) || workflowDetails.getIsDraftCurrent()) {
+        String filePath = workflowFilesService.getWorkflowDraftFileName(workflowPath, JobType.valueOf(jobTypeStr));
 
-    WorkflowFileInfo workflowDetails = workflowFilesService
-      .getWorkflowDetails(workflowPath, JobType.valueOf(jobTypeStr));
-    if (workflowPath.endsWith(Constants.WF_DRAFT_EXTENSION) || workflowDetails.getIsDraftCurrent()) {
-      String filePath = workflowFilesService.getWorkflowDraftFileName(workflowPath, JobType.valueOf(jobTypeStr));
-      try {
         InputStream inputStream = workflowFilesService.readWorkflowXml(filePath);
         String stringResponse = IOUtils.toString(inputStream);
         if (!workflowFilesService.isDraftFormatCurrent(stringResponse)) {
@@ -459,36 +398,35 @@ public class OozieProxyImpersonator {
         } else {
           return Response.ok(stringResponse).header(RESPONSE_TYPE, WorkflowFormat.DRAFT.getValue()).build();
         }
-      } catch (IOException e) {
-        return getRespCodeForException(e);
+      } else {
+        String filePath = workflowFilesService.getWorkflowFileName(workflowPath, JobType.valueOf(jobTypeStr));
+        return getWorkflowResponse(filePath, WorkflowFormat.XML.getValue(), false);
       }
-    } else {
-      String filePath = workflowFilesService.getWorkflowFileName(workflowPath, JobType.valueOf(jobTypeStr));
-      return getWorkflowResponse(filePath, WorkflowFormat.XML.getValue(), false);
+    } catch (WfmWebException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new WfmWebException(ex);
     }
   }
 
-  private Response getWorkflowResponse(String filePath, String responseType, boolean olderFormatDraftIngored) {
-    try {
-      final InputStream is = workflowFilesService
-        .readWorkflowXml(filePath);
-      StreamingOutput streamer = new StreamingOutput() {
-        @Override
-        public void write(OutputStream os) throws IOException,
-          WebApplicationException {
-          IOUtils.copy(is, os);
-          is.close();
-          os.close();
-        }
-      };
-      Response.ResponseBuilder responseBuilder = Response.ok(streamer).header(RESPONSE_TYPE, responseType);
-      if(olderFormatDraftIngored){
-        responseBuilder.header(OLDER_FORMAT_DRAFT_INGORED,Boolean.TRUE.toString());
+  private Response getWorkflowResponse(String filePath, String responseType,
+                                       boolean olderFormatDraftIngored) throws IOException {
+    final InputStream is = workflowFilesService.readWorkflowXml(filePath);
+    StreamingOutput streamer = new StreamingOutput() {
+      @Override
+      public void write(OutputStream os) throws IOException,
+        WebApplicationException {
+        IOUtils.copy(is, os);
+        is.close();
+        os.close();
       }
-      return  responseBuilder.build();
-    } catch (IOException e) {
-      return getRespCodeForException(e);
+    };
+    Response.ResponseBuilder responseBuilder = Response.ok(streamer).header(RESPONSE_TYPE, responseType);
+    if (olderFormatDraftIngored) {
+      responseBuilder.header(OLDER_FORMAT_DRAFT_INGORED, Boolean.TRUE.toString());
     }
+    return responseBuilder.build();
+
   }
 
   @GET
@@ -496,12 +434,13 @@ public class OozieProxyImpersonator {
   public Response readWorkflowXml(
     @QueryParam("workflowXmlPath") String workflowPath,@QueryParam("jobType") String jobTypeStr) {
     if (StringUtils.isEmpty(workflowPath)) {
-      throw new RuntimeException("workflowXmlPath can't be empty.");
+      throw new WfmWebException(ErrorCode.INVALID_EMPTY_INPUT);
     }
-
     try {
-      final InputStream is = workflowFilesService
-        .readWorkflowXml(workflowPath);
+      if (!hdfsFileUtils.fileExists(workflowPath)) {
+        throw new WfmWebException(ErrorCode.WORKFLOW_XML_DOES_NOT_EXIST);
+      }
+      final InputStream is = workflowFilesService.readWorkflowXml(workflowPath);
       StreamingOutput streamer = new StreamingOutput() {
         @Override
         public void write(OutputStream os) throws IOException,
@@ -512,22 +451,11 @@ public class OozieProxyImpersonator {
         }
       };
       return Response.ok(streamer).status(200).build();
-    } catch (IOException e) {
-      return getRespCodeForException(e);
-    }
-  }
-
-  private HashMap<String, String> getErrorDetails(String status,
-                                                  String message, Exception ex) {
-    HashMap<String, String> resp = new HashMap<String, String>();
-    resp.put("status", status);
-    if (message != null) {
-      resp.put("message", message);
-    }
-    if (ex != null) {
-      resp.put("stackTrace", ExceptionUtils.getFullStackTrace(ex));
+    } catch (WfmWebException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new WfmWebException(ex);
     }
-    return resp;
   }
 
   @GET
@@ -538,8 +466,7 @@ public class OozieProxyImpersonator {
         .getPath(), ui.getQueryParameters(), HttpMethod.GET, null);
     } catch (Exception ex) {
       LOGGER.error("Error in GET proxy", ex);
-      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
-        .entity(getErrorDetailsForException("Oozie", ex)).build();
+      throw new WfmWebException(ex);
     }
   }
 
@@ -548,13 +475,11 @@ public class OozieProxyImpersonator {
   public Response handlePost(String xml, @Context HttpHeaders headers,
                              @Context UriInfo ui) {
     try {
-
       return oozieDelegate.consumeService(headers, ui.getAbsolutePath()
         .getPath(), ui.getQueryParameters(), HttpMethod.POST, xml);
     } catch (Exception ex) {
       LOGGER.error("Error in POST proxy", ex);
-      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
-        .entity(getErrorDetailsForException("Oozie", ex)).build();
+      throw new WfmWebException(ex);
     }
   }
 
@@ -567,8 +492,7 @@ public class OozieProxyImpersonator {
         .getPath(), ui.getQueryParameters(), HttpMethod.POST, null);
     } catch (Exception ex) {
       LOGGER.error("Error in DELETE proxy", ex);
-      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
-        .entity(getErrorDetailsForException("Oozie", ex)).build();
+      throw new WfmWebException(ex);
     }
   }
 
@@ -581,22 +505,7 @@ public class OozieProxyImpersonator {
         .getPath(), ui.getQueryParameters(), HttpMethod.PUT, body);
     } catch (Exception ex) {
       LOGGER.error("Error in PUT proxy", ex);
-      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
-        .entity(getErrorDetailsForException("Oozie", ex)).build();
-    }
-  }
-
-  private Map<String, String> getErrorDetailsForException(String component,
-                                                          Exception ex) {
-    String errorCode = component + "exception";
-    String errorMessage = component + " Exception";
-    if (ex instanceof RuntimeException) {
-      Throwable cause = ex.getCause();
-      if (cause instanceof IOException) {
-        errorCode = component + "io.exception";
-        errorMessage = component + "IO Exception";
-      }
+      throw new WfmWebException(ex);
     }
-    return getErrorDetails(errorCode, errorMessage, ex);
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetResource.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetResource.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetResource.java
index ef3b508..3355c85 100644
--- a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetResource.java
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/assets/AssetResource.java
@@ -25,13 +25,15 @@ import org.apache.oozie.ambari.view.*;
 import org.apache.oozie.ambari.view.assets.model.ActionAsset;
 import org.apache.oozie.ambari.view.assets.model.ActionAssetDefinition;
 import org.apache.oozie.ambari.view.assets.model.AssetDefintion;
+import org.apache.oozie.ambari.view.exception.ErrorCode;
+import org.apache.oozie.ambari.view.exception.WfmException;
+import org.apache.oozie.ambari.view.exception.WfmWebException;
 import org.apache.oozie.ambari.view.model.APIResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.*;
 import javax.ws.rs.core.*;
-import javax.ws.rs.core.Response.Status;
 import java.io.IOException;
 import java.util.*;
 
@@ -65,7 +67,7 @@ public class AssetResource {
       result.setData(assets);
       return Response.ok(result).build();
     } catch (Exception e) {
-      throw new ServiceFormattedException(e);
+      throw new WfmWebException(e);
     }
   }
 
@@ -80,7 +82,7 @@ public class AssetResource {
       result.setData(assets);
       return Response.ok(result).build();
     } catch (Exception e) {
-      throw new ServiceFormattedException(e);
+      throw new WfmWebException(e);
     }
   }
   @POST
@@ -88,19 +90,20 @@ public class AssetResource {
                             @QueryParam("id") String id, @Context UriInfo ui, String body) {
     try {
       Gson gson = new Gson();
-      AssetDefintion assetDefinition = gson.fromJson(body,
-        AssetDefintion.class);
+      AssetDefintion assetDefinition = gson.fromJson(body, AssetDefintion.class);
       Map<String, String> validateAsset = validateAsset(headers,
         assetDefinition.getDefinition(), ui.getQueryParameters());
       if (!STATUS_OK.equals(validateAsset.get(STATUS_KEY))) {
-        return Response.status(Status.BAD_REQUEST).build();
+        throw new WfmWebException(ErrorCode.ASSET_INVALID_FROM_OOZIE);
       }
       assetService.saveAsset(id, viewContext.getUsername(), assetDefinition);
       APIResult result = new APIResult();
       result.setStatus(APIResult.Status.SUCCESS);
       return Response.ok(result).build();
-    } catch (Exception e) {
-      throw new ServiceFormattedException(e);
+    } catch (WfmWebException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new WfmWebException(ex);
     }
   }
 
@@ -113,43 +116,50 @@ public class AssetResource {
   public Map<String, String> validateAsset(HttpHeaders headers,
                                            String postBody, MultivaluedMap<String, String> queryParams) {
     String workflowXml = oozieUtils.generateWorkflowXml(postBody);
+    Map<String, String> result = new HashMap<>();
+    String tempWfPath = "/tmp" + "/tmpooziewfs/tempwf_" + Math.round(Math.random() * 100000) + ".xml";
     try {
-      Map<String, String> result = new HashMap<>();
-      String tempWfPath = "/tmp" + "/tmpooziewfs/tempwf_" + Math.round(Math.random()*100000) + ".xml";
       hdfsFileUtils.writeToFile(tempWfPath, workflowXml, true);
-      queryParams.put("oozieparam.action", getAsList("dryrun"));
-      queryParams.put("oozieconfig.rerunOnFailure", getAsList("false"));
-      queryParams.put("oozieconfig.useSystemLibPath", getAsList("true"));
-      queryParams.put("resourceManager", getAsList("useDefault"));
-      String dryRunResp = oozieDelegate.submitWorkflowJobToOozie(headers,
-        tempWfPath, queryParams, JobType.WORKFLOW);
-      LOGGER.info(String.format("resp from validating asset=[%s]",
-        dryRunResp));
+    } catch (IOException e) {
+      throw new WfmWebException(e, ErrorCode.FILE_ACCESS_UNKNOWN_ERROR);
+    }
+    queryParams.put("oozieparam.action", getAsList("dryrun"));
+    queryParams.put("oozieconfig.rerunOnFailure", getAsList("false"));
+    queryParams.put("oozieconfig.useSystemLibPath", getAsList("true"));
+    queryParams.put("resourceManager", getAsList("useDefault"));
+    String dryRunResp = oozieDelegate.submitWorkflowJobToOozie(headers,
+      tempWfPath, queryParams, JobType.WORKFLOW);
+    LOGGER.info(String.format("resp from validating asset=[%s]", dryRunResp));
+    try {
       hdfsFileUtils.deleteFile(tempWfPath);
-      if (dryRunResp != null && dryRunResp.trim().startsWith("{")) {
-        JsonElement jsonElement = new JsonParser().parse(dryRunResp);
-        JsonElement idElem = jsonElement.getAsJsonObject().get("id");
-        if (idElem != null) {
-          result.put(STATUS_KEY, STATUS_OK);
-        } else {
-          result.put(STATUS_KEY, STATUS_FAILED);
-          result.put(MESSAGE_KEY, dryRunResp);
-        }
+    } catch (IOException e) {
+      throw new WfmWebException(e, ErrorCode.FILE_ACCESS_UNKNOWN_ERROR);
+    }
+    if (dryRunResp != null && dryRunResp.trim().startsWith("{")) {
+      JsonElement jsonElement = new JsonParser().parse(dryRunResp);
+      JsonElement idElem = jsonElement.getAsJsonObject().get("id");
+      if (idElem != null) {
+        result.put(STATUS_KEY, STATUS_OK);
       } else {
         result.put(STATUS_KEY, STATUS_FAILED);
         result.put(MESSAGE_KEY, dryRunResp);
       }
-      return result;
-    } catch (IOException e) {
-      throw new RuntimeException(e);
+    } else {
+      result.put(STATUS_KEY, STATUS_FAILED);
+      result.put(MESSAGE_KEY, dryRunResp);
     }
+    return result;
   }
 
   @GET
   @Path("/assetNameAvailable")
   public Response assetNameAvailable(@QueryParam("name") String name){
-    boolean available=assetService.isAssetNameAvailable(name);
-    return Response.ok(available).build();
+    try {
+      boolean available = assetService.isAssetNameAvailable(name);
+      return Response.ok(available).build();
+    }catch (Exception e){
+      throw new WfmWebException(e);
+    }
   }
 
   @GET
@@ -162,7 +172,7 @@ public class AssetResource {
       result.setData(assetDefinition);
       return Response.ok(result).build();
     } catch (Exception e) {
-      throw new ServiceFormattedException(e);
+      throw new WfmWebException(e);
     }
   }
 
@@ -170,14 +180,13 @@ public class AssetResource {
   @Path("/definition/id}")
   public Response getAssetDefinition(@PathParam("defnitionId") String id) {
     try {
-      ActionAssetDefinition assetDefinition = assetService
-        .getAssetDefinition(id);
+      ActionAssetDefinition assetDefinition = assetService.getAssetDefinition(id);
       APIResult result = new APIResult();
       result.setStatus(APIResult.Status.SUCCESS);
       result.setData(assetDefinition);
       return Response.ok(result).build();
     } catch (Exception e) {
-      throw new ServiceFormattedException(e);
+      throw new WfmWebException(e);
     }
   }
 
@@ -187,19 +196,19 @@ public class AssetResource {
     try {
       ActionAsset asset = assetService.getAsset(id);
       if (asset == null) {
-        throw new RuntimeException("Asset doesnt exist");
+        throw new WfmWebException(ErrorCode.ASSET_NOT_EXIST);
       }
       if (!viewContext.getUsername().equals(asset.getOwner())){
-        throw new RuntimeException(
-          "Dont have permission to delete this asset");
+        throw new WfmWebException(ErrorCode.PERMISSION_ERROR);
       }
       assetService.deleteAsset(id);
       APIResult result = new APIResult();
       result.setStatus(APIResult.Status.SUCCESS);
       return Response.ok(result).build();
-    } catch (Exception e) {
-      throw new ServiceFormattedException(e);
+    } catch (WfmWebException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new WfmWebException(ex);
     }
   }
-
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/ErrorCode.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/ErrorCode.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/ErrorCode.java
new file mode 100644
index 0000000..6495d2c
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/ErrorCode.java
@@ -0,0 +1,58 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.oozie.ambari.view.exception;
+
+public enum ErrorCode {
+  OOZIE_SUBMIT_ERROR("error.oozie.submit", "Submitting job to Oozie failed. Please check your definition/configuration.",true),
+  FILE_ACCESS_ACL_ERROR("error.file.access.control", "Access Error to file due to access control", true),
+  FILE_ACCESS_UNKNOWN_ERROR("error.file.access", "Error accessing file"),
+  WORKFLOW_PATH_EXISTS("error.workflow.path.exists", "File exists", true),
+  WORKFLOW_XML_DOES_NOT_EXIST("error.workflow.xml.not.exists", "File does not exist", true),
+  INVALID_ASSET_INPUT("error.invalid.asset.input", "Invalid asset definition", true),
+  INVALID_EMPTY_INPUT("error.invalid.empty.input", "Input path cannot be empty", true),
+  ASSET_NOT_EXIST("error.asset.not.exist","Asset doesn\u2019t exist",true),
+  PERMISSION_ERROR("error.permission","Don\u2019t have permission",true),
+  ASSET_INVALID_FROM_OOZIE("error.oozie.asset.invalid","Invalid Asset Definition",true);
+
+  private String errorCode;
+  private String description;
+  private boolean isInputError = false;
+
+  ErrorCode(String errorCode, String description) {
+    this.errorCode = errorCode;
+    this.description = description;
+  }
+
+  ErrorCode(String errorCode, String description, boolean isInputError) {
+    this.errorCode = errorCode;
+    this.description = description;
+    this.isInputError = isInputError;
+  }
+
+  public String getErrorCode() {
+    return errorCode;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public boolean isInputError() {
+    return isInputError;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/WfmException.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/WfmException.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/WfmException.java
new file mode 100644
index 0000000..36ebec8
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/WfmException.java
@@ -0,0 +1,46 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.oozie.ambari.view.exception;
+
+
+public class WfmException extends RuntimeException {
+  private  ErrorCode errorCode;
+
+  public WfmException(ErrorCode errorCode) {
+    this.errorCode = errorCode;
+  }
+
+  public WfmException(String message, ErrorCode errorCode) {
+    super(message);
+    this.errorCode = errorCode;
+  }
+
+  public WfmException(String message, Throwable cause, ErrorCode errorCode) {
+    super(message, cause);
+    this.errorCode = errorCode;
+  }
+
+  public WfmException(Throwable cause, ErrorCode errorCode) {
+    super(cause);
+    this.errorCode = errorCode;
+  }
+
+  public ErrorCode getErrorCode() {
+    return errorCode;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/WfmWebException.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/WfmWebException.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/WfmWebException.java
new file mode 100644
index 0000000..fd4ce80
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/exception/WfmWebException.java
@@ -0,0 +1,115 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.oozie.ambari.view.exception;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.hadoop.security.AccessControlException;
+import org.json.simple.JSONObject;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.util.HashMap;
+
+public class WfmWebException extends WebApplicationException {
+  private static final int STATUS = 500;
+  private ErrorCode errorCode;
+  private String additionalDetail = null;
+  private String message;
+  public WfmWebException(String message) {
+    super();
+    setMessage(message);
+  }
+
+  private void setMessage(String message) {
+    this.message=message;
+  }
+
+  public WfmWebException(Throwable cause) {
+    super(cause);
+  }
+
+  public WfmWebException(ErrorCode errorCode) {
+    super();
+    setMessage(errorCode.getDescription());
+    this.errorCode = errorCode;
+  }
+
+  public WfmWebException(String message, Throwable cause) {
+    super(cause);
+    setMessage(message);
+  }
+
+  public WfmWebException(String message, ErrorCode errorCode) {
+    super();
+    setMessage(message);
+    this.errorCode = errorCode;
+  }
+
+  public WfmWebException(String message, Throwable cause, ErrorCode errorCode) {
+    super(cause);
+    setMessage(message);
+    this.errorCode = errorCode;
+  }
+
+  public WfmWebException(Throwable cause, ErrorCode errorCode) {
+    super(cause);
+    setMessage(errorCode.getDescription());
+    this.errorCode = errorCode;
+  }
+
+
+  public void setAdditionalDetail(String additionalDetail) {
+    this.additionalDetail = additionalDetail;
+  }
+
+  @Override
+  public Response getResponse() {
+    HashMap<String, Object> response = new HashMap<String, Object>();
+    String trace = null;
+    Throwable ex = this.getCause();
+    if (ex != null) {
+      trace = ExceptionUtils.getStackTrace(ex);
+      if (ex instanceof AccessControlException) {
+        errorCode = ErrorCode.FILE_ACCESS_ACL_ERROR;
+      } else if (ex instanceof IOException) {
+        errorCode = ErrorCode.FILE_ACCESS_UNKNOWN_ERROR;
+      }
+    }else{
+      trace = ExceptionUtils.getStackTrace(this);
+    }
+    response.put("stackTrace", trace);
+    int status = errorCode != null && errorCode.isInputError() ? Response.Status.BAD_REQUEST.getStatusCode() : STATUS;
+    if (errorCode != null) {
+      response.put("errorCode", errorCode.getErrorCode());
+      response.put("message", errorCode.getDescription());
+    } else {
+      response.put("message", this.getMessage());
+    }
+    if (this.additionalDetail != null) {
+      response.put("additionalDetail", additionalDetail);
+    }
+    return Response.status(status).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON).build();
+  }
+
+  @Override
+  public String getMessage() {
+    return message;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsManagerResource.java
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsManagerResource.java b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsManagerResource.java
index a0aa234..e1a5808 100644
--- a/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsManagerResource.java
+++ b/contrib/views/wfmanager/src/main/java/org/apache/oozie/ambari/view/workflowmanager/WorkflowsManagerResource.java
@@ -26,8 +26,10 @@ import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
 
 import org.apache.ambari.view.ViewContext;
+import org.apache.oozie.ambari.view.exception.WfmWebException;
 
 public class WorkflowsManagerResource {
 	private final WorkflowManagerService workflowManagerService;
@@ -38,19 +40,27 @@ public class WorkflowsManagerResource {
 		this.workflowManagerService=new WorkflowManagerService(viewContext);
 	}
 
-	@GET
-	public Map<String,Object> getWorkflows(){
-	    HashMap<String,Object> result=new HashMap<>();
-	    result.put("wfprojects", workflowManagerService.getAllWorkflows(viewContext.getUsername()));
-	    return result;
-	}
-	
-	
-	@DELETE
+  @GET
+  public Response getWorkflows() {
+    try {
+      HashMap<String, Object> result = new HashMap<>();
+      result.put("wfprojects", workflowManagerService.getAllWorkflows(viewContext.getUsername()));
+      return Response.ok(result).build();
+    } catch (Exception ex) {
+      throw new WfmWebException(ex);
+    }
+  }
+
+
+  @DELETE
 	@Path("/{projectId}")
-	public void deleteWorkflow( @PathParam("projectId") String id,
-            @DefaultValue("false") @QueryParam("deleteDefinition") Boolean deleteDefinition){
-	    workflowManagerService.deleteWorkflow(id,deleteDefinition);
+	public Response deleteWorkflow(@PathParam("projectId") String id,
+                                 @DefaultValue("false") @QueryParam("deleteDefinition") Boolean deleteDefinition){
+	  try{
+      workflowManagerService.deleteWorkflow(id,deleteDefinition);
+      return Response.ok().build();
+    }catch (Exception ex) {
+      throw new WfmWebException(ex);
+    }
 	}
-	
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-config.js
index 3ccbc07..e94d51a 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-config.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/bundle-config.js
@@ -156,10 +156,12 @@ export default Ember.Component.extend(Ember.Evented, Validations, {
       }
       this.set('bundleFilePath', filePath);
       this.set("isImporting", false);
-    }.bind(this)).catch(function(e){
+    }.bind(this)).catch(function(data){
+      console.error(data);
+      this.set("errorMsg", "There is some problem while importing.");
       this.set("isImporting", false);
       this.set("isImportingSuccess", false);
-      throw new Error(e);
+      this.set("data", data);
     }.bind(this));
   },
   getBundleFromJSON(draftBundle){
@@ -363,7 +365,7 @@ export default Ember.Component.extend(Ember.Evented, Validations, {
       }.bind(this)).catch(function(e){
         this.$('#loading').hide();
         this.get("errors").pushObject({'message' : 'Could not process coordinator from ' + e.path});
-        throw new Error(e.trace);
+        throw new Error(e);
       }.bind(this));
     },
     preview(){

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-config.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-config.js
index bbd619d..4a57e37 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-config.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/coord-config.js
@@ -274,10 +274,12 @@ export default Ember.Component.extend(Validations, Ember.Evented, {
       }
       this.set('coordinatorFilePath', filePath);
       this.set("isImporting", false);
-    }.bind(this)).catch(function(e){
+    }.bind(this)).catch(function(data){
+      console.error(data);
+      this.set("errorMsg", "There is some problem while importing.");
       this.set("isImporting", false);
       this.set("isImportingSuccess", false);
-      throw new Error(e);
+      this.set("data", data);
     }.bind(this));
   },
   getCoordinatorFromJSON(draftCoordinator){

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/components/designer-errors.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/designer-errors.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/designer-errors.js
index 7a7c38d..fdb4f5e 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/designer-errors.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/designer-errors.js
@@ -18,4 +18,53 @@
 import Ember from 'ember';
 
 export default Ember.Component.extend({
+  showingStackTrace: false,
+  hasErrorMsg : Ember.computed('errorMsg', function() {
+    return !Ember.isBlank(this.get("errorMsg"));
+  }),
+  errorMsgDetails : Ember.computed('data.responseText', function() {
+    var jsonResponse = this.getparsedResponse();
+    if (jsonResponse.message) {
+      if (jsonResponse.message.indexOf('Permission denied') >= 0) {
+        return "Permission Denied";
+      }
+      return jsonResponse.message;
+    }
+    return "";
+  }),
+  stackTrace : Ember.computed('data.responseText', function() {
+      var jsonResponse = this.getparsedResponse();
+      var stackTraceMsg = jsonResponse.stackTrace;
+      if(!stackTraceMsg){
+        return "";
+      }
+      if (stackTraceMsg instanceof Array) {
+        return stackTraceMsg.join("").replace(/\tat /g, '&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;');
+      } else {
+        return stackTraceMsg.replace(/\tat /g, '<br/>&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;');
+      }
+  }),
+  isStackTraceAvailable : Ember.computed('stackTrace', function(){
+    return this.get('stackTrace') && this.get('stackTrace').length ? true : false;
+  }),
+  getparsedResponse() {
+    var response = this.get('data.responseText');
+    if (response) {
+      try {
+        return JSON.parse(response);
+      } catch(err){
+        return "";
+      }
+    }
+    return "";
+  },
+
+  actions: {
+    showStackTrace(){
+      this.set("showingStackTrace", !this.get("showingStackTrace"));
+    },
+    closeStackTrace(){
+      this.set("showingStackTrace", false);
+    }
+  }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js
index f97add8..de72c6d 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js
@@ -94,13 +94,13 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
   isWorkflowImporting: false,
   isAssetPublishing: false,
   errorMsg: "",
+  data : {
+    "responseText": ""
+  },
   shouldPersist : false,
   useCytoscape: Constants.useCytoscape,
   cyOverflow: {},
   clipboard : Ember.computed.alias('clipboardService.clipboard'),
-  isStackTraceVisible: false,
-  isStackTraceAvailable: false,
-  stackTrace:"",
   showingStreamImport:false,
   fileInfo:Ember.Object.create(),
   isDraft: false,
@@ -310,24 +310,6 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
   doValidation(){
     this.validate();
   },
-  getStackTrace(data){
-    if(data){
-     try{
-      var stackTraceMsg = JSON.parse(data).stackTrace;
-      if(!stackTraceMsg){
-        return "";
-      }
-     if(stackTraceMsg instanceof Array){
-       return stackTraceMsg.join("").replace(/\tat /g, '&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;');
-     } else {
-       return stackTraceMsg.replace(/\tat /g, '<br/>&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;');
-     }
-     } catch(err){
-       return "";
-     }
-    }
-    return "";
-  },
   importWorkflow(filePath){
     var self = this;
     this.set("isWorkflowImporting", true);
@@ -343,8 +325,8 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
       this.set("workflowFilePath", filePath);
     }.bind(this)).catch(function(data){
       console.error(data);
-      self.set("errorMsg", "There is some problem while importing.Please try again.");
-      self.showingErrorMsgInDesigner(data);
+      self.set("errorMsg", "There is some problem while importing.");
+      self.set("data", data);
       self.set("isWorkflowImporting", false);
     });
   },
@@ -491,8 +473,8 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
     exportActionNodeXmlDefered.promise.then(function(data){
       self.set("isAssetPublishing", false);
     }.bind(this)).catch(function(data){
-      self.set("errorMsg", "There is some problem while publishing asset. Please try again.");
-      self.showingErrorMsgInDesigner(data);
+      self.set("errorMsg", "There is some problem while publishing asset.");
+      self.set("data", data);
       self.set("isAssetPublishing", false);
     });
 
@@ -739,15 +721,6 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
       this.set("showingWorkflowConfigProps",true);
     }
   },
-  showingErrorMsgInDesigner(data){
-      var self = this, stackTraceMsg = self.getStackTrace(data.responseText);
-      if(stackTraceMsg.length){
-        self.set("stackTrace", stackTraceMsg);
-        self.set("isStackTraceAvailable", true);
-      } else {
-        self.set("isStackTraceAvailable", false);
-      }
-  },
   isDraftExists(path){
     var deferred = Ember.RSVP.defer(), url, self = this;
     if(!path){
@@ -833,12 +806,6 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
       });
       reader.readAsText(file);
     },
-    showStackTrace(){
-      this.set("isStackTraceVisible", true);
-    },
-    hideStackTrace(){
-      this.set("isStackTraceVisible", false);
-    },
     showWorkflowSla (value) {
       this.set('showWorkflowSla', value);
     },
@@ -1029,9 +996,8 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
         actionSettingsXmlDefered.promise.then(function(data){
           this.importActionSettingsFromString(data);
         }.bind(this)).catch(function(data){
-          console.error(data);
-          self.set("errorMsg", "There is some problem while importing asset.Please try again.");
-          self.showingErrorMsgInDesigner(data);
+          self.set("errorMsg", "There is some problem while importing asset.");
+          self.set("data", data);
         });
       }
     },
@@ -1047,9 +1013,8 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
         actionSettingsXmlDefered.promise.then(function(data){
           this.importActionNodeFromString(data);
         }.bind(this)).catch(function(data){
-          console.error(data);
-          self.set("errorMsg", "There is some problem while importing asset. Please try again.");
-          self.showingErrorMsgInDesigner(data);
+          self.set("errorMsg", "There is some problem while importing asset.");
+          self.set("data", data);
         });
       }
     },
@@ -1184,9 +1149,9 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
       saveAssetConfigDefered.promise.then(function(data){
         self.set("isAssetPublishing", false);
       }.bind(this)).catch(function(data){
+        self.set("errorMsg", "There is some problem while saving asset.");
+        self.set("data", data);
         self.set("isAssetPublishing", false);
-        self.set("errorMsg", "There is some problem while saving asset. Please try again.");
-        self.showingErrorMsgInDesigner(data);
       });
     },
     showAssetList(value) {
@@ -1204,9 +1169,9 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
         self.importActionSettingsFromString(importedAsset.definition);
         self.set("isAssetImporting", false);
       }.bind(this)).catch(function(data){
+        self.set("errorMsg", "There is some problem while importing asset.");
+        self.set("data", data);
         self.set("isAssetImporting", false);
-        self.set("errorMsg", "There is some problem while importing asset. Please try again.");
-        self.showingErrorMsgInDesigner(data);
       });
     },
     showAssetNodeList(value) {
@@ -1224,9 +1189,9 @@ export default Ember.Component.extend(FindNodeMixin, Validations, {
         self.importActionNodeFromString(importedAsset.definition);
         self.set("isAssetImporting", false);
       }.bind(this)).catch(function(data){
+        self.set("errorMsg", "There is some problem while importing asset.");
+        self.set("data", data);
         self.set("isAssetImporting", false);
-        self.set("errorMsg", "There is some problem while importing asset. Please try again.");
-        self.showingErrorMsgInDesigner(data);
       });
     }
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/components/stack-trace-dialog.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/stack-trace-dialog.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/stack-trace-dialog.js
new file mode 100644
index 0000000..dd96510
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/stack-trace-dialog.js
@@ -0,0 +1,26 @@
+/*
+*    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.
+*/
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  initialize: function(){
+    this.$('#stack_trace_dialog').modal('show');
+    this.$('#stack_trace_dialog').modal().on('hidden.bs.modal', function() {
+      this.sendAction('closeStackTrace');
+    }.bind(this));
+  }.on('didInsertElement'),
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.less
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.less b/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.less
index e98d182..a424049 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.less
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/styles/app.less
@@ -605,9 +605,10 @@ input:invalid {
 #configureJob .modal-dialog,
 #asset-delete-confirm-dialog .modal-dialog,
 #projectsList .modal-dialog,
-#previewModal .modal-dialog {
+#previewModal .modal-dialog,
+#stack_trace_dialog .modal-dialog {
     width: @modalDialogWidth;
-height: 100vh;
+    height: 100vh;
 }
 
 #collapseOne{
@@ -1547,15 +1548,11 @@ height: 100vh;
   padding-left: 0px;
   padding-right: 0px;
 }
-#stackTrace{
-  white-space: pre-wrap;
-  max-width: 100%;
-  max-height: 400px;
-  overflow: scroll;
-}
+
 .jobIdClass {
   width: 50px;
 }
+
 .width50 {
     white-space: nowrap;
     width: 150px;

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/bundle-config.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/bundle-config.hbs b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/bundle-config.hbs
index ca58431..2d374a5 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/bundle-config.hbs
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/bundle-config.hbs
@@ -72,7 +72,7 @@
     <div id='loading'>
       {{spin-spinner lines=13 length=20 width=10}}
     </div>
-    {{designer-errors errors=errors}}
+    {{designer-errors errors=errors validationErrors=validationErrors errorMsg=errorMsg data=data}}
     <form class="form-horizontal">
       <div class="col-sm-12 paddingtop10">
         <div class="col-sm-8 centralize-panel">
@@ -100,7 +100,7 @@
                   <li class="list-group-item">No Coordinators Configured.</li>
                   {{/each}}
                 </ul>
-                {{#field-error model=this field='bundle.coordinators' showErrorMessage=true}}{{/field-error}}
+                {{#field-error model=this field='bundle.coordinators' showErrorMessage=showErrorMessage}}{{/field-error}}
 
               {{#if coordinatorCreateMode}}
               {{#bundle-coord-config coordinator=currentCoordinator openTab="openTab" openFileBrowser="openFileBrowser" add="addCoordinator" cancel="cancelCoordinatorOperation" createMode=coordinatorCreateMode}}{{/bundle-coord-config}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/coord-config.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/coord-config.hbs b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/coord-config.hbs
index 7b607ca..7db5ce2 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/coord-config.hbs
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/coord-config.hbs
@@ -93,7 +93,7 @@
   {{spin-spinner lines=13 length=20 width=10}}
 </div>
 <div class="container-fluid">
-  {{designer-errors errors=errors}}
+  {{designer-errors errors=errors validationErrors=validationErrors errorMsg=errorMsg data=data}}
   <form class="form-horizontal">
     <div class="col-sm-12 paddingtop10">
       <div class="col-sm-8 centralize-panel">

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/designer-errors.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/designer-errors.hbs b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/designer-errors.hbs
index 8438255..00cb8a6 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/designer-errors.hbs
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/designer-errors.hbs
@@ -34,4 +34,21 @@
       {{/if}}
     </div>
   {{/if}}
+
+  {{#if hasErrorMsg}}
+    <div id="loader">
+        <div id="alert"class="alert alert-danger alert-dismissible workflow-error" role="alert">
+            {{errorMsg}}
+            <div id="errorMsgDetails">
+              {{errorMsgDetails}}
+              {{#if isStackTraceAvailable}}
+                <a href="#" class="action-link" {{action "showStackTrace"}}>Details</a>
+              {{/if}}
+            </div>
+        </div>
+    </div>
+  {{/if}}
 </div>
+{{#if showingStackTrace}}
+  {{#stack-trace-dialog title="Stack Trace" stackTrace=stackTrace closeStackTrace="closeStackTrace"}}{{/stack-trace-dialog}}
+{{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/flow-designer.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/flow-designer.hbs b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/flow-designer.hbs
index 1a73421..4ff9d87 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/flow-designer.hbs
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/flow-designer.hbs
@@ -138,7 +138,7 @@
   {{/if}}
   <div  id="content" class="panel panel-default designer-main-panel col-xs-20">
     <div class="designer-panel designer-canvas">
-      {{designer-errors errors=errors validationErrors=validationErrors}}
+      {{designer-errors errors=errors validationErrors=validationErrors errorMsg=errorMsg data=data}}
       {{#if undoAvailable}}
         <div id="alert"class="alert alert-warning workflow-error" role="alert">
           {{#if (eq undoType 'nodeDeleted')}}
@@ -157,22 +157,6 @@
           </div>
       </div>
       {{/if}}
-      {{#if (not (eq errorMsg ""))}}
-        <div id="loader">
-            <div id="alert"class="alert alert-danger alert-dismissible workflow-error" role="alert">
-                {{errorMsg}}
-                {{#if isStackTraceAvailable}}
-                  {{#if isStackTraceVisible}}
-                    <a href="#" class="action-link" {{action "hideStackTrace"}}>Hide Log</a>
-                    <div id="stackTrace">{{{stackTrace}}}</div>
-                  {{/if}}
-                  {{#unless isStackTraceVisible}}
-                    <a href="#" class="action-link" {{action "showStackTrace"}}>Show Log</a>
-                  {{/unless}}
-                {{/if}}
-            </div>
-        </div>
-      {{/if}}
       {{#if isAssetPublishing}}
         <div id="loader">
             <div id="alert"class="alert alert-info alert-dismissible workflow-error" role="alert">

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/stack-trace-dialog.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/stack-trace-dialog.hbs b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/stack-trace-dialog.hbs
new file mode 100644
index 0000000..b82726b
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/templates/components/stack-trace-dialog.hbs
@@ -0,0 +1,33 @@
+{{!
+* 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.
+}}
+<div id="stack_trace_dialog" class="modal fade" role="dialog">
+  <div class="modal-dialog">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal">&times;</button>
+        <h4 class="modal-title">{{title}}</h4>
+      </div>
+      <div class="modal-body">
+        <div id="stackTrace">{{{stackTrace}}}</div>
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/9fdeec1a/contrib/views/wfmanager/src/main/resources/ui/tests/integration/components/stack-trace-dialog-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/tests/integration/components/stack-trace-dialog-test.js b/contrib/views/wfmanager/src/main/resources/ui/tests/integration/components/stack-trace-dialog-test.js
new file mode 100644
index 0000000..64e3f90
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/tests/integration/components/stack-trace-dialog-test.js
@@ -0,0 +1,40 @@
+/*
+ *    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.
+ */
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('stack-trace-dialog', 'Integration | Component | stack trace dialog', {
+  integration: true
+});
+
+test('it renders', function(assert) {
+  // Set any properties with this.set('myProperty', 'value');
+  // Handle any actions with this.on('myAction', function(val) { ... });"
+
+  this.render(hbs`{{stack-trace-dialog}}`);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:"
+  this.render(hbs`
+    {{#stack-trace-dialog}}
+      template block text
+    {{/stack-trace-dialog}}
+  `);
+
+  assert.equal(this.$().text().trim(), 'template block text');
+});