瀏覽代碼

AMBARI-6836. View: Files property checking should log more explicit failure.

Mahadev Konar 10 年之前
父節點
當前提交
221f0e511c

+ 121 - 80
contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/DownloadService.java

@@ -46,6 +46,8 @@ import javax.ws.rs.core.UriInfo;
 import javax.xml.bind.annotation.XmlElement;
 
 import com.google.gson.Gson;
+import org.apache.ambari.view.filebrowser.utils.NotFoundFormattedException;
+import org.apache.ambari.view.filebrowser.utils.ServiceFormattedException;
 import org.apache.hadoop.fs.FSDataInputStream;
 import org.apache.hadoop.fs.FileStatus;
 import org.apache.ambari.view.ViewContext;
@@ -89,17 +91,16 @@ public class DownloadService extends HdfsService {
             "filename=\"" + status.getPath().getName() + "\"").type(mimeType);
       }
       return result.build();
+    } catch (WebApplicationException ex) {
+      throw ex;
     } catch (FileNotFoundException ex) {
-      return Response.ok(Response.Status.NOT_FOUND.getStatusCode())
-          .entity(ex.getMessage()).build();
+      throw new NotFoundFormattedException(ex.getMessage(), ex);
     } catch (Exception ex) {
-      return Response.ok(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode())
-          .entity(ex.getMessage()).build();
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
   }
 
-  private void zipFile(ZipOutputStream zip, String path)
-      throws InterruptedException, Exception {
+  private void zipFile(ZipOutputStream zip, String path) throws InterruptedException, IOException {
     try {
       zip.putNextEntry(new ZipEntry(path.substring(1)));
       FSDataInputStream in = getApi(context).open(path);
@@ -108,22 +109,25 @@ public class DownloadService extends HdfsService {
         zip.write(chunk);
       }
     } catch (IOException ex) {
-      logger.error("Error zipping file " + path.substring(1) + ": "
-          + ex.getMessage());
+      String msg = "Error zipping file " + path.substring(1) + ": "
+          + ex.getMessage();
+      logger.error(msg);
       zip.write(ex.getMessage().getBytes());
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     } finally {
       zip.closeEntry();
     }
-
   }
 
   private void zipDirectory(ZipOutputStream zip, String path) {
     try {
       zip.putNextEntry(new ZipEntry(path.substring(1) + "/"));
       zip.closeEntry();
-    } catch (IOException e) {
-      logger.error("Error zipping directory " + path.substring(1) + "/" + ": "
-          + e.getMessage());
+    } catch (IOException ex) {
+      String msg = "Error zipping directory " + path.substring(1) + "/" + ": "
+          + ex.getMessage();
+      logger.error(msg);
+      throw new ServiceFormattedException(msg, ex);
     }
   }
 
@@ -137,40 +141,47 @@ public class DownloadService extends HdfsService {
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_OCTET_STREAM)
   public Response downloadGZip(final DownloadRequest request) {
-    StreamingOutput result = new StreamingOutput() {
-      public void write(OutputStream output) throws IOException,
-          WebApplicationException {
-        ZipOutputStream zip = new ZipOutputStream(output);
-        try {
-          HdfsApi api = getApi(context);
-          Queue<String> files = new LinkedList<String>();
-          for (String file : request.entries) {
-            files.add(file);
-          }
-          while (!files.isEmpty()) {
-            String path = files.poll();
-            FileStatus status = api.getFileStatus(path);
-            if (status.isDirectory()) {
-              FileStatus[] subdir = api.listdir(path);
-              for (FileStatus file : subdir) {
-                files.add(org.apache.hadoop.fs.Path
-                    .getPathWithoutSchemeAndAuthority(file.getPath())
-                    .toString());
+    try {
+      StreamingOutput result = new StreamingOutput() {
+        public void write(OutputStream output) throws IOException,
+            ServiceFormattedException {
+          ZipOutputStream zip = new ZipOutputStream(output);
+          try {
+            HdfsApi api = getApi(context);
+            Queue<String> files = new LinkedList<String>();
+            for (String file : request.entries) {
+              files.add(file);
+            }
+            while (!files.isEmpty()) {
+              String path = files.poll();
+              FileStatus status = api.getFileStatus(path);
+              if (status.isDirectory()) {
+                FileStatus[] subdir = api.listdir(path);
+                for (FileStatus file : subdir) {
+                  files.add(org.apache.hadoop.fs.Path
+                      .getPathWithoutSchemeAndAuthority(file.getPath())
+                      .toString());
+                }
+                zipDirectory(zip, path);
+              } else {
+                zipFile(zip, path);
               }
-              zipDirectory(zip, path);
-            } else {
-              zipFile(zip, path);
             }
+          } catch (Exception ex) {
+            logger.error("Error occurred: " + ex.getMessage());
+            throw new ServiceFormattedException(ex.getMessage(), ex);
+          } finally {
+            zip.close();
           }
-        } catch (Exception ex) {
-          logger.error("Error occured: " + ex.getMessage());
-        } finally {
-          zip.close();
         }
-      }
-    };
-    return Response.ok(result)
-        .header("Content-Disposition", "inline; filename=\"hdfs.zip\"").build();
+      };
+      return Response.ok(result)
+          .header("Content-Disposition", "inline; filename=\"hdfs.zip\"").build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
@@ -183,33 +194,39 @@ public class DownloadService extends HdfsService {
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_OCTET_STREAM)
   public Response concat(final DownloadRequest request) {
-    StreamingOutput result = new StreamingOutput() {
-      public void write(OutputStream output) throws IOException,
-          WebApplicationException {
-        FSDataInputStream in = null;
-        for (String path : request.entries) {
-          try {
-            in = getApi(context).open(path);
-            byte[] chunk = new byte[1024];
-            while (in.read(chunk) != -1) {
-              output.write(chunk);
+    try {
+      StreamingOutput result = new StreamingOutput() {
+        public void write(OutputStream output) throws IOException,
+            ServiceFormattedException {
+          FSDataInputStream in = null;
+          for (String path : request.entries) {
+            try {
+              in = getApi(context).open(path);
+              byte[] chunk = new byte[1024];
+              while (in.read(chunk) != -1) {
+                output.write(chunk);
+              }
+            } catch (Exception ex) {
+              throw new ServiceFormattedException(ex.getMessage(), ex);
+            } finally {
+              if (in != null)
+                in.close();
             }
-          } catch (Exception ex) {
-            ex.printStackTrace();
-          } finally {
-            if (in != null)
-              in.close();
           }
         }
+      };
+      ResponseBuilder response = Response.ok(result);
+      if (request.download) {
+        response.header("Content-Disposition", "inline; filename=\"concatResult.txt\"").type(MediaType.APPLICATION_OCTET_STREAM);
+      } else {
+        response.header("Content-Disposition", "filename=\"concatResult.txt\"").type(MediaType.TEXT_PLAIN);
       }
-    };
-    ResponseBuilder response = Response.ok(result);
-    if (request.download){
-      response.header("Content-Disposition", "inline; filename=\"concatResult.txt\"").type(MediaType.APPLICATION_OCTET_STREAM);
-    } else {
-      response.header("Content-Disposition", "filename=\"concatResult.txt\"").type(MediaType.TEXT_PLAIN);
+      return response.build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return response.build();
   }
 
   // ===============================
@@ -225,10 +242,16 @@ public class DownloadService extends HdfsService {
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_OCTET_STREAM)
   public Response zipByRequestId(@QueryParam("requestId") String requestId) {
-    String json = context.getInstanceData(requestId);
-    DownloadRequest request = gson.fromJson(json, DownloadRequest.class);
-    context.removeInstanceData(requestId);
-    return downloadGZip(request);
+    try {
+      String json = context.getInstanceData(requestId);
+      DownloadRequest request = gson.fromJson(json, DownloadRequest.class);
+      context.removeInstanceData(requestId);
+      return downloadGZip(request);
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
@@ -242,10 +265,16 @@ public class DownloadService extends HdfsService {
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   public Response zipGenerateLink(final DownloadRequest request) {
-    String requestId = generateUniqueIdentifer(request);
-    JSONObject json = new JSONObject();
-    json.put("requestId", requestId);
-    return Response.ok(json).build();
+    try {
+      String requestId = generateUniqueIdentifer(request);
+      JSONObject json = new JSONObject();
+      json.put("requestId", requestId);
+      return Response.ok(json).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
@@ -258,10 +287,16 @@ public class DownloadService extends HdfsService {
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_OCTET_STREAM)
   public Response concatByRequestId(@QueryParam("requestId") String requestId) {
-    String json = context.getInstanceData(requestId);
-    DownloadRequest request = gson.fromJson(json, DownloadRequest.class);
-    context.removeInstanceData(requestId);
-    return concat(request);
+    try {
+      String json = context.getInstanceData(requestId);
+      DownloadRequest request = gson.fromJson(json, DownloadRequest.class);
+      context.removeInstanceData(requestId);
+      return concat(request);
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
@@ -275,10 +310,16 @@ public class DownloadService extends HdfsService {
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   public Response concatGenerateLink(final DownloadRequest request) {
-    String requestId = generateUniqueIdentifer(request);
-    JSONObject json = new JSONObject();
-    json.put("requestId", requestId);
-    return Response.ok(json).build();
+    try {
+      String requestId = generateUniqueIdentifer(request);
+      JSONObject json = new JSONObject();
+      json.put("requestId", requestId);
+      return Response.ok(json).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   private Gson gson = new Gson();

+ 92 - 77
contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java

@@ -19,16 +19,8 @@
 package org.apache.ambari.view.filebrowser;
 
 import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
+
+import javax.ws.rs.*;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
@@ -39,6 +31,8 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.filebrowser.utils.NotFoundFormattedException;
+import org.apache.ambari.view.filebrowser.utils.ServiceFormattedException;
 
 /**
  * File operations service
@@ -57,20 +51,20 @@ public class FileOperationService extends HdfsService {
    * List dir
    * @param path path
    * @return response with dir content
-   * @throws Exception
    */
   @GET
   @Path("/listdir")
   @Produces(MediaType.APPLICATION_JSON)
-  public Response listdir(@QueryParam("path") String path) throws Exception {
+  public Response listdir(@QueryParam("path") String path) {
     try {
       return Response.ok(
           HdfsApi.fileStatusToJSON(getApi(context).listdir(path))).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
     } catch (FileNotFoundException ex) {
-      return Response.ok(Response.Status.NOT_FOUND.getStatusCode())
-          .entity(ex.getMessage()).build();
-    } catch (Throwable ex) {
-      throw new Exception(ex.getMessage());
+      throw new NotFoundFormattedException(ex.getMessage(), ex);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
   }
 
@@ -78,131 +72,152 @@ public class FileOperationService extends HdfsService {
    * Rename
    * @param request rename request
    * @return response with success
-   * @throws IOException
-   * @throws Exception
    */
   @POST
   @Path("/rename")
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
-  public Response rename(final SrcDstFileRequest request) throws IOException,
-      Exception {
-    HdfsApi api = getApi(context);
-    ResponseBuilder result;
-    if (api.rename(request.src, request.dst)) {
-      result = Response.ok(HdfsApi.fileStatusToJSON(api
-          .getFileStatus(request.dst)));
-    } else {
-      result = Response.ok(new BoolResult(false)).status(422);
+  public Response rename(final SrcDstFileRequest request) {
+    try {
+      HdfsApi api = getApi(context);
+      ResponseBuilder result;
+      if (api.rename(request.src, request.dst)) {
+        result = Response.ok(HdfsApi.fileStatusToJSON(api
+            .getFileStatus(request.dst)));
+      } else {
+        result = Response.ok(new BoolResult(false)).status(422);
+      }
+      return result.build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return result.build();
   }
 
   /**
    * Copy file
    * @param request source and destination request
    * @return response with success
-   * @throws IOException
-   * @throws Exception
    */
   @POST
   @Path("/copy")
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   public Response copy(final SrcDstFileRequest request,
-                       @Context HttpHeaders headers, @Context UriInfo ui) throws IOException,
-      Exception {
-    HdfsApi api = getApi(context);
-    ResponseBuilder result;
-    if (api.copy(request.src, request.dst)) {
-      result = Response.ok(HdfsApi.fileStatusToJSON(api
-          .getFileStatus(request.dst)));
-    } else {
-      result = Response.ok(new BoolResult(false)).status(422);
+                       @Context HttpHeaders headers, @Context UriInfo ui) {
+    try {
+      HdfsApi api = getApi(context);
+      ResponseBuilder result;
+      if (api.copy(request.src, request.dst)) {
+        result = Response.ok(HdfsApi.fileStatusToJSON(api
+            .getFileStatus(request.dst)));
+      } else {
+        result = Response.ok(new BoolResult(false)).status(422);
+      }
+      return result.build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return result.build();
   }
 
   /**
    * Make directory
    * @param request make directory request
    * @return response with success
-   * @throws IOException
-   * @throws Exception
    */
   @PUT
   @Path("/mkdir")
   @Produces(MediaType.APPLICATION_JSON)
-  public Response mkdir(final MkdirRequest request) throws IOException,
-      Exception {
-    HdfsApi api = getApi(context);
-    ResponseBuilder result;
-    if (api.mkdir(request.path)) {
-      result = Response.ok(HdfsApi.fileStatusToJSON(api.getFileStatus(request.path)));
-    } else {
-      result = Response.ok(new BoolResult(false)).status(422);
+  public Response mkdir(final MkdirRequest request) {
+    try{
+      HdfsApi api = getApi(context);
+      ResponseBuilder result;
+      if (api.mkdir(request.path)) {
+        result = Response.ok(HdfsApi.fileStatusToJSON(api.getFileStatus(request.path)));
+      } else {
+        result = Response.ok(new BoolResult(false)).status(422);
+      }
+      return result.build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return result.build();
   }
 
   /**
    * Empty trash
    * @return response with success
-   * @throws IOException
-   * @throws Exception
    */
   @DELETE
   @Path("/trash/emptyTrash")
   @Produces(MediaType.APPLICATION_JSON)
-  public Response emptyTrash() throws IOException, Exception {
-    HdfsApi api = getApi(context);
-    api.emptyTrash();
-    return Response.ok(new BoolResult(true)).build();
+  public Response emptyTrash() {
+    try {
+      HdfsApi api = getApi(context);
+      api.emptyTrash();
+      return Response.ok(new BoolResult(true)).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
    * Move to trash
    * @param request remove request
    * @return response with success
-   * @throws IOException
-   * @throws Exception
    */
   @DELETE
   @Path("/moveToTrash")
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
-  public Response moveToTrash(RemoveRequest request) throws IOException, Exception {
-    HdfsApi api = getApi(context);
-    ResponseBuilder result;
-    if (api.moveToTrash(request.path)){
-      result = Response.ok(new BoolResult(true)).status(204);
-    } else {
-      result = Response.ok(new BoolResult(false)).status(422);
+  public Response moveToTrash(RemoveRequest request) {
+    try {
+      HdfsApi api = getApi(context);
+      ResponseBuilder result;
+      if (api.moveToTrash(request.path)){
+        result = Response.ok(new BoolResult(true)).status(204);
+      } else {
+        result = Response.ok(new BoolResult(false)).status(422);
+      }
+      return result.build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return result.build();
   }
 
   /**
    * Remove
    * @param request remove request
    * @return response with success
-   * @throws IOException
-   * @throws Exception
    */
   @DELETE
   @Path("/remove")
   @Consumes(MediaType.APPLICATION_JSON)
   @Produces(MediaType.APPLICATION_JSON)
   public Response remove(RemoveRequest request, @Context HttpHeaders headers,
-                         @Context UriInfo ui) throws IOException, Exception {
-    HdfsApi api = getApi(context);
-    ResponseBuilder result;
-    if (api.delete(request.path, request.recursive)){
-      result = Response.ok(new BoolResult(true)).status(204);
-    } else {
-      result = Response.ok(new BoolResult(false)).status(422);
+                         @Context UriInfo ui) {
+    try {
+      HdfsApi api = getApi(context);
+      ResponseBuilder result;
+      if (api.delete(request.path, request.recursive)) {
+        result = Response.ok(new BoolResult(true)).status(204);
+      } else {
+        result = Response.ok(new BoolResult(false)).status(422);
+      }
+      return result.build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return result.build();
   }
 
   /**

+ 11 - 7
contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsService.java

@@ -18,11 +18,11 @@
 
 package org.apache.ambari.view.filebrowser;
 
-import java.io.IOException;
-
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.filebrowser.utils.MisconfigurationFormattedException;
+import org.apache.ambari.view.filebrowser.utils.ServiceFormattedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,13 +60,18 @@ public abstract class HdfsService {
    * Ger HdfsApi instance
    * @param context View Context instance
    * @return HdfsApi business delegate
-   * @throws IOException
-   * @throws Exception
    */
-  public HdfsApi getApi(ViewContext context) throws IOException, Exception {
+  public HdfsApi getApi(ViewContext context) {
     if (_api == null) {
       Thread.currentThread().setContextClassLoader(null);
-      _api = new HdfsApi(context.getProperties().get("dataworker.defaultFs"), getUsername(context));
+      String defaultFs = context.getProperties().get("dataworker.defaultFs");
+      if (defaultFs == null)
+        throw new MisconfigurationFormattedException("dataworker.defaultFs");
+      try {
+        _api = new HdfsApi(defaultFs, getUsername(context));
+      } catch (Exception ex) {
+        throw new ServiceFormattedException("HdfsApi connection failed. Check \"dataworker.defaultFs\" property", ex);
+      }
     }
     return _api;
   }
@@ -82,5 +87,4 @@ public abstract class HdfsService {
       username = context.getUsername();
     return username;
   }
-
 }

+ 30 - 24
contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HelpService.java

@@ -19,19 +19,17 @@
 package org.apache.ambari.view.filebrowser;
 
 import java.io.FileNotFoundException;
-import java.io.IOException;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriInfo;
 
 import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.filebrowser.utils.NotFoundFormattedException;
+import org.apache.ambari.view.filebrowser.utils.ServiceFormattedException;
 
 /**
  * Help service
@@ -83,52 +81,60 @@ public class HelpService extends HdfsService {
   /**
    * Returns home directory
    * @return home directory
-   * @throws Exception
    */
   @GET
   @Path("/home")
   @Produces(MediaType.APPLICATION_JSON)
-  public Response homeDir()
-      throws
-      Exception {
-    HdfsApi api = getApi(context);
-    return Response
-        .ok(HdfsApi.fileStatusToJSON(api.getFileStatus(api.getHomeDir()
-            .toString()))).build();
+  public Response homeDir() {
+    try {
+      HdfsApi api = getApi(context);
+      return Response
+          .ok(HdfsApi.fileStatusToJSON(api.getFileStatus(api.getHomeDir()
+              .toString()))).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
    * Is trash enabled
    * @return is trash enabled
-   * @throws Exception
    */
   @GET
   @Path("/trash/enabled")
   @Produces(MediaType.APPLICATION_JSON)
-  public Response trashEnabled()
-      throws Exception {
-    HdfsApi api = getApi(context);
-    return Response.ok(new BoolResult(api.trashEnabled())).build();
+  public Response trashEnabled() {
+    try {
+      HdfsApi api = getApi(context);
+      return Response.ok(new BoolResult(api.trashEnabled())).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
    * Trash dir
    * @return trash dir
-   * @throws Exception
    */
   @GET
   @Path("/trashDir")
   @Produces(MediaType.APPLICATION_JSON)
-  public Response trashdir()
-      throws Exception {
-    HdfsApi api = getApi(context);
+  public Response trashdir() {
     try {
+      HdfsApi api = getApi(context);
       return Response.ok(
           HdfsApi.fileStatusToJSON(api.getFileStatus(api.getTrashDir()
               .toString()))).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
     } catch (FileNotFoundException ex) {
-      return Response.ok(new BoolResult(false)).status(Status.NOT_FOUND)
-          .build();
+      throw new NotFoundFormattedException(ex.getMessage(), ex);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
   }
 

+ 37 - 28
contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/UploadService.java

@@ -23,14 +23,12 @@ import java.io.InputStream;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
+import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
 import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.filebrowser.utils.ServiceFormattedException;
 import org.apache.hadoop.fs.FSDataOutputStream;
 
 import com.sun.jersey.core.header.FormDataContentDisposition;
@@ -50,7 +48,7 @@ public class UploadService extends HdfsService {
   }
 
   private void uploadFile(final String filePath, InputStream uploadedInputStream)
-      throws IOException, Exception {
+      throws IOException, InterruptedException {
     byte[] chunk = new byte[1024];
     FSDataOutputStream out = getApi(context).create(filePath, false);
     while (uploadedInputStream.read(chunk) != -1) {
@@ -65,7 +63,6 @@ public class UploadService extends HdfsService {
    * @param contentDisposition content disposition
    * @param path path
    * @return file status
-   * @throws Exception
    */
   @PUT
   @Consumes(MediaType.MULTIPART_FORM_DATA)
@@ -73,14 +70,20 @@ public class UploadService extends HdfsService {
   public Response uploadFile(
       @FormDataParam("file") InputStream uploadedInputStream,
       @FormDataParam("file") FormDataContentDisposition contentDisposition,
-      @FormDataParam("path") String path) throws Exception {
-    if (!path.endsWith("/"))
-      path = path + "/";
-    String filePath = path + contentDisposition.getFileName();
-    uploadFile(filePath, uploadedInputStream);
-    return Response.ok(
-        HdfsApi.fileStatusToJSON(getApi(context).getFileStatus(filePath)))
-        .build();
+      @FormDataParam("path") String path) {
+    try {
+      if (!path.endsWith("/"))
+        path = path + "/";
+      String filePath = path + contentDisposition.getFileName();
+      uploadFile(filePath, uploadedInputStream);
+      return Response.ok(
+          HdfsApi.fileStatusToJSON(getApi(context).getFileStatus(filePath)))
+          .build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
@@ -99,22 +102,28 @@ public class UploadService extends HdfsService {
   public Response uploadZip(
       @FormDataParam("file") InputStream uploadedInputStream,
       @FormDataParam("file") FormDataContentDisposition contentDisposition,
-      @FormDataParam("path") String path) throws Exception {
-    if (!path.endsWith("/"))
-      path = path + "/";
-    ZipInputStream zip = new ZipInputStream(uploadedInputStream);
-    ZipEntry ze = zip.getNextEntry();
-    HdfsApi api = getApi(context);
-    while (ze != null) {
-      String filePath = path + ze.getName();
-      if (ze.isDirectory()) {
-        api.mkdir(filePath);
-      } else {
-        uploadFile(filePath, zip);
+      @FormDataParam("path") String path) {
+    try {
+      if (!path.endsWith("/"))
+        path = path + "/";
+      ZipInputStream zip = new ZipInputStream(uploadedInputStream);
+      ZipEntry ze = zip.getNextEntry();
+      HdfsApi api = getApi(context);
+      while (ze != null) {
+        String filePath = path + ze.getName();
+        if (ze.isDirectory()) {
+          api.mkdir(filePath);
+        } else {
+          uploadFile(filePath, zip);
+        }
+        ze = zip.getNextEntry();
       }
-      ze = zip.getNextEntry();
+      return Response.ok(HdfsApi.fileStatusToJSON(api.listdir(path))).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return Response.ok(HdfsApi.fileStatusToJSON(api.listdir(path))).build();
   }
 
 }

+ 43 - 0
contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/utils/MisconfigurationFormattedException.java

@@ -0,0 +1,43 @@
+/**
+ * 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.view.filebrowser.utils;
+
+import org.json.simple.JSONObject;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.HashMap;
+
+public class MisconfigurationFormattedException extends WebApplicationException {
+  private final static int STATUS = 500;
+  private final static String message = "Parameter \"%s\" is set to null";
+
+  public MisconfigurationFormattedException(String name) {
+    super(errorEntity(name));
+  }
+
+  protected static Response errorEntity(String name) {
+    HashMap<String, Object> response = new HashMap<String, Object>();
+    response.put("message", String.format(message, name));
+    response.put("trace", null);
+    response.put("status", STATUS);
+    return Response.status(STATUS).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON).build();
+  }
+}

+ 27 - 0
contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/utils/NotFoundFormattedException.java

@@ -0,0 +1,27 @@
+/**
+ * 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.view.filebrowser.utils;
+
+public class NotFoundFormattedException extends ServiceFormattedException {
+  private final static int STATUS = 404;
+
+  public NotFoundFormattedException(String message, Throwable exception) {
+    super(message, exception, STATUS);
+  }
+}

+ 57 - 0
contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/utils/ServiceFormattedException.java

@@ -0,0 +1,57 @@
+/**
+ * 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.view.filebrowser.utils;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.json.simple.JSONObject;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.security.AccessControlException;
+import java.util.HashMap;
+
+public class ServiceFormattedException extends WebApplicationException {
+  public ServiceFormattedException(String message, Throwable exception) {
+    super(errorEntity(message, exception, suggestStatus(exception)));
+  }
+
+  public ServiceFormattedException(String message, Throwable exception, int status) {
+    super(errorEntity(message, exception, status));
+  }
+
+  private static int suggestStatus(Throwable exception) {
+    int status = 500;
+    if (exception instanceof AccessControlException) {
+      status = 403;
+    }
+    return status;
+  }
+
+  protected static Response errorEntity(String message, Throwable e, int status) {
+    HashMap<String, Object> response = new HashMap<String, Object>();
+    response.put("message", message);
+    String trace = null;
+    if (e != null)
+      trace = ExceptionUtils.getStackTrace(e);
+    response.put("trace", trace);
+    response.put("status", status);
+    return Response.status(status).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON).build();
+  }
+}

+ 50 - 0
contrib/views/files/src/main/resources/ui/app/controllers/error.js

@@ -0,0 +1,50 @@
+/**
+ * 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.
+ */
+
+App.ErrorController = Ember.ObjectController.extend({
+  init:function () {
+    this._super();
+  },
+  actions: {
+    toggleStackTrace:function () {
+      var value = this.get('isExpanded');
+      this.set('isExpanded', !value);
+    },
+  },
+
+  isExpanded: false,
+
+  publicMessage:function () {
+    var content = this.get('content');
+    var text = content.statusText;
+    if (content && content.responseText) {
+      var json = JSON.parse(content.responseText);
+      text = json.message;
+    }
+    return text;
+  }.property('content'),
+  stackTrace:function () {
+    var content = this.get('content');
+    var trace = null;
+    if (content && content.responseText) {
+      var json = JSON.parse(content.responseText);
+      trace = json.trace;
+    }
+    return trace;
+  }.property('content'),
+});

+ 20 - 0
contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js

@@ -0,0 +1,20 @@
+/**
+ * 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.
+ */
+
+App.FilesAlertController = App.ErrorController.extend({
+});

+ 2 - 0
contrib/views/files/src/main/resources/ui/app/initialize.js

@@ -49,6 +49,8 @@ require('models/file');
 
 require('controllers/files');
 require('controllers/file');
+require('controllers/error');
+require('controllers/filesAlert');
 
 /////////////////////////////////
 // Components

+ 19 - 6
contrib/views/files/src/main/resources/ui/app/templates/error.hbs

@@ -6,9 +6,9 @@
    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.
@@ -17,8 +17,21 @@
 }}
 
 <div class="container">
-  <div class="alert alert-danger">
-    <strong>{{content.status}}</strong>
-    {{content.statusText}}
-  </div>
+    <div class="alert alert-danger collapse-group">
+        <strong>{{content.status}}</strong> {{publicMessage}}
+    </div>
+  {{#if stackTrace}}
+      <a href="#" {{action "toggleStackTrace" post}}>
+        {{#if isExpanded}}
+            <span class="glyphicon glyphicon-collapse-down"></span> Collapse Stack Trace
+        {{else}}
+            <span class="glyphicon glyphicon-expand"></span> Expand Stack Trace
+        {{/if}}
+      </a>
+    {{#if isExpanded}}
+        <pre class="prettyprint">
+{{stackTrace}}
+        </pre>
+    {{/if}}
+  {{/if}}
 </div>

+ 17 - 2
contrib/views/files/src/main/resources/ui/app/templates/util/errorRow.hbs

@@ -16,9 +16,24 @@
    limitations under the License.
 }}
 
-<div class="text-center">
+<div>
   <button {{action 'removeAlert'}} type="button" class="close" aria-hidden="true">&times;</button>
-  <strong>{{output.status}} </strong> {{output.message}}
+  <div class="text-center"><strong>{{content.status}} </strong> {{publicMessage}}
+    {{#if stackTrace}}<a href="#" {{action "toggleStackTrace" post}}>
+        {{#if isExpanded}}
+            <span class="glyphicon glyphicon-collapse-down"></span> Collapse Stack Trace
+        {{else}}
+            <span class="glyphicon glyphicon-expand"></span> Expand Stack Trace
+        {{/if}}
+      </a>
+    </div>
+    {{#if isExpanded}}
+        <pre class="prettyprint">
+{{publicMessage}}
+{{stackTrace}}
+        </pre>
+    {{/if}}
+  {{/if}}
 </div>
   
 

+ 1 - 1
contrib/views/files/src/main/resources/ui/config.coffee

@@ -43,7 +43,7 @@ exports.config =
     addSourceURLs: true
 
   paths:
-    public: '/usr/lib/ambari-server/web/views-debug/FILES/FILES_1/'
+    public: '/usr/lib/ambari-server/web/views-debug/FILES/0.1.0/MyFiles/'
 
   overrides:
     production: