Jelajahi Sumber

AMBARI-6121. View: Pig property checking should log more explicit failure.

Siddharth Wagle 11 tahun lalu
induk
melakukan
fbd5235e27
45 mengubah file dengan 1051 tambahan dan 311 penghapusan
  1. 17 0
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/persistence/InstanceKeyValueStorage.java
  2. 2 2
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/persistence/LocalKeyValueStorage.java
  3. 62 24
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/files/FileService.java
  4. 50 17
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/jobs/JobResourceManager.java
  5. 88 59
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/jobs/JobService.java
  6. 7 7
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/scripts/ScriptResourceManager.java
  7. 51 27
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/scripts/ScriptService.java
  8. 50 29
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/udf/UDFService.java
  9. 29 44
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/services/BaseService.java
  10. 53 0
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/services/HelpService.java
  11. 27 0
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/utils/BadRequestFormattedException.java
  12. 26 0
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/utils/HdfsApi.java
  13. 47 0
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/utils/MisconfigurationFormattedException.java
  14. 27 0
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/utils/NotFoundFormattedException.java
  15. 71 0
      contrib/views/pig/src/main/java/org/apache/ambari/view/pig/utils/ServiceFormattedException.java
  16. 16 0
      contrib/views/pig/src/main/resources/ui/pig-web/app/app.js
  17. 5 1
      contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/edit.js
  18. 23 0
      contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/errorLog.js
  19. 1 1
      contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/jobResults.js
  20. 25 18
      contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/pigScriptEdit.js
  21. 5 2
      contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/poll.js
  22. 72 0
      contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/splash.js
  23. 7 1
      contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/util/pigUtilAlert.js
  24. 8 15
      contrib/views/pig/src/main/resources/ui/pig-web/app/initialize.js
  25. 1 1
      contrib/views/pig/src/main/resources/ui/pig-web/app/models/file.js
  26. 1 1
      contrib/views/pig/src/main/resources/ui/pig-web/app/models/pig_script.js
  27. 4 4
      contrib/views/pig/src/main/resources/ui/pig-web/app/router.js
  28. 14 1
      contrib/views/pig/src/main/resources/ui/pig-web/app/routes/pig.js
  29. 4 1
      contrib/views/pig/src/main/resources/ui/pig-web/app/routes/pigJob.js
  30. 4 1
      contrib/views/pig/src/main/resources/ui/pig-web/app/routes/pigScriptEdit.js
  31. 8 3
      contrib/views/pig/src/main/resources/ui/pig-web/app/routes/pigScriptList.js
  32. 8 2
      contrib/views/pig/src/main/resources/ui/pig-web/app/routes/pigUdfs.js
  33. 50 0
      contrib/views/pig/src/main/resources/ui/pig-web/app/routes/splash.js
  34. 7 0
      contrib/views/pig/src/main/resources/ui/pig-web/app/styles/style.less
  35. 23 0
      contrib/views/pig/src/main/resources/ui/pig-web/app/templates/pig/errorLog.hbs
  36. 3 0
      contrib/views/pig/src/main/resources/ui/pig-web/app/templates/pig/util/alert-content.hbs
  37. 96 0
      contrib/views/pig/src/main/resources/ui/pig-web/app/templates/splash.hbs
  38. 4 1
      contrib/views/pig/src/main/resources/ui/pig-web/app/translations.js
  39. 1 1
      contrib/views/pig/src/main/resources/ui/pig-web/config.coffee
  40. 13 6
      contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/FileTest.java
  41. 13 16
      contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/JobTest.java
  42. 8 4
      contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/ScriptTest.java
  43. 7 2
      contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/ScriptTestHDFSUnmanaged.java
  44. 5 16
      contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/ScriptTestUnmanaged.java
  45. 8 4
      contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/UDFTest.java

+ 17 - 0
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/persistence/InstanceKeyValueStorage.java

@@ -20,10 +20,13 @@ package org.apache.ambari.view.pig.persistence;
 
 import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.pig.persistence.utils.*;
+import org.apache.ambari.view.pig.utils.ServiceFormattedException;
 import org.apache.commons.configuration.Configuration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.ws.rs.WebApplicationException;
+
 
 /**
  * Persistent storage engine for storing java beans to
@@ -112,4 +115,18 @@ public class InstanceKeyValueStorage extends KeyValueStorage {
     }
     getConfig().clearProperty(modelPropName);
   }
+
+  public static void storageSmokeTest(ViewContext context) {
+    try {
+      final String property = "test.smoke.property";
+      context.putInstanceData(property, "42");
+      boolean status = context.getInstanceData(property).equals("42");
+      context.removeInstanceData(property);
+      if (!status) throw new ServiceFormattedException("Ambari Views instance data DB doesn't work properly", null);
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
+  }
 }

+ 2 - 2
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/persistence/LocalKeyValueStorage.java

@@ -19,11 +19,11 @@
 package org.apache.ambari.view.pig.persistence;
 
 import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.pig.utils.MisconfigurationFormattedException;
 import org.apache.commons.configuration.ConfigurationException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.xml.ws.WebServiceException;
 
 /**
  * Persistent storage engine for storing java beans to
@@ -55,7 +55,7 @@ public class LocalKeyValueStorage extends KeyValueStorage {
       if (fileName == null) {
         String msg = "dataworker.storagePath is not configured!";
         LOG.error(msg);
-        throw new WebServiceException(msg);
+        throw new MisconfigurationFormattedException("dataworker.storagePath");
       }
       try {
         config = new PersistentConfiguration(fileName);

+ 62 - 24
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/files/FileService.java

@@ -19,9 +19,10 @@
 package org.apache.ambari.view.pig.resources.files;
 
 import com.google.inject.Inject;
+import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.ViewResourceHandler;
 import org.apache.ambari.view.pig.services.BaseService;
-import org.apache.ambari.view.pig.utils.FilePaginator;
+import org.apache.ambari.view.pig.utils.*;
 import org.apache.hadoop.fs.FSDataOutputStream;
 import org.apache.hadoop.fs.FileAlreadyExistsException;
 import org.json.simple.JSONObject;
@@ -77,10 +78,14 @@ public class FileService extends BaseService {
       JSONObject object = new JSONObject();
       object.put("file", file);
       return Response.ok(object).status(200).build();
-    } catch (FileNotFoundException e) {
-      return notFoundResponse(e.toString());
-    } catch (IllegalArgumentException e) {
-      return badRequestResponse(e.toString());
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (FileNotFoundException ex) {
+      throw new NotFoundFormattedException(ex.getMessage(), ex);
+    } catch (IllegalArgumentException ex) {
+      throw new BadRequestFormattedException(ex.getMessage(), ex);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
   }
 
@@ -90,11 +95,17 @@ public class FileService extends BaseService {
   @DELETE
   @Path("{filePath:.*}")
   public Response deleteFile(@PathParam("filePath") String filePath) throws IOException, InterruptedException {
-    LOG.debug("Deleting file " + filePath);
-    if (getHdfsApi().delete(filePath, false)) {
-      return Response.status(204).build();
+    try {
+      LOG.debug("Deleting file " + filePath);
+      if (getHdfsApi().delete(filePath, false)) {
+        return Response.status(204).build();
+      }
+      throw new NotFoundFormattedException("FileSystem.delete returned false", null);
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return notFoundResponse("FileSystem.delete returned false");
   }
 
   /**
@@ -105,11 +116,17 @@ public class FileService extends BaseService {
   @Consumes(MediaType.APPLICATION_JSON)
   public Response updateFile(FileResourceRequest request,
                              @PathParam("filePath") String filePath) throws IOException, InterruptedException {
-    LOG.debug("Rewriting file " + filePath);
-    FSDataOutputStream output = getHdfsApi().create(filePath, true);
-    output.writeBytes(request.file.getFileContent());
-    output.close();
-    return Response.status(204).build();
+    try {
+      LOG.debug("Rewriting file " + filePath);
+      FSDataOutputStream output = getHdfsApi().create(filePath, true);
+      output.writeBytes(request.file.getFileContent());
+      output.close();
+      return Response.status(204).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
@@ -120,19 +137,40 @@ public class FileService extends BaseService {
   public Response createFile(FileResourceRequest request,
                              @Context HttpServletResponse response, @Context UriInfo ui)
       throws IOException, InterruptedException {
-    LOG.debug("Creating file " + request.file.getFilePath());
     try {
-      FSDataOutputStream output = getHdfsApi().create(request.file.getFilePath(), false);
-      if (request.file.getFileContent() != null) {
-        output.writeBytes(request.file.getFileContent());
+      LOG.debug("Creating file " + request.file.getFilePath());
+      try {
+        FSDataOutputStream output = getHdfsApi().create(request.file.getFilePath(), false);
+        if (request.file.getFileContent() != null) {
+          output.writeBytes(request.file.getFileContent());
+        }
+        output.close();
+      } catch (FileAlreadyExistsException ex) {
+        throw new ServiceFormattedException(ex.getMessage(), ex, 400);
       }
-      output.close();
-    } catch (FileAlreadyExistsException e) {
-      return badRequestResponse(e.toString());
+      response.setHeader("Location",
+          String.format("%s/%s", ui.getAbsolutePath().toString(), request.file.getFilePath()));
+      return Response.status(204).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
+  }
+
+  /**
+   * Checks connection to HDFS
+   * @param context View Context
+   */
+  public static void hdfsSmokeTest(ViewContext context) {
+    try {
+      HdfsApi api = connectToHDFSApi(context);
+      api.getStatus();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    response.setHeader("Location",
-        String.format("%s/%s", ui.getAbsolutePath().toString(), request.file.getFilePath()));
-    return Response.status(204).build();
   }
 
   /**

+ 50 - 17
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/jobs/JobResourceManager.java

@@ -26,11 +26,13 @@ import org.apache.ambari.view.pig.resources.jobs.models.PigJob;
 import org.apache.ambari.view.pig.resources.jobs.utils.JobPolling;
 import org.apache.ambari.view.pig.services.BaseService;
 import org.apache.ambari.view.pig.templeton.client.TempletonApi;
+import org.apache.ambari.view.pig.utils.MisconfigurationFormattedException;
+import org.apache.ambari.view.pig.utils.ServiceFormattedException;
 import org.apache.hadoop.fs.FSDataOutputStream;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.xml.ws.WebServiceException;
+import javax.ws.rs.WebApplicationException;
 import java.io.File;
 import java.io.IOException;
 import java.text.SimpleDateFormat;
@@ -64,8 +66,7 @@ public class JobResourceManager extends PersonalCRUDResourceManager<PigJob> {
    */
   public TempletonApi getTempletonApi() {
     if (api == null) {
-      api = new TempletonApi(context.getProperties().get("dataworker.webhcat.url"),
-          getTempletonUser(), getTempletonUser(), context);
+      api = connectToTempletonApi(context);
     }
     return api;
   }
@@ -158,15 +159,15 @@ public class JobResourceManager extends PersonalCRUDResourceManager<PigJob> {
         if (job.getSourceFile() != null && !job.getSourceFile().isEmpty()) {
           // otherwise, just copy original file
           if (!BaseService.getHdfsApi(context).copy(job.getSourceFile(), newSourceFilePath)) {
-            throw new WebServiceException("Can't copy source file from " + job.getSourceFile() +
+            throw new ServiceFormattedException("Can't copy source file from " + job.getSourceFile() +
                 " to " + newPigScriptPath);
           }
         }
       }
     } catch (IOException e) {
-      throw new WebServiceException("Can't create/copy source file: " + e.toString(), e);
+      throw new ServiceFormattedException("Can't create/copy source file: " + e.toString(), e);
     } catch (InterruptedException e) {
-      throw new WebServiceException("Can't create/copy source file: " + e.toString(), e);
+      throw new ServiceFormattedException("Can't create/copy source file: " + e.toString(), e);
     }
 
     try {
@@ -185,26 +186,26 @@ public class JobResourceManager extends PersonalCRUDResourceManager<PigJob> {
       } else {
         // otherwise, just copy original file
         if (!BaseService.getHdfsApi(context).copy(job.getPigScript(), newPigScriptPath)) {
-          throw new WebServiceException("Can't copy pig script file from " + job.getPigScript() +
+          throw new ServiceFormattedException("Can't copy pig script file from " + job.getPigScript() +
               " to " + newPigScriptPath);
         }
       }
     } catch (IOException e) {
-      throw new WebServiceException("Can't create/copy pig script file: " + e.toString(), e);
+      throw new ServiceFormattedException("Can't create/copy pig script file: " + e.toString(), e);
     } catch (InterruptedException e) {
-      throw new WebServiceException("Can't create/copy pig script file: " + e.toString(), e);
+      throw new ServiceFormattedException("Can't create/copy pig script file: " + e.toString(), e);
     }
 
     if (job.getPythonScript() != null && !job.getPythonScript().isEmpty()) {
       try {
         if (!BaseService.getHdfsApi(context).copy(job.getPythonScript(), newPythonScriptPath)) {
-          throw new WebServiceException("Can't copy python udf script file from " + job.getPythonScript() +
+          throw new ServiceFormattedException("Can't copy python udf script file from " + job.getPythonScript() +
               " to " + newPythonScriptPath);
         }
       } catch (IOException e) {
-        throw new WebServiceException("Can't create/copy python udf file: " + e.toString(), e);
+        throw new ServiceFormattedException("Can't create/copy python udf file: " + e.toString(), e);
       } catch (InterruptedException e) {
-        throw new WebServiceException("Can't create/copy python udf file: " + e.toString(), e);
+        throw new ServiceFormattedException("Can't create/copy python udf file: " + e.toString(), e);
       }
     }
 
@@ -215,9 +216,9 @@ public class JobResourceManager extends PersonalCRUDResourceManager<PigJob> {
       }
       stream.close();
     } catch (IOException e) {
-      throw new WebServiceException("Can't create params file: " + e.toString(), e);
+      throw new ServiceFormattedException("Can't create params file: " + e.toString(), e);
     } catch (InterruptedException e) {
-      throw new WebServiceException("Can't create params file: " + e.toString(), e);
+      throw new ServiceFormattedException("Can't create params file: " + e.toString(), e);
     }
     job.setPigScript(newPigScriptPath);
 
@@ -230,7 +231,7 @@ public class JobResourceManager extends PersonalCRUDResourceManager<PigJob> {
     } catch (IOException templetonBadResponse) {
       String msg = String.format("Templeton bad response: %s", templetonBadResponse.toString());
       LOG.debug(msg);
-      throw new WebServiceException(msg, templetonBadResponse);
+      throw new ServiceFormattedException(msg, templetonBadResponse);
     }
     job.setJobId(data.id);
 
@@ -289,12 +290,44 @@ public class JobResourceManager extends PersonalCRUDResourceManager<PigJob> {
     save(job);
   }
 
+  /**
+   * Checks connection to WebHCat
+   * @param context View Context
+   */
+  public static void webhcatSmokeTest(ViewContext context) {
+    try {
+      TempletonApi api = connectToTempletonApi(context);
+      api.status();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
+  }
+
+  private static TempletonApi connectToTempletonApi(ViewContext context) {
+    String webhcatUrl = context.getProperties().get("dataworker.webhcat.url");
+    if (webhcatUrl == null) {
+      String message = "dataworker.webhcat.url is not configured!";
+      LOG.error(message);
+      throw new MisconfigurationFormattedException("dataworker.webhcat.url");
+    }
+    return new TempletonApi(context.getProperties().get("dataworker.webhcat.url"),
+        getTempletonUser(context), getTempletonUser(context), context);
+  }
+
   /**
    * Extension point to use different usernames in templeton
    * requests instead of logged in user
    * @return username in templeton
    */
-  private String getTempletonUser() {
-    return context.getProperties().get("dataworker.webhcat.user");
+  private static String getTempletonUser(ViewContext context) {
+    String username = context.getProperties().get("dataworker.webhcat.user");
+    if (username == null) {
+      String message = "dataworker.webhcat.user is not configured!";
+      LOG.error(message);
+      throw new MisconfigurationFormattedException("dataworker.webhcat.user");
+    }
+    return username;
   }
 }

+ 88 - 59
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/jobs/JobService.java

@@ -19,19 +19,22 @@
 package org.apache.ambari.view.pig.resources.jobs;
 
 import com.google.inject.Inject;
+import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.ViewResourceHandler;
 import org.apache.ambari.view.pig.persistence.utils.ItemNotFound;
 import org.apache.ambari.view.pig.persistence.utils.OnlyOwnersFilteringStrategy;
 import org.apache.ambari.view.pig.resources.files.FileResource;
 import org.apache.ambari.view.pig.resources.jobs.models.PigJob;
 import org.apache.ambari.view.pig.services.BaseService;
+import org.apache.ambari.view.pig.utils.BadRequestFormattedException;
 import org.apache.ambari.view.pig.utils.FilePaginator;
+import org.apache.ambari.view.pig.utils.NotFoundFormattedException;
+import org.apache.ambari.view.pig.utils.ServiceFormattedException;
 import org.json.simple.JSONObject;
 
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.*;
 import javax.ws.rs.core.*;
-import javax.xml.ws.WebServiceException;
 import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.Callable;
@@ -82,16 +85,22 @@ public class JobService extends BaseService {
   @Path("{jobId}")
   @Produces(MediaType.APPLICATION_JSON)
   public Response getJob(@PathParam("jobId") String jobId) {
-    PigJob job = null;
     try {
-      job = getResourceManager().read(jobId);
-    } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
+      PigJob job = null;
+      try {
+        job = getResourceManager().read(jobId);
+      } catch (ItemNotFound itemNotFound) {
+        throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+      }
+      getResourceManager().retrieveJobStatus(job);
+      JSONObject object = new JSONObject();
+      object.put("job", job);
+      return Response.ok(object).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    getResourceManager().retrieveJobStatus(job);
-    JSONObject object = new JSONObject();
-    object.put("job", job);
-    return Response.ok(object).build();
   }
 
   /**
@@ -100,14 +109,20 @@ public class JobService extends BaseService {
   @DELETE
   @Path("{jobId}")
   public Response killJob(@PathParam("jobId") String jobId) throws IOException {
-    PigJob job = null;
     try {
-      job = getResourceManager().read(jobId);
-    } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
+      PigJob job = null;
+      try {
+        job = getResourceManager().read(jobId);
+      } catch (ItemNotFound itemNotFound) {
+        throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+      }
+      getResourceManager().killJob(job);
+      return Response.status(204).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    getResourceManager().killJob(job);
-    return Response.status(204).build();
   }
 
   /**
@@ -118,8 +133,8 @@ public class JobService extends BaseService {
   public Response jobCompletionNotification(@Context HttpHeaders headers,
                                             @Context UriInfo ui,
                                             @PathParam("jobId") final String jobId) {
-    PigJob job = null;
     try {
+      PigJob job = null;
       job = getResourceManager().ignorePermissions(new Callable<PigJob>() {
         public PigJob call() throws Exception {
           PigJob job = null;
@@ -131,14 +146,16 @@ public class JobService extends BaseService {
           return job;
         }
       });
-    } catch (Exception e) {
-      return Response.status(500).build();
-    }
-    if (job == null)
-      return Response.status(404).build();
+      if (job == null)
+        throw new NotFoundFormattedException("Job with id '" + jobId + "' not found", null);
 
-    getResourceManager().retrieveJobStatus(job);
-    return Response.ok().build();
+      getResourceManager().retrieveJobStatus(job);
+      return Response.ok().build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   @GET
@@ -149,13 +166,13 @@ public class JobService extends BaseService {
                               @PathParam("jobId") String jobId,
                               @PathParam("fileName") String fileName,
                               @QueryParam("page") Long page) {
-    PigJob job = null;
-    try {
-      job = getResourceManager().read(jobId);
-    } catch (ItemNotFound itemNotFound) {
-      return Response.ok("No such job").status(404).build();
-    }
     try {
+      PigJob job = null;
+      try {
+        job = getResourceManager().read(jobId);
+      } catch (ItemNotFound itemNotFound) {
+        throw new NotFoundFormattedException("Job with id '" + jobId + "' not found", null);
+      }
       String filePath = job.getStatusDir() + "/" + fileName;
       LOG.debug("Reading file " + filePath);
       FilePaginator paginator = new FilePaginator(filePath, context);
@@ -173,10 +190,14 @@ public class JobService extends BaseService {
       JSONObject object = new JSONObject();
       object.put("file", file);
       return Response.ok(object).status(200).build();
-    } catch (IOException e) {
-      return Response.ok(e.getMessage()).status(404).build();
-    } catch (InterruptedException e) {
-      return Response.ok(e.getMessage()).status(404).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (IOException ex) {
+      throw new NotFoundFormattedException(ex.getMessage(), ex);
+    } catch (InterruptedException ex) {
+      throw new NotFoundFormattedException(ex.getMessage(), ex);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
   }
 
@@ -186,12 +207,18 @@ public class JobService extends BaseService {
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   public Response getJobList(@Context HttpHeaders headers, @Context UriInfo ui) {
-    List allJobs = getResourceManager().readAll(
-        new OnlyOwnersFilteringStrategy(this.context.getUsername()));
+    try {
+      List allJobs = getResourceManager().readAll(
+          new OnlyOwnersFilteringStrategy(this.context.getUsername()));
 
-    JSONObject object = new JSONObject();
-    object.put("jobs", allJobs);
-    return Response.ok(object).build();
+      JSONObject object = new JSONObject();
+      object.put("jobs", allJobs);
+      return Response.ok(object).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
@@ -202,31 +229,31 @@ public class JobService extends BaseService {
   @Produces(MediaType.APPLICATION_JSON)
   public Response runJob(PigJobRequest request, @Context HttpServletResponse response,
                          @Context UriInfo ui) {
-    if (!request.validatePOST()) {
-      return badRequestResponse(request.explainPOST());
-    }
     try {
+      request.validatePOST();
       getResourceManager().create(request.job);
-    } catch (IllegalArgumentException e) {
-      return badRequestResponse(e.getMessage());
-    } catch (WebServiceException e) {
-      return serverErrorResponse(e.getMessage());
-    }
 
-    PigJob job = null;
+      PigJob job = null;
 
-    try {
-      job = getResourceManager().read(request.job.getId());
-    } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
-    }
+      try {
+        job = getResourceManager().read(request.job.getId());
+      } catch (ItemNotFound itemNotFound) {
+        throw new NotFoundFormattedException("Job not found", null);
+      }
 
-    response.setHeader("Location",
-        String.format("%s/%s", ui.getAbsolutePath().toString(), request.job.getId()));
+      response.setHeader("Location",
+          String.format("%s/%s", ui.getAbsolutePath().toString(), request.job.getId()));
 
-    JSONObject object = new JSONObject();
-    object.put("job", job);
-    return Response.ok(object).status(201).build();
+      JSONObject object = new JSONObject();
+      object.put("job", job);
+      return Response.ok(object).status(201).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (IllegalArgumentException ex) {
+      throw new BadRequestFormattedException(ex.getMessage(), ex);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
@@ -247,8 +274,10 @@ public class JobService extends BaseService {
       return result.toString();
     }
 
-    public boolean validatePOST() {
-      return explainPOST().isEmpty();
+    public void validatePOST() {
+      if (!explainPOST().isEmpty()) {
+        throw new BadRequestFormattedException(explainPOST(), null);
+      }
     }
   }
 }

+ 7 - 7
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/scripts/ScriptResourceManager.java

@@ -23,12 +23,13 @@ import org.apache.ambari.view.pig.persistence.utils.ItemNotFound;
 import org.apache.ambari.view.pig.resources.PersonalCRUDResourceManager;
 import org.apache.ambari.view.pig.resources.scripts.models.PigScript;
 import org.apache.ambari.view.pig.services.BaseService;
+import org.apache.ambari.view.pig.utils.MisconfigurationFormattedException;
+import org.apache.ambari.view.pig.utils.ServiceFormattedException;
 import org.apache.hadoop.fs.FSDataOutputStream;
 import org.apache.hadoop.fs.FileAlreadyExistsException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.xml.ws.WebServiceException;
 import java.io.IOException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -62,7 +63,7 @@ public class ScriptResourceManager extends PersonalCRUDResourceManager<PigScript
     if (userScriptsPath == null) {
       String msg = "dataworker.scripts.path is not configured!";
       LOG.error(msg);
-      throw new WebServiceException(msg);
+      throw new MisconfigurationFormattedException("dataworker.scripts.path");
     }
     int checkId = 0;
 
@@ -88,16 +89,16 @@ public class ScriptResourceManager extends PersonalCRUDResourceManager<PigScript
         try {
           delete(object.getId());
         } catch (ItemNotFound itemNotFound) {
-          throw new WebServiceException("Error in creation, during clean up: " + itemNotFound.toString(), itemNotFound);
+          throw new ServiceFormattedException("Error in creation, during clean up: " + itemNotFound.toString(), itemNotFound);
         }
-        throw new WebServiceException("Error in creation: " + e.toString(), e);
+        throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
       } catch (InterruptedException e) {
         try {
           delete(object.getId());
         } catch (ItemNotFound itemNotFound) {
-          throw new WebServiceException("Error in creation, during clean up: " + itemNotFound.toString(), itemNotFound);
+          throw new ServiceFormattedException("Error in creation, during clean up: " + itemNotFound.toString(), itemNotFound);
         }
-        throw new WebServiceException("Error in creation: " + e.toString(), e);
+        throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
       }
       checkId += 1;
     } while (!fileCreated);
@@ -105,5 +106,4 @@ public class ScriptResourceManager extends PersonalCRUDResourceManager<PigScript
     object.setPigScript(newFilePath);
     getPigStorage().store(object);
   }
-
 }

+ 51 - 27
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/scripts/ScriptService.java

@@ -25,6 +25,8 @@ import org.apache.ambari.view.pig.persistence.utils.ItemNotFound;
 import org.apache.ambari.view.pig.resources.PersonalCRUDResourceManager;
 import org.apache.ambari.view.pig.resources.scripts.models.PigScript;
 import org.apache.ambari.view.pig.services.BaseService;
+import org.apache.ambari.view.pig.utils.NotFoundFormattedException;
+import org.apache.ambari.view.pig.utils.ServiceFormattedException;
 import org.json.simple.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -67,15 +69,19 @@ public class ScriptService extends BaseService {
   @Path("{scriptId}")
   @Produces(MediaType.APPLICATION_JSON)
   public Response getScript(@PathParam("scriptId") String scriptId) {
-    PigScript script = null;
     try {
+      PigScript script = null;
       script = getResourceManager().read(scriptId);
+      JSONObject object = new JSONObject();
+      object.put("script", script);
+      return Response.ok(object).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
     } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
+      throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    JSONObject object = new JSONObject();
-    object.put("script", script);
-    return Response.ok(object).build();
   }
 
   /**
@@ -86,10 +92,14 @@ public class ScriptService extends BaseService {
   public Response deleteScript(@PathParam("scriptId") String scriptId) {
     try {
       getResourceManager().delete(scriptId);
+      return Response.status(204).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
     } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
+      throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return Response.status(204).build();
   }
 
   /**
@@ -98,13 +108,19 @@ public class ScriptService extends BaseService {
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   public Response getScriptList() {
-    LOG.debug("Getting all scripts");
-    List allScripts = getResourceManager().readAll(
-        new OnlyOwnersFilteringStrategy(this.context.getUsername()));
-
-    JSONObject object = new JSONObject();
-    object.put("scripts", allScripts);
-    return Response.ok(object).build();
+    try {
+      LOG.debug("Getting all scripts");
+      List allScripts = getResourceManager().readAll(
+          new OnlyOwnersFilteringStrategy(this.context.getUsername()));
+
+      JSONObject object = new JSONObject();
+      object.put("scripts", allScripts);
+      return Response.ok(object).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
@@ -117,10 +133,14 @@ public class ScriptService extends BaseService {
                                @PathParam("scriptId") String scriptId) {
     try {
       getResourceManager().update(request.script, scriptId);
+      return Response.status(204).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
     } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
+      throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return Response.status(204).build();
   }
 
   /**
@@ -130,22 +150,26 @@ public class ScriptService extends BaseService {
   @Consumes(MediaType.APPLICATION_JSON)
   public Response saveScript(PigScriptRequest request, @Context HttpServletResponse response,
                              @Context UriInfo ui) {
-    getResourceManager().create(request.script);
+    try {
+      getResourceManager().create(request.script);
 
-    PigScript script = null;
+      PigScript script = null;
 
-    try {
       script = getResourceManager().read(request.script.getId());
-    } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
-    }
 
-    response.setHeader("Location",
-        String.format("%s/%s", ui.getAbsolutePath().toString(), request.script.getId()));
+      response.setHeader("Location",
+          String.format("%s/%s", ui.getAbsolutePath().toString(), request.script.getId()));
 
-    JSONObject object = new JSONObject();
-    object.put("script", script);
-    return Response.ok(object).status(201).build();
+      JSONObject object = new JSONObject();
+      object.put("script", script);
+      return Response.ok(object).status(201).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (ItemNotFound itemNotFound) {
+      throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**

+ 50 - 29
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/resources/udf/UDFService.java

@@ -25,6 +25,8 @@ import org.apache.ambari.view.pig.persistence.utils.OnlyOwnersFilteringStrategy;
 import org.apache.ambari.view.pig.resources.PersonalCRUDResourceManager;
 import org.apache.ambari.view.pig.resources.udf.models.UDF;
 import org.apache.ambari.view.pig.services.BaseService;
+import org.apache.ambari.view.pig.utils.NotFoundFormattedException;
+import org.apache.ambari.view.pig.utils.ServiceFormattedException;
 import org.json.simple.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -69,15 +71,18 @@ public class UDFService extends BaseService {
   @Path("{udfId}")
   @Produces(MediaType.APPLICATION_JSON)
   public Response getUDF(@PathParam("udfId") String udfId) {
-    UDF udf = null;
     try {
-      udf = getResourceManager().read(udfId);
+      UDF udf = getResourceManager().read(udfId);
+      JSONObject object = new JSONObject();
+      object.put("udf", udf);
+      return Response.ok(object).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
     } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
+      throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    JSONObject object = new JSONObject();
-    object.put("udf", udf);
-    return Response.ok(object).build();
   }
 
   /**
@@ -88,10 +93,14 @@ public class UDFService extends BaseService {
   public Response deleteUDF(@PathParam("udfId") String udfId) {
     try {
       getResourceManager().delete(udfId);
+      return Response.status(204).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
     } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
+      throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return Response.status(204).build();
   }
 
   /**
@@ -100,13 +109,19 @@ public class UDFService extends BaseService {
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   public Response getUDFList(@Context UriInfo ui) {
-    LOG.debug("Getting all UDFs");
-    List allUDFs = getResourceManager().readAll(
-        new OnlyOwnersFilteringStrategy(this.context.getUsername()));
-
-    JSONObject object = new JSONObject();
-    object.put("udfs", allUDFs);
-    return Response.ok(object).build();
+    try {
+      LOG.debug("Getting all UDFs");
+      List allUDFs = getResourceManager().readAll(
+          new OnlyOwnersFilteringStrategy(this.context.getUsername()));
+
+      JSONObject object = new JSONObject();
+      object.put("udfs", allUDFs);
+      return Response.ok(object).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
   }
 
   /**
@@ -119,10 +134,14 @@ public class UDFService extends BaseService {
                             @PathParam("udfId") String udfId) {
     try {
       getResourceManager().update(request.udf, udfId);
+      return Response.status(204).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
     } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
+      throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-    return Response.status(204).build();
   }
 
   /**
@@ -132,22 +151,24 @@ public class UDFService extends BaseService {
   @Consumes(MediaType.APPLICATION_JSON)
   public Response createUDF(UDFRequest request, @Context HttpServletResponse response,
                             @Context UriInfo ui) {
-    getResourceManager().create(request.udf);
+    try {
+      getResourceManager().create(request.udf);
 
-    UDF udf = null;
+      UDF udf = getResourceManager().read(request.udf.getId());
 
-    try {
-      udf = getResourceManager().read(request.udf.getId());
+      response.setHeader("Location",
+          String.format("%s/%s", ui.getAbsolutePath().toString(), request.udf.getId()));
+
+      JSONObject object = new JSONObject();
+      object.put("udf", udf);
+      return Response.ok(object).status(201).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
     } catch (ItemNotFound itemNotFound) {
-      return Response.status(404).build();
+      throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
     }
-
-    response.setHeader("Location",
-        String.format("%s/%s", ui.getAbsolutePath().toString(), request.udf.getId()));
-
-    JSONObject object = new JSONObject();
-    object.put("udf", udf);
-    return Response.ok(object).status(201).build();
   }
 
   /**

+ 29 - 44
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/services/BaseService.java

@@ -20,17 +20,17 @@ package org.apache.ambari.view.pig.services;
 
 import com.google.inject.Inject;
 import org.apache.ambari.view.ViewContext;
-import org.apache.ambari.view.ViewResourceHandler;
 import org.apache.ambari.view.pig.persistence.Storage;
 import org.apache.ambari.view.pig.utils.HdfsApi;
 import org.apache.ambari.view.pig.persistence.utils.StorageUtil;
+import org.apache.ambari.view.pig.utils.MisconfigurationFormattedException;
+import org.apache.ambari.view.pig.utils.ServiceFormattedException;
 import org.json.simple.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import javax.xml.ws.WebServiceException;
 import java.io.IOException;
 import java.util.HashMap;
 
@@ -67,31 +67,37 @@ public class BaseService {
    */
   public static HdfsApi getHdfsApi(ViewContext context) {
     if (hdfsApi == null) {
-      Thread.currentThread().setContextClassLoader(null);
-
-      String defaultFS = context.getProperties().get("dataworker.defaultFs");
-      if (defaultFS == null) {
-        String message = "dataworker.defaultFs is not configured!";
-        LOG.error(message);
-        throw new WebServiceException(message);
-      }
-
-      try {
-        hdfsApi = new HdfsApi(defaultFS, getHdfsUsername(context));
-        LOG.info("HdfsApi connected OK");
-      } catch (IOException e) {
-        String message = "HdfsApi IO error: " + e.getMessage();
-        LOG.error(message);
-        throw new WebServiceException(message, e);
-      } catch (InterruptedException e) {
-        String message = "HdfsApi Interrupted error: " + e.getMessage();
-        LOG.error(message);
-        throw new WebServiceException(message, e);
-      }
+      hdfsApi = connectToHDFSApi(context);
     }
     return hdfsApi;
   }
 
+  protected static HdfsApi connectToHDFSApi(ViewContext context) {
+    HdfsApi api = null;
+    Thread.currentThread().setContextClassLoader(null);
+
+    String defaultFS = context.getProperties().get("dataworker.defaultFs");
+    if (defaultFS == null) {
+      String message = "dataworker.defaultFs is not configured!";
+      LOG.error(message);
+      throw new MisconfigurationFormattedException("dataworker.defaultFs");
+    }
+
+    try {
+      api = new HdfsApi(defaultFS, getHdfsUsername(context));
+      LOG.info("HdfsApi connected OK");
+    } catch (IOException e) {
+      String message = "HdfsApi IO error: " + e.getMessage();
+      LOG.error(message);
+      throw new ServiceFormattedException(message, e);
+    } catch (InterruptedException e) {
+      String message = "HdfsApi Interrupted error: " + e.getMessage();
+      LOG.error(message);
+      throw new ServiceFormattedException(message, e);
+    }
+    return api;
+  }
+
   public static String getHdfsUsername(ViewContext context) {
     String userName = context.getProperties().get("dataworker.hdfs.username");
     if (userName == null)
@@ -110,25 +116,4 @@ public class BaseService {
   public static void setHdfsApi(HdfsApi api)  {
     hdfsApi = api;
   }
-
-  protected static Response badRequestResponse(String message) {
-    HashMap<String, Object> response = new HashMap<String, Object>();
-    response.put("message", message);
-    response.put("status", 400);
-    return Response.status(400).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON).build();
-  }
-
-  protected static Response serverErrorResponse(String message) {
-    HashMap<String, Object> response = new HashMap<String, Object>();
-    response.put("message", message);
-    response.put("status", 500);
-    return Response.status(500).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON).build();
-  }
-
-  protected static Response notFoundResponse(String message) {
-    HashMap<String, Object> response = new HashMap<String, Object>();
-    response.put("message", message);
-    response.put("status", 404);
-    return Response.status(404).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON).build();
-  }
 }

+ 53 - 0
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/services/HelpService.java

@@ -20,12 +20,17 @@ package org.apache.ambari.view.pig.services;
 
 import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.ViewResourceHandler;
+import org.apache.ambari.view.pig.persistence.InstanceKeyValueStorage;
+import org.apache.ambari.view.pig.resources.files.FileService;
+import org.apache.ambari.view.pig.resources.jobs.JobResourceManager;
+import org.apache.commons.lang.exception.ExceptionUtils;
 import org.json.simple.JSONObject;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.*;
+import java.util.HashMap;
 
 /**
  * Help service
@@ -69,4 +74,52 @@ public class HelpService extends BaseService {
   public Response version(){
     return Response.ok("0.0.1-SNAPSHOT").build();
   }
+
+  // ================================================================================
+  // Smoke tests
+  // ================================================================================
+
+  /**
+   * HDFS Status
+   * @return status
+   */
+  @GET
+  @Path("/hdfsStatus")
+  @Produces(MediaType.APPLICATION_JSON)
+  public Response hdfsStatus(){
+    FileService.hdfsSmokeTest(context);
+    return getOKResponse();
+  }
+
+  /**
+   * WebHCat Status
+   * @return status
+   */
+  @GET
+  @Path("/webhcatStatus")
+  @Produces(MediaType.APPLICATION_JSON)
+  public Response webhcatStatus(){
+    JobResourceManager.webhcatSmokeTest(context);
+    return getOKResponse();
+  }
+
+  /**
+   * Storage Status
+   * @return status
+   */
+  @GET
+  @Path("/storageStatus")
+  @Produces(MediaType.APPLICATION_JSON)
+  public Response storageStatus(){
+    InstanceKeyValueStorage.storageSmokeTest(context);
+    return getOKResponse();
+  }
+
+  private Response getOKResponse() {
+    JSONObject response = new JSONObject();
+    response.put("message", "OK");
+    response.put("trace", null);
+    response.put("status", "200");
+    return Response.ok().entity(response).type(MediaType.APPLICATION_JSON).build();
+  }
 }

+ 27 - 0
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/utils/BadRequestFormattedException.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.pig.utils;
+
+public class BadRequestFormattedException extends ServiceFormattedException {
+  private final static int STATUS = 400;
+
+  public BadRequestFormattedException(String message, Throwable exception) {
+    super(message, exception, STATUS);
+  }
+}

+ 26 - 0
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/utils/HdfsApi.java

@@ -147,6 +147,32 @@ public class HdfsApi {
     });
   }
 
+  /**
+   * Home directory
+   * @return home directory
+   * @throws Exception
+   */
+  public Path getHomeDir() throws Exception {
+    return ugi.doAs(new PrivilegedExceptionAction<Path>() {
+      public Path run() throws IOException {
+        return fs.getHomeDirectory();
+      }
+    });
+  }
+
+  /**
+   * Hdfs Status
+   * @return home directory
+   * @throws Exception
+   */
+  public FsStatus getStatus() throws Exception {
+    return ugi.doAs(new PrivilegedExceptionAction<FsStatus>() {
+      public FsStatus run() throws IOException {
+        return fs.getStatus();
+      }
+    });
+  }
+
   /**
    * Create file
    * @param path path

+ 47 - 0
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/utils/MisconfigurationFormattedException.java

@@ -0,0 +1,47 @@
+/**
+ * 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.pig.utils;
+
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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";
+  private final static Logger LOG =
+      LoggerFactory.getLogger(MisconfigurationFormattedException.class);
+
+  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/pig/src/main/java/org/apache/ambari/view/pig/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.pig.utils;
+
+public class NotFoundFormattedException extends ServiceFormattedException {
+  private final static int STATUS = 404;
+
+  public NotFoundFormattedException(String message, Throwable exception) {
+    super(message, exception, STATUS);
+  }
+}

+ 71 - 0
contrib/views/pig/src/main/java/org/apache/ambari/view/pig/utils/ServiceFormattedException.java

@@ -0,0 +1,71 @@
+/**
+ * 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.pig.utils;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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 {
+  private final static Logger LOG =
+      LoggerFactory.getLogger(ServiceFormattedException.class);
+
+  public ServiceFormattedException(String message) {
+    super(errorEntity(message, null, suggestStatus(null)));
+  }
+
+  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 == null) return status;
+    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);
+
+    if(message != null) LOG.error(message);
+    if(trace != null) LOG.debug(trace);
+
+    return Response.status(status).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON).build();
+  }
+}

+ 16 - 0
contrib/views/pig/src/main/resources/ui/pig-web/app/app.js

@@ -20,4 +20,20 @@
 module.exports = Em.Application.create({
   //LOG_TRANSITIONS: true, 
   //LOG_TRANSITIONS_INTERNAL: true
+  smokeTests: false,
+
+  errorLog: "",
+
+  getNamespaceUrl: function() {
+    var parts = window.location.pathname.match(/\/[^\/]*/g);
+    var view = parts[1];
+    var version = '/versions' + parts[2];
+    var instance = parts[3];
+    if (parts.length == 4) { // version is not present
+      instance = parts[2];
+      version = '';
+    }
+    var namespaceUrl = 'api/v1/views' + view + version + '/instances' + instance;
+    return namespaceUrl;
+  }
 });

+ 5 - 1
contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/edit.js

@@ -70,7 +70,11 @@ App.EditController = Em.ObjectController.extend({
             var alerts = {success:Em.I18n.t('job.alert.job_started'), error:Em.I18n.t('job.alert.start_filed')};
             return function (argument) {
               controller.set('isExec',false);
-              controller.send('showAlert', {message:alerts[status],status:status});
+              var trace = null;
+              if (status=='error') {
+                trace = argument.responseJSON.trace;
+              }
+              controller.send('showAlert', {message:alerts[status],status:status,trace:trace});
               if (status=='success') {
                 controller.transitionToRoute('job',job);
               };

+ 23 - 0
contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/errorLog.js

@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.PigErrorLogController = Ember.ObjectController.extend({
+  errorLog: null,
+});

+ 1 - 1
contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/jobResults.js

@@ -35,7 +35,7 @@ App.JobResultsController = Em.ObjectController.extend({
       },function (error) {
         var responseText = JSON.parse(error.responseText);
         self.send('showAlert', {'message': Em.I18n.t('job.alert.'+output+'_error',
-          {status:responseText.status, message:responseText.message}), status:'error'});
+          {status:responseText.status, message:responseText.message}), status:'error', trace: responseText.trace});
       })
     });
 

+ 25 - 18
contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/pigScriptEdit.js

@@ -38,24 +38,31 @@ App.PigScriptEditController = App.EditController.extend({
     and updates pigParams object as they changes. 
    */
   pigParamsMatch:function (controller) {
-    var editorContent = this.get('content.pigScript.fileContent');
-    if (editorContent) {
-      var match_var = editorContent.match(/\%\w+\%/g);
-      if (match_var) {
-        var oldParams = controller.pigParams;
-        controller.set('pigParams',[]);
-        match_var.forEach(function (param) {
-          var suchParams = controller.pigParams.filterProperty('param',param);
-          if (suchParams.length == 0){
-            var oldParam = oldParams.filterProperty('param',param);
-            var oldValue = oldParam.get('firstObject.value');
-            controller.pigParams.pushObject(Em.Object.create({param:param,value:oldValue,title:param.replace(/%/g,'')}));
-          }
-        });
-      } else {
-        controller.set('pigParams',[]);
-      }
-    };
+    var editorContent = this.get('content.pigScript').then(function(data) {
+      editorContent = data.get('fileContent');
+      if (editorContent) {
+            var match_var = editorContent.match(/\%\w+\%/g);
+            if (match_var) {
+              var oldParams = controller.pigParams;
+              controller.set('pigParams',[]);
+              match_var.forEach(function (param) {
+                var suchParams = controller.pigParams.filterProperty('param',param);
+                if (suchParams.length == 0){
+                  var oldParam = oldParams.filterProperty('param',param);
+                  var oldValue = oldParam.get('firstObject.value');
+                  controller.pigParams.pushObject(Em.Object.create({param:param,value:oldValue,title:param.replace(/%/g,'')}));
+                }
+              });
+            } else {
+              controller.set('pigParams',[]);
+            }
+          };
+    }, function(reason) {
+      var trace = null;
+      if (reason.responseJSON.trace)
+        trace = reason.responseJSON.trace;
+      controller.send('showAlert', {'message':Em.I18n.t('scripts.alert.file_not_found',{title: 'Error'}),status:'error',trace:trace});
+    });
   }.observes('content.pigScript.fileContent'),
 
 });

+ 5 - 2
contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/poll.js

@@ -25,8 +25,11 @@ App.PollController = Ember.ObjectController.extend({
       job.kill(function () {
         job.reload();
         self.send('showAlert', {'message': Em.I18n.t('job.alert.job_killed',{title:self.get('title')}), status:'info'});
-      },function () {
-        self.send('showAlert', {'message': Em.I18n.t('job.alert.job_kill_error'), status:'error'});
+      },function (reason) {
+        var trace = null;
+        if (reason && reason.responseJSON.trace)
+          trace = reason.responseJSON.trace;
+        self.send('showAlert', {'message': Em.I18n.t('job.alert.job_kill_error'), status:'error', trace:trace});
       });
     },
   },

+ 72 - 0
contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/splash.js

@@ -0,0 +1,72 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.SplashController = Ember.ObjectController.extend({
+  actions: {
+    toggleStackTrace:function () {
+      var value = this.get('isExpanded');
+      this.set('isExpanded', !value);
+    },
+  },
+  isExpanded: false,
+
+  errors: "",
+  stackTrace: "",
+  startTests: function(model) {
+    var url = App.getNamespaceUrl() + '/resources/pig/help/';
+    var self = this;
+    var processResponse = function(name, data) {
+      model.set(name + 'Test', data.status == 200);
+
+      if (data.status != 200) {
+        var checkFailedMessage = "Service '" + name + "' check failed";
+        var errors = self.get("errors");
+        errors += checkFailedMessage;
+        errors += (data.message)?(': <i>' + data.message + '</i><br>'):'<br>';
+        self.set("errors", errors);
+      }
+
+      if (data.trace != null) {
+        var stackTrace = self.get("stackTrace");
+        stackTrace += checkFailedMessage + ':\n' + data.trace;
+        self.set("stackTrace", stackTrace);
+      }
+      model.set(name + 'TestDone', true);
+      var percent = model.get('percent');
+      model.set('percent', percent + 33.33);
+    };
+    var promises = ['storage', 'webhcat', 'hdfs'].map(function(name) {
+      return Ember.$.getJSON('/' + url + name + 'Status')
+               .then(
+                 function(data) {
+                   processResponse(name, data);
+                 },
+                 function(reason) {
+                   processResponse(name, reason.responseJSON);
+                 }
+               );
+    });
+
+    return Ember.RSVP.all(promises);
+  },
+  progressBarStyle: function() {
+    return 'width: ' + this.get("model").get("percent") + '%;';
+  }.property("model.percent")
+});

+ 7 - 1
contrib/views/pig/src/main/resources/ui/pig-web/app/controllers/util/pigUtilAlert.js

@@ -20,9 +20,15 @@ var App = require('app');
 
 App.PigUtilAlertController = Ember.ArrayController.extend({
   content:Ember.A(),
+  needs: ['pigErrorLog'],
   actions:{
     removeAlertObject:function (alert) {
       this.content.removeObject(alert)
-    }
+    },
+    showErrorLog:function (content) {
+      errorLogController = this.get('controllers.pigErrorLog');
+      errorLogController.set('errorLog', content[0].trace);
+      this.transitionToRoute('pig.errorLog');
+    },
   },
 });

+ 8 - 15
contrib/views/pig/src/main/resources/ui/pig-web/app/initialize.js

@@ -18,21 +18,8 @@
 
 var App = require('app');
 
-function getNamespaceUrl() {
-  var parts = window.location.pathname.match(/\/[^\/]*/g);
-  var view = parts[1];
-  var version = '/versions' + parts[2];
-  var instance = parts[3];
-  if (parts.length == 4) { // version is not present
-    instance = parts[2];
-    version = '';
-  }
-  var namespaceUrl = 'api/v1/views' + view + version + '/instances' + instance;
-  return namespaceUrl;
-}
-
 App.ApplicationAdapter = DS.RESTAdapter.extend({
-  namespace: getNamespaceUrl(),
+  namespace: App.getNamespaceUrl(),
   headers: {
    'X-Requested-By': 'ambari'
   }
@@ -41,7 +28,7 @@ App.ApplicationAdapter = DS.RESTAdapter.extend({
 App.FileAdapter = App.ApplicationAdapter.extend({
   pathForType: function() {
     return 'resources/file';
-  }
+  },
 });
 
 App.FileSerializer = DS.RESTSerializer.extend({
@@ -93,6 +80,7 @@ require('templates/pig/jobResults');
 require('templates/pig/jobResultsOutput');
 require('templates/pig/history');
 require('templates/pig/udfs');
+require('templates/pig/errorLog');
 
 require('templates/pig/util/script-nav');
 require('templates/pig/util/alert');
@@ -103,6 +91,8 @@ require('templates/pig/modal/createUdf');
 require('templates/pig/modal/modalLayout');
 require('templates/pig/modal/createScript');
 
+require('templates/splash');
+
 //////////////////////////////////
 // Models
 //////////////////////////////////
@@ -126,6 +116,8 @@ require('controllers/pigUdfs');
 require('controllers/pigHistory');
 require('controllers/pigJob');
 require('controllers/jobResults');
+require('controllers/splash');
+require('controllers/errorLog');
 require('controllers/util/pigUtilAlert');
 require('controllers/modal/pigModal');
 
@@ -162,6 +154,7 @@ require('routes/pigScriptList');
 require('routes/pigUdfs');
 require('routes/pigJob');
 require('routes/jobResults');
+require('routes/splash');
 
 /////////////////////////////////
 // Router

+ 1 - 1
contrib/views/pig/src/main/resources/ui/pig-web/app/models/file.js

@@ -22,5 +22,5 @@ App.File = DS.Model.extend({
   fileContent: DS.attr('string'),
   hasNext:DS.attr('boolean'),
   page:DS.attr('number'),
-  pageCount:DS.attr('number')
+  pageCount:DS.attr('number'),
 });

+ 1 - 1
contrib/views/pig/src/main/resources/ui/pig-web/app/models/pig_script.js

@@ -39,5 +39,5 @@ App.Script = DS.Model.extend(App.OpenedScript,{
   }.property('title'),
   label:function (){
     return this.get('title');
-  }.property('title')
+  }.property('title'),
 });

+ 4 - 4
contrib/views/pig/src/main/resources/ui/pig-web/app/router.js

@@ -20,18 +20,18 @@ var App = require('app');
 
 App.Router.map(function () {
   this.resource('pig', { path: "/" }, function() {
-  	this.route('scriptList',{ path: "/list" });
+    this.route('scriptList',{ path: "/list" });
     this.route('scriptEdit',{ path: "/edit/:script_id" });
     this.resource('job', { path: "/job/:job_id" },function (argument) {
       this.route('results');
     });
     this.route('udfs');
-  	this.route('history');
+    this.route('history');
+    this.route('errorLog');
   });
+  this.route('splash');
 });
 
-App.LoadingRoute = Ember.Route.extend({});
-
 App.LoadingView = Em.View.extend({
     templateName: 'pig/loading'
 });

+ 14 - 1
contrib/views/pig/src/main/resources/ui/pig-web/app/routes/pig.js

@@ -19,6 +19,16 @@
 var App = require('app');
 
 App.PigRoute = Em.Route.extend({
+  beforeModel: function(transition) {
+    App.set('previousTransition', transition);
+  },
+  redirect: function () {
+    testsConducted = App.get("smokeTests");
+    if (!testsConducted) {
+        App.set("smokeTests", true);
+        this.transitionTo('splash');
+    }
+  },
   actions: {
     gotoSection: function(nav) {
       var location = (nav.hasOwnProperty('url'))?[nav.url]:['pig.scriptEdit',nav.get('id')];
@@ -38,7 +48,10 @@ App.PigRoute = Em.Route.extend({
         self.send('showAlert', {'message':Em.I18n.t('scripts.alert.script_saved',{title: script.get('title')}), status:'success'});
       },function (error) {
         //script.open();
-        self.send('showAlert', {'message': Em.I18n.t('scripts.alert.save_error_reason',{message:error.statusText}) , status:'error'});
+        var trace = null;
+        if (error && error.responseJSON.trace)
+          trace = error.responseJSON.trace;
+        self.send('showAlert', {'message': Em.I18n.t('scripts.alert.save_error_reason',{message:error.statusText}) , status:'error', trace:trace});
       });
     },
     showAlert:function (alert) {

+ 4 - 1
contrib/views/pig/src/main/resources/ui/pig-web/app/routes/pigJob.js

@@ -22,7 +22,10 @@ App.JobRoute = Em.Route.extend({
     actions: {
       error: function(error, transition) {
         Em.warn(error.stack);
-        transition.send('showAlert', {'message':Em.I18n.t('job.alert.load_error',{message:error.message}), status:'error'});
+        var trace = null;
+        if (error && error.responseJSON.trace)
+          trace = error.responseJSON.trace;
+        transition.send('showAlert', {'message':Em.I18n.t('job.alert.load_error',{message:error.message}), status:'error', trace:trace});
         this.transitionTo('pig.scriptList');
       },
       navigate:function (argument) {

+ 4 - 1
contrib/views/pig/src/main/resources/ui/pig-web/app/routes/pigScriptEdit.js

@@ -36,7 +36,10 @@ App.PigScriptEditRoute = Em.Route.extend({
           router.send('showAlert', {'message':Em.I18n.t('scripts.alert.script_saved',{title: script.get('title')}),status:'success'});
         },
         onFail = function(error){
-          router.send('showAlert', {'message':Em.I18n.t('scripts.alert.save_error'),status:'error'});
+          var trace = null;
+          if (error && error.responseJSON.trace)
+            trace = error.responseJSON.trace;
+          router.send('showAlert', {'message':Em.I18n.t('scripts.alert.save_error'),status:'error',trace:trace});
         };
 
       return script.get('pigScript').then(function(file){

+ 8 - 3
contrib/views/pig/src/main/resources/ui/pig-web/app/routes/pigScriptList.js

@@ -38,11 +38,13 @@ App.PigScriptListRoute = Em.Route.extend({
           success:Em.I18n.t('scripts.alert.script_created',{title:'New script'}), 
           error: Em.I18n.t('scripts.alert.create_failed')
         };
-        return function () {
+        return function (data) {
+          var trace = null;
           if (status=='error'){
             script.deleteRecord();
+            trace = data.responseJSON.trace;
           }
-          route.send('showAlert', {message:alerts[status],status:status});
+          route.send('showAlert', {message:alerts[status],status:status,trace:trace});
         };
       };
       if (filePath) {
@@ -73,7 +75,10 @@ App.PigScriptListRoute = Em.Route.extend({
             router.send('showAlert', {'message':Em.I18n.t('scripts.alert.script_deleted',{title : model.get('title')}),status:'success'});
           };
       var onFail = function(error){
-            router.send('showAlert', {'message':Em.I18n.t('scripts.alert.delete_failed'),status:'error'});
+            var trace = null;
+            if (error && error.responseJSON.trace)
+              trace = error.responseJSON.trace;
+            router.send('showAlert', {'message':Em.I18n.t('scripts.alert.delete_failed'),status:'error',trace:trace});
           };
       script.deleteRecord();
       return script.save().then(onSuccess,onFail);

+ 8 - 2
contrib/views/pig/src/main/resources/ui/pig-web/app/routes/pigUdfs.js

@@ -30,7 +30,10 @@ App.PigUdfsRoute = Em.Route.extend({
           router.send('showAlert', {'message': Em.I18n.t('udfs.alert.udf_created',{name : model.get('name')}), status:'success'});
         };
       var onFail = function(error){
-          router.send('showAlert', {'message':Em.I18n.t('udfs.alert.create_failed'),status:'error'});
+          var trace = null;
+          if (error && error.responseJSON.trace)
+            trace = error.responseJSON.trace;
+          router.send('showAlert', {'message':Em.I18n.t('udfs.alert.create_failed'),status:'error',trace:trace});
         };
       return udf.save().then(onSuccess,onFail);
     },
@@ -40,7 +43,10 @@ App.PigUdfsRoute = Em.Route.extend({
             router.send('showAlert', {'message': Em.I18n.t('udfs.alert.udf_deleted',{name : model.get('name')}),status:'success'});
           };
       var onFail = function(error){
-            router.send('showAlert', {'message': Em.I18n.t('udfs.alert.delete_failed'),status:'error'});
+            var trace = null;
+            if (error && error.responseJSON.trace)
+              trace = error.responseJSON.trace;
+            router.send('showAlert', {'message': Em.I18n.t('udfs.alert.delete_failed'),status:'error',trace:trace});
           };
       udf.deleteRecord();
       return udf.save().then(onSuccess,onFail);

+ 50 - 0
contrib/views/pig/src/main/resources/ui/pig-web/app/routes/splash.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.
+ */
+
+var App = require('app');
+
+App.SplashRoute = Em.Route.extend({
+  model: function() {
+    return Ember.Object.create({storageTest: null,
+                                storageTestDone: null,
+                                webhcatTest: null,
+                                webhcatTestDone: null,
+                                hdfsTest: null,
+                                hdfsTestDone: null,
+                                percent: 0});
+  },
+  renderTemplate: function() {
+    this.render('splash');
+  },
+  setupController: function(controller, model) {
+    controller.set('model', model);
+    var self = this;
+    controller.startTests(model).then(function() {
+      if (model.get("storageTest") && model.get("webhcatTest") && model.get("hdfsTest")) {
+        Ember.run.later(this, function() {
+          previousTransition = App.get('previousTransition');
+          if (previousTransition) {
+            previousTransition.retry();
+          } else {
+            self.transitionTo('pig.scriptList');
+          }
+        }, 1000);
+      }
+    });
+  }
+});

+ 7 - 0
contrib/views/pig/src/main/resources/ui/pig-web/app/styles/style.less

@@ -369,3 +369,10 @@ a.list-group-item.active{
   font-size: 1.1em;
   font-weight: bold;
 }
+
+.green {
+    color: green;
+}
+.red {
+    color: red;
+}

+ 23 - 0
contrib/views/pig/src/main/resources/ui/pig-web/app/templates/pig/errorLog.hbs

@@ -0,0 +1,23 @@
+{{!
+   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.
+}}
+
+<h1>{{t 'common.errorLog'}}</h1>
+
+<pre class="prettyprint">
+{{errorLog}}
+</pre>

+ 3 - 0
contrib/views/pig/src/main/resources/ui/pig-web/app/templates/pig/util/alert-content.hbs

@@ -20,3 +20,6 @@
   <p>
     {{view.content.message}}
   </p>
+  {{#if view.content.trace}}
+      <a href="#" {{action 'showErrorLog' this}}>{{t 'common.showErrorLog'}}</a>
+  {{/if}}

+ 96 - 0
contrib/views/pig/src/main/resources/ui/pig-web/app/templates/splash.hbs

@@ -0,0 +1,96 @@
+{{!
+   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 class="container-fluid">
+  <h1>Welcome to Apache Pig UI</h1>
+  <h2>Please wait while we test connection to services</h2>
+
+  <div class="progress progress-striped active">
+    <div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" {{bind-attr style="progressBarStyle"}}>
+    </div>
+  </div>
+
+  <table class="table">
+    <tbody>
+      <tr>
+        <td>
+          {{#if storageTestDone}}
+            {{#if storageTest}}
+              <span class="glyphicon glyphicon-ok green"></span>
+            {{else}}
+              <span class="glyphicon glyphicon-remove red"></span>
+            {{/if}}
+          {{else}}
+            <span class="glyphicon glyphicon-arrow-right"></span>
+          {{/if}}
+        </td>
+        <td>Storage test</td>
+      </tr>
+
+      <tr>
+          <td>
+            {{#if hdfsTestDone}}
+              {{#if hdfsTest}}
+                  <span class="glyphicon glyphicon-ok green"></span>
+              {{else}}
+                  <span class="glyphicon glyphicon-remove red"></span>
+              {{/if}}
+            {{else}}
+                <span class="glyphicon glyphicon-arrow-right"></span>
+            {{/if}}
+          </td>
+          <td>HDFS test</td>
+      </tr>
+
+      <tr>
+          <td>
+            {{#if webhcatTestDone}}
+              {{#if webhcatTest}}
+                  <span class="glyphicon glyphicon-ok green"></span>
+              {{else}}
+                  <span class="glyphicon glyphicon-remove red"></span>
+              {{/if}}
+            {{else}}
+                <span class="glyphicon glyphicon-arrow-right"></span>
+            {{/if}}
+          </td>
+          <td>WebHCat test</td>
+      </tr>
+
+    </tbody>
+  </table>
+
+  {{#if errors}}
+  <h3>Issues detected</h3>
+  <p>{{{errors}}}</p>
+  {{/if}}
+  {{#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>

+ 4 - 1
contrib/views/pig/src/main/resources/ui/pig-web/app/translations.js

@@ -30,7 +30,9 @@ Ember.I18n.translations = {
     'history':"History",
     'clone':"Clone",
     'cancel':"Cancel",
-    'arguments':"Arguments"
+    'arguments':"Arguments",
+    'errorLog':"Stack Trace",
+    'showErrorLog':"Show Stack Trace"
   },
   'scripts':{
     'scripts':"Scripts",
@@ -51,6 +53,7 @@ Ember.I18n.translations = {
       'confirm_delete_massage':'Are you sure you want to delete {{title}} script?'
     },
     'alert':{
+      'file_not_found':'File not found',
       'arg_present':'Argument already present',
       'file_exist_error':'File already exist',
       'script_saved':'{{title}} saved!',

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

@@ -42,7 +42,7 @@ exports.config =
 
 
   paths:
-    public: '/usr/lib/ambari-server/web/views-debug/PIG/PIG_1/'
+    public: '/usr/lib/ambari-server/web/views-debug/PIG/0.1.0/MyPig/'
 
   overrides:
     production:

+ 13 - 6
contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/FileTest.java

@@ -21,9 +21,13 @@ package org.apache.ambari.view.pig.test;
 import org.apache.ambari.view.pig.HDFSTest;
 import org.apache.ambari.view.pig.resources.files.FileResource;
 import org.apache.ambari.view.pig.resources.files.FileService;
+import org.apache.ambari.view.pig.utils.BadRequestFormattedException;
 import org.apache.ambari.view.pig.utils.FilePaginator;
+import org.apache.ambari.view.pig.utils.NotFoundFormattedException;
+import org.apache.ambari.view.pig.utils.ServiceFormattedException;
 import org.json.simple.JSONObject;
 import org.junit.*;
+import org.junit.rules.ExpectedException;
 
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.Response;
@@ -39,6 +43,8 @@ public class FileTest extends HDFSTest {
   private final static int PAGINATOR_PAGE_SIZE = 4;
   private FileService fileService;
 
+  @Rule public ExpectedException thrown = ExpectedException.none();
+
   @Override
   @Before
   public void setUp() throws Exception {
@@ -103,6 +109,7 @@ public class FileTest extends HDFSTest {
     Response response2 = doCreateFile("Leia", null, "/tmp/");
     Assert.assertEquals(204, response2.getStatus());
 
+    thrown.expect(ServiceFormattedException.class);
     Response response3 = doCreateFile("Leia", null, "/tmp/"); // file already exists
     Assert.assertEquals(400, response3.getStatus());
   }
@@ -163,8 +170,8 @@ public class FileTest extends HDFSTest {
     Assert.assertEquals(2, ((FileResource) obj.get("file")).getPage());
     Assert.assertFalse(((FileResource) obj.get("file")).isHasNext());
 
-    response = fileService.getFile(filePath, 3L);
-    Assert.assertEquals(400, response.getStatus()); //page not found
+    thrown.expect(BadRequestFormattedException.class);
+    fileService.getFile(filePath, 3L);
   }
 
   @Test
@@ -184,8 +191,8 @@ public class FileTest extends HDFSTest {
 
   @Test
   public void testFileNotFound() throws IOException, InterruptedException {
-    Response response1 = fileService.getFile("/tmp/notExistentFile", 2L);
-    Assert.assertEquals(404, response1.getStatus());
+    thrown.expect(NotFoundFormattedException.class);
+    fileService.getFile("/tmp/notExistentFile", 2L);
   }
 
   @Test
@@ -197,7 +204,7 @@ public class FileTest extends HDFSTest {
     Response response = fileService.deleteFile(filePath);
     Assert.assertEquals(204, response.getStatus());
 
-    Response response2 = fileService.getFile(filePath, 0L);
-    Assert.assertEquals(404, response2.getStatus());
+    thrown.expect(NotFoundFormattedException.class);
+    fileService.getFile(filePath, 0L);
   }
 }

+ 13 - 16
contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/JobTest.java

@@ -22,10 +22,14 @@ import org.apache.ambari.view.pig.BasePigTest;
 import org.apache.ambari.view.pig.resources.jobs.JobService;
 import org.apache.ambari.view.pig.resources.jobs.models.PigJob;
 import org.apache.ambari.view.pig.templeton.client.TempletonApi;
+import org.apache.ambari.view.pig.utils.BadRequestFormattedException;
 import org.apache.ambari.view.pig.utils.HdfsApi;
+import org.apache.ambari.view.pig.utils.NotFoundFormattedException;
+import org.apache.ambari.view.pig.utils.ServiceFormattedException;
 import org.apache.hadoop.fs.FSDataOutputStream;
 import org.json.simple.JSONObject;
 import org.junit.*;
+import org.junit.rules.ExpectedException;
 
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.Response;
@@ -39,6 +43,7 @@ import static org.easymock.EasyMock.*;
 
 public class JobTest extends BasePigTest {
   private JobService jobService;
+  @Rule public ExpectedException thrown = ExpectedException.none();
 
   @Override
   @Before
@@ -190,10 +195,8 @@ public class JobTest extends BasePigTest {
     expect(api.runPigQuery((File) anyObject(), anyString(), eq("-useHCatalog"))).andReturn(data);
     replay(api);
 
-    Response response = doCreateJob("Test", null, "-useHCatalog");
-    Assert.assertEquals(400, response.getStatus());
-    JSONObject obj = (JSONObject)response.getEntity();
-    Assert.assertTrue(((String)obj.get("message")).contains("No pigScript file or forcedContent specifed;"));
+    thrown.expect(ServiceFormattedException.class);
+    doCreateJob("Test", null, "-useHCatalog");
   }
 
   @Test
@@ -240,10 +243,8 @@ public class JobTest extends BasePigTest {
     expect(api.runPigQuery((File) anyObject(), anyString(), eq("-useHCatalog"))).andReturn(data);
     replay(api);
 
-    Response response = doCreateJob(null, "/tmp/1.pig", "-useHCatalog");
-    Assert.assertEquals(400, response.getStatus());
-    JSONObject obj = (JSONObject)response.getEntity();
-    Assert.assertTrue(((String)obj.get("message")).contains("No title specifed"));
+    thrown.expect(BadRequestFormattedException.class);
+    doCreateJob(null, "/tmp/1.pig", "-useHCatalog");
   }
 
   @Test
@@ -264,10 +265,8 @@ public class JobTest extends BasePigTest {
     expect(api.runPigQuery((File) anyObject(), anyString(), eq("-useHCatalog"))).andReturn(data);
     replay(api);
 
-    Response response = doCreateJob("Test", "/tmp/script.pig", "-useHCatalog");
-    Assert.assertEquals(500, response.getStatus());
-    JSONObject obj = (JSONObject)response.getEntity();
-    Assert.assertTrue(((String)obj.get("message")).contains("Can't copy"));
+    thrown.expect(ServiceFormattedException.class);
+    doCreateJob("Test", "/tmp/script.pig", "-useHCatalog");
   }
 
   @Test
@@ -289,10 +288,8 @@ public class JobTest extends BasePigTest {
     expect(api.runPigQuery((File) anyObject(), anyString(), eq("-useHCatalog"))).andThrow(new IOException());
     replay(api);
 
-    Response response = doCreateJob("Test", "/tmp/script.pig", "-useHCatalog");
-    Assert.assertEquals(500, response.getStatus());
-    JSONObject obj = (JSONObject)response.getEntity();
-    Assert.assertTrue(((String) obj.get("message")).contains("Templeton"));
+    thrown.expect(ServiceFormattedException.class);
+    doCreateJob("Test", "/tmp/script.pig", "-useHCatalog");
   }
 
   @Test

+ 8 - 4
contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/ScriptTest.java

@@ -22,8 +22,11 @@ import org.apache.ambari.view.pig.HDFSTest;
 import org.apache.ambari.view.pig.resources.files.FileService;
 import org.apache.ambari.view.pig.resources.scripts.ScriptService;
 import org.apache.ambari.view.pig.resources.scripts.models.PigScript;
+import org.apache.ambari.view.pig.utils.BadRequestFormattedException;
+import org.apache.ambari.view.pig.utils.NotFoundFormattedException;
 import org.json.simple.JSONObject;
 import org.junit.*;
+import org.junit.rules.ExpectedException;
 
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.Response;
@@ -38,6 +41,7 @@ import static org.easymock.EasyMock.*;
 
 public class ScriptTest extends HDFSTest {
     private ScriptService scriptService;
+    @Rule public ExpectedException thrown = ExpectedException.none();
 
     @BeforeClass
     public static void startUp() throws Exception {
@@ -114,8 +118,8 @@ public class ScriptTest extends HDFSTest {
 
     @Test
     public void scriptNotFound() {
-        Response response2 = scriptService.getScript("4242");
-        Assert.assertEquals(404, response2.getStatus());
+      thrown.expect(NotFoundFormattedException.class);
+      scriptService.getScript("4242");
     }
 
     @Test
@@ -146,8 +150,8 @@ public class ScriptTest extends HDFSTest {
         Response response = scriptService.deleteScript(createdScriptId);
         Assert.assertEquals(204, response.getStatus());
 
-        Response response2 = scriptService.getScript(createdScriptId);
-        Assert.assertEquals(404, response2.getStatus());
+        thrown.expect(NotFoundFormattedException.class);
+        scriptService.getScript(createdScriptId);
     }
 
     @Test

+ 7 - 2
contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/ScriptTestHDFSUnmanaged.java

@@ -29,11 +29,14 @@ import org.apache.ambari.view.pig.resources.scripts.ScriptService;
 import org.apache.ambari.view.pig.persistence.utils.StorageUtil;
 import org.apache.ambari.view.pig.resources.scripts.models.PigScript;
 import org.apache.ambari.view.pig.services.BaseService;
+import org.apache.ambari.view.pig.utils.MisconfigurationFormattedException;
+import org.apache.ambari.view.pig.utils.NotFoundFormattedException;
 import org.json.simple.JSONObject;
 import org.junit.*;
+import org.junit.rules.ExpectedException;
 
 import javax.ws.rs.core.Response;
-import javax.xml.ws.WebServiceException;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.HashMap;
@@ -43,6 +46,7 @@ import static org.easymock.EasyMock.*;
 
 public class ScriptTestHDFSUnmanaged extends HDFSTest {
   private ScriptService scriptService;
+  @Rule public ExpectedException thrown = ExpectedException.none();
 
   @BeforeClass
   public static void startUp() throws Exception {
@@ -64,7 +68,7 @@ public class ScriptTestHDFSUnmanaged extends HDFSTest {
     StorageUtil.setStorage(null);
   }
 
-  @Test(expected=WebServiceException.class)
+  @Test
   public void createScriptAutoCreateNoScriptsPath() throws IOException, InterruptedException {
     Map<String, String> properties = new HashMap<String, String>();
     baseDir = new File(DATA_DIRECTORY)
@@ -82,6 +86,7 @@ public class ScriptTestHDFSUnmanaged extends HDFSTest {
     replay(handler, context);
     scriptService = getService(ScriptService.class, handler, context);
 
+    thrown.expect(MisconfigurationFormattedException.class);
     doCreateScript("Test", null);
   }
 

+ 5 - 16
contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/ScriptTestUnmanaged.java

@@ -21,28 +21,15 @@ package org.apache.ambari.view.pig.test;
 import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.ViewResourceHandler;
 import org.apache.ambari.view.pig.BasePigTest;
-import org.apache.ambari.view.pig.HDFSTest;
 import org.apache.ambari.view.pig.resources.files.FileService;
 import org.apache.ambari.view.pig.resources.scripts.ScriptService;
-import org.apache.ambari.view.pig.resources.scripts.models.PigScript;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.FileUtil;
-import org.apache.hadoop.hdfs.MiniDFSCluster;
-import org.json.simple.JSONObject;
+import org.apache.ambari.view.pig.utils.MisconfigurationFormattedException;
 import org.junit.*;
-import org.junit.experimental.runners.Enclosed;
-import org.junit.runner.RunWith;
+import org.junit.rules.ExpectedException;
 
-import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriBuilder;
-import javax.ws.rs.core.UriInfo;
-import javax.xml.ws.WebServiceException;
 import java.io.File;
-import java.io.IOException;
-import java.net.URI;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 import static org.easymock.EasyMock.*;
@@ -51,6 +38,7 @@ import static org.easymock.EasyMock.*;
  * Tests without HDFS and predefined properties
  */
 public class ScriptTestUnmanaged extends BasePigTest {
+  @Rule public ExpectedException thrown = ExpectedException.none();
   private ScriptService scriptService;
   private File pigStorageFile;
   private File baseDir;
@@ -75,7 +63,7 @@ public class ScriptTestUnmanaged extends BasePigTest {
     return ScriptTest.doCreateScript(title, path, scriptService);
   }
 
-  @Test(expected=WebServiceException.class)
+  @Test
   public void createScriptAutoCreateNoDefaultFS() {
     Map<String, String> properties = new HashMap<String, String>();
     properties.put("dataworker.storagePath", pigStorageFile.toString());
@@ -87,6 +75,7 @@ public class ScriptTestUnmanaged extends BasePigTest {
     replay(handler, context);
     scriptService = getService(ScriptService.class, handler, context);
 
+    thrown.expect(MisconfigurationFormattedException.class);
     doCreateScript("Test", null);
   }
 }

+ 8 - 4
contrib/views/pig/src/test/java/org/apache/ambari/view/pig/test/UDFTest.java

@@ -21,10 +21,13 @@ package org.apache.ambari.view.pig.test;
 import org.apache.ambari.view.pig.BasePigTest;
 import org.apache.ambari.view.pig.resources.udf.UDFService;
 import org.apache.ambari.view.pig.resources.udf.models.UDF;
+import org.apache.ambari.view.pig.utils.NotFoundFormattedException;
 import org.json.simple.JSONObject;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.Response;
@@ -36,6 +39,7 @@ import static org.easymock.EasyMock.*;
 import static org.easymock.EasyMock.replay;
 
 public class UDFTest extends BasePigTest {
+  @Rule public ExpectedException thrown = ExpectedException.none();
   private UDFService udfService;
 
   @Override
@@ -76,8 +80,8 @@ public class UDFTest extends BasePigTest {
 
   @Test
   public void udfNotFound() {
-    Response response2 = udfService.getUDF("4242");
-    Assert.assertEquals(404, response2.getStatus());
+    thrown.expect(NotFoundFormattedException.class);
+    udfService.getUDF("4242");
   }
 
   @Test
@@ -110,7 +114,7 @@ public class UDFTest extends BasePigTest {
     Response response = udfService.deleteUDF(createdUdfId);
     Assert.assertEquals(204, response.getStatus());
 
-    Response response2 = udfService.getUDF(createdUdfId);
-    Assert.assertEquals(404, response2.getStatus());
+    thrown.expect(NotFoundFormattedException.class);
+    udfService.getUDF(createdUdfId);
   }
 }