oleewere closed pull request #20: AMBARI-24969 - Infra Manager: REST api cleanup
URL: https://github.com/apache/ambari-infra/pull/20
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/ambari-infra-manager-it/pom.xml b/ambari-infra-manager-it/pom.xml
index f1f296bc..87457133 100644
--- a/ambari-infra-manager-it/pom.xml
+++ b/ambari-infra-manager-it/pom.xml
@@ -33,9 +33,12 @@
 
   <properties>
     <jbehave.version>4.0.5</jbehave.version>
-    <failsafe-plugin.version>2.20</failsafe-plugin.version>
-    <infra-manager.docker.host>localhost</infra-manager.docker.host>
+    <failsafePlugin.version>2.20</failsafePlugin.version>
+    <infraManager.docker.host>localhost</infraManager.docker.host>
     <stories.location>NONE</stories.location>
+    <okhttp.version>2.7.5</okhttp.version>
+    
<generatedSourcesPath>${project.build.directory}/generated-sources</generatedSourcesPath>
+    <generatedSourcesJavaPath>main/java</generatedSourcesJavaPath>
   </properties>
 
   <dependencies>
@@ -167,6 +170,32 @@
       <artifactId>log4j-core</artifactId>
       <version>2.11.1</version>
     </dependency>
+    <!-- swagger client related -->
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.swagger</groupId>
+      <artifactId>swagger-annotations</artifactId>
+      <version>1.5.21</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>io.gsonfire</groupId>
+      <artifactId>gson-fire</artifactId>
+      <version>1.8.3</version>
+    </dependency>
+    <dependency>
+      <groupId>com.squareup.okhttp</groupId>
+      <artifactId>okhttp</artifactId>
+      <version>${okhttp.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.squareup.okhttp</groupId>
+      <artifactId>logging-interceptor</artifactId>
+      <version>${okhttp.version}</version>
+    </dependency>
   </dependencies>
 
   <build>
@@ -182,6 +211,53 @@
         <directory>src/test/resources</directory>
       </testResource>
     </testResources>
+    <plugins>
+      <plugin>
+        <groupId>io.swagger</groupId>
+        <artifactId>swagger-codegen-maven-plugin</artifactId>
+        <version>2.3.1</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>generate</goal>
+            </goals>
+            <configuration>
+              
<inputSpec>${project.parent.basedir}/ambari-infra-manager/src/main/resources/swagger/swagger.yaml</inputSpec>
+              <language>java</language>
+              <generateApiTests>false</generateApiTests>
+              <generateModelTests>false</generateModelTests>
+              <output>${generatedSourcesPath}</output>
+              <configOptions>
+                <sourceFolder>${generatedSourcesJavaPath}</sourceFolder>
+                <apiPackage>org.apache.ambari.infra.client.api</apiPackage>
+                
<modelPackage>org.apache.ambari.infra.client.model</modelPackage>
+                
<invokerPackage>org.apache.ambari.infra.client.invoker</invokerPackage>
+                <dateLibrary>java8</dateLibrary>
+              </configOptions>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>3.0.0</version>
+        <executions>
+          <execution>
+            <id>add-generated-source</id>
+            <phase>initialize</phase>
+            <goals>
+              <goal>add-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                
<source>${generatedSourcesPath}/${generatedSourcesJavaPath}</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
   </build>
 
   <profiles>
@@ -197,7 +273,7 @@
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-failsafe-plugin</artifactId>
-            <version>${failsafe-plugin.version}</version>
+            <version>${failsafePlugin.version}</version>
             <executions>
               <execution>
                 <id>run-integration-tests</id>
@@ -212,7 +288,7 @@
                   </includes>
                   <systemPropertyVariables>
                     
<log4j.configuration>file:${project.build.testOutputDirectory}/log4j.properties</log4j.configuration>
-                    <docker.host>${infra-manager.docker.host}</docker.host>
+                    <docker.host>${infraManager.docker.host}</docker.host>
                     
<backend.stories.location>${stories.location}</backend.stories.location>
                   </systemPropertyVariables>
                 </configuration>
diff --git 
a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/InfraClient.java
 
b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/InfraClient.java
index 1b0b4cf0..bce3c7e6 100644
--- 
a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/InfraClient.java
+++ 
b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/InfraClient.java
@@ -18,134 +18,59 @@
  */
 package org.apache.ambari.infra;
 
-import static org.apache.commons.lang.StringUtils.isBlank;
+import java.util.List;
 
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.Charset;
-import java.util.HashMap;
-import java.util.Map;
+import org.apache.ambari.infra.client.api.JobsApi;
+import org.apache.ambari.infra.client.invoker.ApiClient;
+import org.apache.ambari.infra.client.invoker.ApiException;
+import org.apache.ambari.infra.client.model.JobExecutionInfoResponse;
 
-import org.apache.commons.io.IOUtils;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.CredentialsProvider;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpDelete;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.impl.client.BasicCredentialsProvider;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-// TODO: use swagger
-public class InfraClient implements AutoCloseable {
-  private static final Logger logger = LogManager.getLogger(InfraClient.class);
-
-  private final CloseableHttpClient httpClient;
-  private final URI baseUrl;
+public class InfraClient {
+  private final JobsApi jobsApi;
 
   public InfraClient(String baseUrl) {
+    ApiClient apiClient = new ApiClient().setBasePath(baseUrl);
+    apiClient.setUsername("admin");
+    apiClient.setPassword("admin");
+    this.jobsApi = new JobsApi(apiClient);
+  }
+
+  public List<String> getJobs() {
     try {
-      this.baseUrl = new URI(baseUrl);
-    } catch (URISyntaxException e) {
+      return jobsApi.getAllJobNames();
+    } catch (ApiException e) {
       throw new RuntimeException(e);
     }
-
-    CredentialsProvider provider = new BasicCredentialsProvider();
-    UsernamePasswordCredentials credentials
-            = new UsernamePasswordCredentials("admin", "admin");
-    provider.setCredentials(AuthScope.ANY, credentials);
-
-    httpClient = HttpClientBuilder.create()
-            .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
-            .setDefaultCredentialsProvider(provider)
-            .build();
-  }
-
-  @Override
-  public void close() throws Exception {
-    httpClient.close();
   }
 
-  // TODO: return job data
-  public void getJobs() {
-    execute(new HttpGet(baseUrl));
-  }
-
-  private HttpResponse execute(HttpRequestBase post) {
-    try (CloseableHttpResponse response = httpClient.execute(post)) {
-      String responseBodyText = 
IOUtils.toString(response.getEntity().getContent(), Charset.defaultCharset());
-      int statusCode = response.getStatusLine().getStatusCode();
-      logger.info("Response code {} body {} ", statusCode, responseBodyText);
-      if (!(200 <= statusCode && statusCode <= 299))
-        throw new RuntimeException("Error while executing http request: " + 
responseBodyText);
-      return new HttpResponse(statusCode, responseBodyText);
-    } catch (ClientProtocolException e) {
+  public JobExecutionInfoResponse startJob(String jobName, String parameters) {
+    try {
+      return jobsApi.startJob(jobName, parameters);
+    } catch (ApiException e) {
       throw new RuntimeException(e);
-    } catch (IOException e) {
-      throw new UncheckedIOException(e);
     }
   }
 
-  public JobExecutionInfo startJob(String jobName, String parameters) {
-    URIBuilder uriBuilder = new URIBuilder(baseUrl);
-    uriBuilder.setScheme("http");
-    uriBuilder.setPath(uriBuilder.getPath() + "/" + jobName);
-    if (!isBlank(parameters))
-      uriBuilder.addParameter("params", parameters);
+  public void restartJob(String jobName, long jobId) {
     try {
-      String responseText = execute(new 
HttpPost(uriBuilder.build())).getBody();
-      Map<String, Object> responseContent = new 
ObjectMapper().readValue(responseText, new 
TypeReference<HashMap<String,Object>>() {});
-      if (!responseContent.containsKey("jobId"))
-        throw new NullPointerException("jobId is not found in start job 
responseContent");
-      if (!responseContent.containsKey("jobExecutionData"))
-        throw new NullPointerException("jobExecutionData is not found in start 
job responseContent");
-      if (!((Map)responseContent.get("jobExecutionData")).containsKey("id"))
-        throw new NullPointerException("id is not found in jobExecutionData");
-      return new JobExecutionInfo(responseContent.get("jobId").toString(), 
((Map)responseContent.get("jobExecutionData")).get("id").toString());
-    } catch (URISyntaxException | JsonParseException | JsonMappingException e) 
{
+      jobsApi.restartJobInstance(jobName, jobId, "RESTART");
+    } catch (ApiException e) {
       throw new RuntimeException(e);
-    } catch (IOException e) {
-      throw new UncheckedIOException(e);
     }
   }
 
-  public void restartJob(String jobName, String jobId) {
-    URIBuilder uriBuilder = new URIBuilder(baseUrl);
-    uriBuilder.setScheme("http");
-    uriBuilder.setPath(String.format("%s/%s/%s/executions", 
uriBuilder.getPath(), jobName, jobId));
-    uriBuilder.addParameter("operation", "RESTART");
+  public void stopJob(long jobExecutionId) {
     try {
-      HttpResponse httpResponse = execute(new HttpPost(uriBuilder.build()));
-      if (httpResponse.getCode() != 200)
-        throw new RuntimeException(httpResponse.getBody());
-    } catch (URISyntaxException e) {
+      jobsApi.stopOrAbandonJobExecution(jobExecutionId, "STOP");
+    } catch (ApiException e) {
       throw new RuntimeException(e);
     }
   }
 
-  public void stopJob(String jobExecutionId) {
-    URIBuilder uriBuilder = new URIBuilder(baseUrl);
-    uriBuilder.setScheme("http");
-    uriBuilder.setPath(String.format("%s/executions/%s", uriBuilder.getPath(), 
jobExecutionId));
-    uriBuilder.addParameter("operation", "STOP");
+  public boolean isRunning(String jobName) {
     try {
-      execute(new HttpDelete(uriBuilder.build()));
-    } catch (URISyntaxException e) {
+      return !jobsApi.getExecutionIdsByJobName(jobName).isEmpty();
+    } catch (ApiException e) {
       throw new RuntimeException(e);
     }
   }
diff --git 
a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/AbstractInfraSteps.java
 
b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/AbstractInfraSteps.java
index 9ecf489a..985fda54 100644
--- 
a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/AbstractInfraSteps.java
+++ 
b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/AbstractInfraSteps.java
@@ -47,17 +47,16 @@
 
   private static final int INFRA_MANAGER_PORT = 61890;
   private static final int FAKE_S3_PORT = 4569;
-  private static final int HDFS_PORT = 9000;
   protected static final String S3_BUCKET_NAME = "testbucket";
   private String ambariFolder;
   private String shellScriptLocation;
-  private String dockerHost;
   private S3Client s3client;
   private int documentId = 0;
   private Solr solr;
+  private InfraClient infraClient;
 
   public InfraClient getInfraClient() {
-    return new InfraClient(String.format("http://%s:%d/api/v1/jobs";, 
dockerHost, INFRA_MANAGER_PORT));
+    return infraClient;
   }
 
   public Solr getSolr() {
@@ -89,13 +88,16 @@ public void initDockerContainer() throws Exception {
       FileUtils.cleanDirectory(new File(localDataFolder));
     }
 
+    logger.info("Copy resources");
     FileUtils.copyDirectory(new ClassPathResource("conf").getFile(), new 
File(getInfraManagerConfDir()));
 
     shellScriptLocation = ambariFolder + 
"/ambari-infra/ambari-infra-manager/docker/infra-manager-docker-compose.sh";
     logger.info("Create new docker container for testing Ambari Infra Manager 
...");
     runCommand(new String[]{shellScriptLocation, "start"});
 
-    dockerHost = getDockerHost();
+    String dockerHost = getDockerHost();
+
+    this.infraClient = new InfraClient(String.format("http://%s:%d/api/v1";, 
dockerHost, INFRA_MANAGER_PORT));
 
     solr = new Solr();
     solr.waitUntilSolrIsUp();
@@ -110,11 +112,10 @@ public void initDockerContainer() throws Exception {
     checkInfraManagerReachable();
   }
 
-  private void checkInfraManagerReachable() throws Exception {
-    try (InfraClient httpClient = getInfraClient()) {
-      doWithin(30, "Start Ambari Infra Manager", httpClient::getJobs);
-      logger.info("Ambari Infra Manager is up and running");
-    }
+  private void checkInfraManagerReachable() {
+    InfraClient infraClient = getInfraClient();
+    doWithin(30, "Start Ambari Infra Manager", infraClient::getJobs);
+    logger.info("Ambari Infra Manager is up and running");
   }
 
   protected SolrInputDocument addDocument(OffsetDateTime logtime) {
@@ -149,7 +150,7 @@ protected SolrInputDocument addDocument(OffsetDateTime 
logtime) {
     solrInputDocument.addField("logtime", new 
Date(logtime.toInstant().toEpochMilli()));
     solrInputDocument.addField("evtTime", new 
Date(logtime.toInstant().toEpochMilli()));
     solrInputDocument.addField("_ttl_", "+7DAYS");
-    solrInputDocument.addField("_expire_at_", "2017-12-15T10:23:19.106Z");
+    solrInputDocument.addField("_expire_at_", new 
Date(logtime.plusDays(7).toInstant().toEpochMilli()));
     solr.add(solrInputDocument);
     return solrInputDocument;
   }
diff --git 
a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/ExportJobsSteps.java
 
b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/ExportJobsSteps.java
index e67762e3..08252900 100644
--- 
a/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/ExportJobsSteps.java
+++ 
b/ambari-infra-manager-it/src/test/java/org/apache/ambari/infra/steps/ExportJobsSteps.java
@@ -40,8 +40,8 @@
 import java.util.Set;
 
 import org.apache.ambari.infra.InfraClient;
-import org.apache.ambari.infra.JobExecutionInfo;
 import org.apache.ambari.infra.S3Client;
+import org.apache.ambari.infra.client.model.JobExecutionInfoResponse;
 import 
org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -58,7 +58,7 @@
   private static final Logger logger = 
LogManager.getLogger(ExportJobsSteps.class);
   private Set<String> documentIds = new HashSet<>();
 
-  private Map<String, JobExecutionInfo> launchedJobs = new HashMap<>();
+  private Map<String, JobExecutionInfoResponse> launchedJobs = new HashMap<>();
 
   @Given("$count documents in solr")
   public void addDocuments(int count) {
@@ -94,32 +94,24 @@ public void startJob(String jobName) throws Exception {
   @When("start $jobName job with parameters $parameters after $waitSec 
seconds")
   public void startJob(String jobName, String parameters, int waitSec) throws 
Exception {
     Thread.sleep(waitSec * 1000);
-    try (InfraClient httpClient = getInfraClient()) {
-      JobExecutionInfo jobExecutionInfo = httpClient.startJob(jobName, 
parameters);
-      logger.info("Job {} started: {}", jobName, jobExecutionInfo);
-      launchedJobs.put(jobName, jobExecutionInfo);
-    }
+    JobExecutionInfoResponse jobExecutionInfo = 
getInfraClient().startJob(jobName, parameters);
+    logger.info("Job {} started: {}", jobName, jobExecutionInfo);
+    launchedJobs.put(jobName, jobExecutionInfo);
   }
 
   @When("restart $jobName job within $waitSec seconds")
   public void restartJob(String jobName, int waitSec) {
-    doWithin(waitSec, "Restarting job " + jobName, () -> {
-      try (InfraClient httpClient = getInfraClient()) {
-        httpClient.restartJob(jobName, launchedJobs.get(jobName).getJobId());
-      } catch (Exception e) {
-        throw new RuntimeException(e);
-      }
-    });
+    doWithin(waitSec, "Restarting job " + jobName, () ->
+            getInfraClient().restartJob(jobName, 
launchedJobs.get(jobName).getJobInstanceId()));
   }
 
   @When("stop job $jobName after at least $count file exists in s3 with 
filename containing text $text within $waitSec seconds")
   public void stopJob(String jobName, int count, String text, int waitSec) 
throws Exception {
     S3Client s3Client = getS3client();
     doWithin(waitSec, "check uploaded files to s3", () -> 
s3Client.listObjectKeys(text).size() > count);
-
-    try (InfraClient httpClient = getInfraClient()) {
-      httpClient.stopJob(launchedJobs.get(jobName).getExecutionId());
-    }
+    InfraClient infraClient = getInfraClient();
+    infraClient.stopJob(launchedJobs.get(jobName).getJobExecutionId());
+    doWithin(waitSec, String.format("Wait for job %s stops", jobName), () -> 
infraClient.isRunning(jobName));
   }
 
   @When("delete file with key $key from s3")
@@ -182,7 +174,7 @@ private boolean isSolrEmpty(SolrQuery query) {
 
   @Then("Check $count files exists on local filesystem with filenames 
containing the text $text in the folder $path for job $jobName")
   public void checkNumberOfFilesOnLocalFilesystem(long count, String text, 
String path, String jobName) {
-    File destinationDirectory = new File(getLocalDataFolder(), 
path.replace("${jobId}", launchedJobs.get(jobName).getJobId()));
+    File destinationDirectory = new File(getLocalDataFolder(), 
path.replace("${jobId}", 
Long.toString(launchedJobs.get(jobName).getJobInstanceId())));
     logger.info("Destination directory path: {}", 
destinationDirectory.getAbsolutePath());
     doWithin(5, "Destination directory exists", destinationDirectory::exists);
 
@@ -216,7 +208,21 @@ public void checkStoredDocumentIds(String fileNamePart) 
throws Exception {
   }
 
   @AfterScenario
-  public void waitABit() throws InterruptedException {
-    Thread.sleep(5000);
+  public void waitForJobStops() throws InterruptedException {
+    InfraClient infraClient = getInfraClient();
+    doWithin(20, "Stop all launched jobs", () -> {
+      int runningJobCount = 0;
+      for (String jobName : launchedJobs.keySet()) {
+        if (launchedJobs.get(jobName) == null)
+          continue;
+        if (!infraClient.isRunning(jobName)) {
+          launchedJobs.put(jobName, null);
+        }
+        else {
+          ++runningJobCount;
+        }
+      }
+      return runningJobCount == 0;
+    });
   }
 }
diff --git a/ambari-infra-manager-it/src/test/resources/log4j.properties 
b/ambari-infra-manager-it/src/test/resources/log4j2-test.properties
similarity index 57%
rename from ambari-infra-manager-it/src/test/resources/log4j.properties
rename to ambari-infra-manager-it/src/test/resources/log4j2-test.properties
index 956bc636..4e488fde 100644
--- a/ambari-infra-manager-it/src/test/resources/log4j.properties
+++ b/ambari-infra-manager-it/src/test/resources/log4j2-test.properties
@@ -9,8 +9,17 @@
 #   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.
-log4j.rootLogger=INFO, stdout
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.Target=System.out
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p 
%c{1}:%L - %m%n
\ No newline at end of file
+
+status = error
+dest = err
+name = PropertiesConfig
+
+appender.console.type = Console
+appender.console.name = STDOUT
+appender.console.layout.type = PatternLayout
+appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+appender.console.filter.threshold.type = ThresholdFilter
+appender.console.filter.threshold.level = info
+
+rootLogger.level = info
+rootLogger.appenderRef.stdout.ref = STDOUT
diff --git a/ambari-infra-manager/.gitignore b/ambari-infra-manager/.gitignore
index 4aece788..dd3661e0 100644
--- a/ambari-infra-manager/.gitignore
+++ b/ambari-infra-manager/.gitignore
@@ -3,4 +3,6 @@ out/*
 Profile
 .env
 test-out
-test.db
\ No newline at end of file
+test.db
+**/swagger/swagger.json
+**/swagger/swagger.yaml
\ No newline at end of file
diff --git a/ambari-infra-manager/docs/api/swagger.yaml 
b/ambari-infra-manager/docs/api/swagger.yaml
deleted file mode 100644
index 6fad22df..00000000
--- a/ambari-infra-manager/docs/api/swagger.yaml
+++ /dev/null
@@ -1,784 +0,0 @@
----
-swagger: "2.0"
-info:
-  description: "Manager component for Ambari Infra"
-  version: "1.0.0"
-  title: "Infra Manager REST API"
-  license:
-    name: "Apache 2.0"
-    url: "http://www.apache.org/licenses/LICENSE-2.0.html";
-basePath: "/api/v1"
-tags:
-- name: "jobs"
-schemes:
-- "http"
-- "https"
-paths:
-  /jobs:
-    get:
-      tags:
-      - "jobs"
-      summary: "Get all jobs"
-      description: ""
-      operationId: "getAllJobs"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "page"
-        in: "query"
-        required: false
-        type: "integer"
-        default: 0
-        format: "int32"
-      - name: "size"
-        in: "query"
-        required: false
-        type: "integer"
-        default: 20
-        format: "int32"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            type: "array"
-            items:
-              $ref: "#/definitions/JobInfo"
-  /jobs/executions:
-    delete:
-      tags:
-      - "jobs"
-      summary: "Stop all job executions."
-      description: ""
-      operationId: "stopAll"
-      produces:
-      - "application/json"
-      parameters: []
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            type: "integer"
-            format: "int32"
-  /jobs/executions/{jobExecutionId}:
-    get:
-      tags:
-      - "jobs"
-      summary: "Get job and step details for job execution instance."
-      description: ""
-      operationId: "getExecutionInfo"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "jobExecutionId"
-        in: "path"
-        required: true
-        type: "integer"
-        format: "int64"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            $ref: "#/definitions/JobExecutionDetailsResponse"
-    delete:
-      tags:
-      - "jobs"
-      summary: "Stop or abandon a running job execution."
-      description: ""
-      operationId: "stopOrAbandonJobExecution"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "jobExecutionId"
-        in: "path"
-        required: true
-        type: "integer"
-        format: "int64"
-      - name: "operation"
-        in: "query"
-        required: true
-        type: "string"
-        enum:
-        - "STOP"
-        - "ABANDON"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            $ref: "#/definitions/JobExecutionInfoResponse"
-  /jobs/executions/{jobExecutionId}/context:
-    get:
-      tags:
-      - "jobs"
-      summary: "Get execution context for specific job."
-      description: ""
-      operationId: "getExecutionContextByJobExecId"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "jobExecutionId"
-        in: "path"
-        required: true
-        type: "integer"
-        format: "int64"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            $ref: "#/definitions/ExecutionContextResponse"
-  /jobs/executions/{jobExecutionId}/steps/{stepExecutionId}:
-    get:
-      tags:
-      - "jobs"
-      summary: "Get step execution details."
-      description: ""
-      operationId: "getStepExecution"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "jobExecutionId"
-        in: "path"
-        required: true
-        type: "integer"
-        format: "int64"
-      - name: "stepExecutionId"
-        in: "path"
-        required: true
-        type: "integer"
-        format: "int64"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            $ref: "#/definitions/StepExecutionInfoResponse"
-  /jobs/executions/{jobExecutionId}/steps/{stepExecutionId}/execution-context:
-    get:
-      tags:
-      - "jobs"
-      summary: "Get the execution context of step execution."
-      description: ""
-      operationId: "getStepExecutionContext"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "jobExecutionId"
-        in: "path"
-        required: true
-        type: "integer"
-        format: "int64"
-      - name: "stepExecutionId"
-        in: "path"
-        required: true
-        type: "integer"
-        format: "int64"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            $ref: "#/definitions/StepExecutionContextResponse"
-  /jobs/executions/{jobExecutionId}/steps/{stepExecutionId}/progress:
-    get:
-      tags:
-      - "jobs"
-      summary: "Get progress of step execution."
-      description: ""
-      operationId: "getStepExecutionProgress"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "jobExecutionId"
-        in: "path"
-        required: true
-        type: "integer"
-        format: "int64"
-      - name: "stepExecutionId"
-        in: "path"
-        required: true
-        type: "integer"
-        format: "int64"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            $ref: "#/definitions/StepExecutionProgressResponse"
-  /jobs/info/names:
-    get:
-      tags:
-      - "jobs"
-      summary: "Get all job names"
-      description: ""
-      operationId: "getAllJobNames"
-      produces:
-      - "application/json"
-      parameters: []
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            type: "array"
-            uniqueItems: true
-            items:
-              type: "string"
-  /jobs/{jobName}:
-    post:
-      tags:
-      - "jobs"
-      summary: "Start a new job instance by job name."
-      description: ""
-      operationId: "startJob"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "jobName"
-        in: "path"
-        required: true
-        type: "string"
-      - name: "params"
-        in: "query"
-        required: false
-        type: "string"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            $ref: "#/definitions/JobExecutionInfoResponse"
-  /jobs/{jobName}/executions:
-    get:
-      tags:
-      - "jobs"
-      summary: "Get the id values of all the running job instances."
-      description: ""
-      operationId: "getExecutionIdsByJobName"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "jobName"
-        in: "path"
-        required: true
-        type: "string"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            type: "array"
-            uniqueItems: true
-            items:
-              type: "integer"
-              format: "int64"
-  /jobs/{jobName}/info:
-    get:
-      tags:
-      - "jobs"
-      summary: "Get job details by job name."
-      description: ""
-      operationId: "getJobDetails"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "page"
-        in: "query"
-        required: false
-        type: "integer"
-        default: 0
-        format: "int32"
-      - name: "size"
-        in: "query"
-        required: false
-        type: "integer"
-        default: 20
-        format: "int32"
-      - name: "jobName"
-        in: "path"
-        required: true
-        type: "string"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            $ref: "#/definitions/JobDetailsResponse"
-  /jobs/{jobName}/{jobInstanceId}/executions:
-    get:
-      tags:
-      - "jobs"
-      summary: "Get execution for job instance."
-      description: ""
-      operationId: "getExecutionsForInstance"
-      produces:
-      - "application/json"
-      parameters:
-      - name: "jobName"
-        in: "path"
-        required: true
-        type: "string"
-      - name: "jobInstanceId"
-        in: "path"
-        required: true
-        type: "integer"
-        format: "int64"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            type: "array"
-            items:
-              $ref: "#/definitions/JobExecutionInfoResponse"
-    post:
-      tags:
-      - "jobs"
-      summary: "Restart job instance."
-      description: ""
-      operationId: "restartJobInstance"
-      produces:
-      - "application/json"
-      parameters:
-      - in: "body"
-        name: "body"
-        required: false
-        schema:
-          $ref: "#/definitions/JobExecutionRestartRequest"
-      responses:
-        200:
-          description: "successful operation"
-          schema:
-            $ref: "#/definitions/JobExecutionInfoResponse"
-definitions:
-  JobExecutionData:
-    type: "object"
-    properties:
-      id:
-        type: "integer"
-        format: "int64"
-      executionContext:
-        $ref: "#/definitions/ExecutionContext"
-      jobInstance:
-        $ref: "#/definitions/JobInstance"
-      jobId:
-        type: "integer"
-        format: "int64"
-      jobParameters:
-        $ref: "#/definitions/JobParameters"
-      failureExceptions:
-        type: "array"
-        items:
-          $ref: "#/definitions/Throwable"
-      endTime:
-        type: "string"
-        format: "date-time"
-      exitStatus:
-        $ref: "#/definitions/ExitStatus"
-      createTime:
-        type: "string"
-        format: "date-time"
-      lastUpdated:
-        type: "string"
-        format: "date-time"
-      jobConfigurationName:
-        type: "string"
-      startTime:
-        type: "string"
-        format: "date-time"
-      status:
-        type: "string"
-        enum:
-        - "COMPLETED"
-        - "STARTING"
-        - "STARTED"
-        - "STOPPING"
-        - "STOPPED"
-        - "FAILED"
-        - "ABANDONED"
-        - "UNKNOWN"
-      stepExecutionDataList:
-        type: "array"
-        items:
-          $ref: "#/definitions/StepExecutionData"
-  JobInstance:
-    type: "object"
-    properties:
-      id:
-        type: "integer"
-        format: "int64"
-      version:
-        type: "integer"
-        format: "int32"
-      jobName:
-        type: "string"
-      instanceId:
-        type: "integer"
-        format: "int64"
-  StepExecutionData:
-    type: "object"
-    properties:
-      id:
-        type: "integer"
-        format: "int64"
-      jobExecutionId:
-        type: "integer"
-        format: "int64"
-      executionContext:
-        $ref: "#/definitions/ExecutionContext"
-      stepName:
-        type: "string"
-      terminateOnly:
-        type: "boolean"
-        default: false
-      failureExceptions:
-        type: "array"
-        items:
-          $ref: "#/definitions/Throwable"
-      endTime:
-        type: "string"
-        format: "date-time"
-      exitStatus:
-        $ref: "#/definitions/ExitStatus"
-      lastUpdated:
-        type: "string"
-        format: "date-time"
-      commitCount:
-        type: "integer"
-        format: "int32"
-      readCount:
-        type: "integer"
-        format: "int32"
-      filterCount:
-        type: "integer"
-        format: "int32"
-      writeCount:
-        type: "integer"
-        format: "int32"
-      readSkipCount:
-        type: "integer"
-        format: "int32"
-      writeSkipCount:
-        type: "integer"
-        format: "int32"
-      processSkipCount:
-        type: "integer"
-        format: "int32"
-      rollbackCount:
-        type: "integer"
-        format: "int32"
-      startTime:
-        type: "string"
-        format: "date-time"
-      status:
-        type: "string"
-        enum:
-        - "COMPLETED"
-        - "STARTING"
-        - "STARTED"
-        - "STOPPING"
-        - "STOPPED"
-        - "FAILED"
-        - "ABANDONED"
-        - "UNKNOWN"
-  StackTraceElement:
-    type: "object"
-    properties:
-      methodName:
-        type: "string"
-      fileName:
-        type: "string"
-      lineNumber:
-        type: "integer"
-        format: "int32"
-      className:
-        type: "string"
-      nativeMethod:
-        type: "boolean"
-        default: false
-  JobExecutionDetailsResponse:
-    type: "object"
-    properties:
-      jobExecutionInfoResponse:
-        $ref: "#/definitions/JobExecutionInfoResponse"
-      stepExecutionInfoList:
-        type: "array"
-        items:
-          $ref: "#/definitions/StepExecutionInfoResponse"
-  StepExecutionContextResponse:
-    type: "object"
-    properties:
-      executionContextMap:
-        type: "object"
-        additionalProperties:
-          type: "object"
-      jobExecutionId:
-        type: "integer"
-        format: "int64"
-      stepExecutionId:
-        type: "integer"
-        format: "int64"
-      stepName:
-        type: "string"
-  StepExecutionProgress:
-    type: "object"
-    properties:
-      estimatedPercentCompleteMessage:
-        $ref: "#/definitions/MessageSourceResolvable"
-      estimatedPercentComplete:
-        type: "number"
-        format: "double"
-  ExitStatus:
-    type: "object"
-    properties:
-      exitCode:
-        type: "string"
-      exitDescription:
-        type: "string"
-      running:
-        type: "boolean"
-        default: false
-  ExecutionContextResponse:
-    type: "object"
-    properties:
-      jobExecutionId:
-        type: "integer"
-        format: "int64"
-      executionContextMap:
-        type: "object"
-        additionalProperties:
-          type: "object"
-  StepExecutionHistory:
-    type: "object"
-    properties:
-      stepName:
-        type: "string"
-      count:
-        type: "integer"
-        format: "int32"
-      commitCount:
-        $ref: "#/definitions/CumulativeHistory"
-      rollbackCount:
-        $ref: "#/definitions/CumulativeHistory"
-      readCount:
-        $ref: "#/definitions/CumulativeHistory"
-      writeCount:
-        $ref: "#/definitions/CumulativeHistory"
-      filterCount:
-        $ref: "#/definitions/CumulativeHistory"
-      readSkipCount:
-        $ref: "#/definitions/CumulativeHistory"
-      writeSkipCount:
-        $ref: "#/definitions/CumulativeHistory"
-      processSkipCount:
-        $ref: "#/definitions/CumulativeHistory"
-      duration:
-        $ref: "#/definitions/CumulativeHistory"
-      durationPerRead:
-        $ref: "#/definitions/CumulativeHistory"
-  TimeZone:
-    type: "object"
-    properties:
-      displayName:
-        type: "string"
-      id:
-        type: "string"
-      dstsavings:
-        type: "integer"
-        format: "int32"
-      rawOffset:
-        type: "integer"
-        format: "int32"
-  MessageSourceResolvable:
-    type: "object"
-    properties:
-      arguments:
-        type: "array"
-        items:
-          type: "object"
-      codes:
-        type: "array"
-        items:
-          type: "string"
-      defaultMessage:
-        type: "string"
-  ExecutionContext:
-    type: "object"
-    properties:
-      dirty:
-        type: "boolean"
-        default: false
-      empty:
-        type: "boolean"
-        default: false
-  StepExecutionInfoResponse:
-    type: "object"
-    properties:
-      id:
-        type: "integer"
-        format: "int64"
-      jobExecutionId:
-        type: "integer"
-        format: "int64"
-      jobName:
-        type: "string"
-      name:
-        type: "string"
-      startDate:
-        type: "string"
-      startTime:
-        type: "string"
-      duration:
-        type: "string"
-      durationMillis:
-        type: "integer"
-        format: "int64"
-      exitCode:
-        type: "string"
-      status:
-        type: "string"
-  JobExecutionInfoResponse:
-    type: "object"
-    properties:
-      id:
-        type: "integer"
-        format: "int64"
-      stepExecutionCount:
-        type: "integer"
-        format: "int32"
-      jobId:
-        type: "integer"
-        format: "int64"
-      jobName:
-        type: "string"
-      startDate:
-        type: "string"
-      startTime:
-        type: "string"
-      duration:
-        type: "string"
-      jobExecutionData:
-        $ref: "#/definitions/JobExecutionData"
-      jobParameters:
-        type: "object"
-        additionalProperties:
-          type: "object"
-      jobParametersString:
-        type: "string"
-      restartable:
-        type: "boolean"
-        default: false
-      abandonable:
-        type: "boolean"
-        default: false
-      stoppable:
-        type: "boolean"
-        default: false
-      timeZone:
-        $ref: "#/definitions/TimeZone"
-  JobInfo:
-    type: "object"
-    properties:
-      name:
-        type: "string"
-      executionCount:
-        type: "integer"
-        format: "int32"
-      launchable:
-        type: "boolean"
-        default: false
-      incrementable:
-        type: "boolean"
-        default: false
-      jobInstanceId:
-        type: "integer"
-        format: "int64"
-  JobExecutionRestartRequest:
-    type: "object"
-    properties:
-      jobName:
-        type: "string"
-      jobInstanceId:
-        type: "integer"
-        format: "int64"
-      operation:
-        type: "string"
-        enum:
-        - "RESTART"
-  Throwable:
-    type: "object"
-    properties:
-      cause:
-        $ref: "#/definitions/Throwable"
-      stackTrace:
-        type: "array"
-        items:
-          $ref: "#/definitions/StackTraceElement"
-      message:
-        type: "string"
-      localizedMessage:
-        type: "string"
-      suppressed:
-        type: "array"
-        items:
-          $ref: "#/definitions/Throwable"
-  JobParameters:
-    type: "object"
-    properties:
-      parameters:
-        type: "object"
-        additionalProperties:
-          $ref: "#/definitions/JobParameter"
-      empty:
-        type: "boolean"
-        default: false
-  CumulativeHistory:
-    type: "object"
-    properties:
-      count:
-        type: "integer"
-        format: "int32"
-      min:
-        type: "number"
-        format: "double"
-      max:
-        type: "number"
-        format: "double"
-      standardDeviation:
-        type: "number"
-        format: "double"
-      mean:
-        type: "number"
-        format: "double"
-  JobInstanceDetailsResponse:
-    type: "object"
-    properties:
-      jobInstance:
-        $ref: "#/definitions/JobInstance"
-      jobExecutionInfoResponseList:
-        type: "array"
-        items:
-          $ref: "#/definitions/JobExecutionInfoResponse"
-  JobParameter:
-    type: "object"
-    properties:
-      identifying:
-        type: "boolean"
-        default: false
-      value:
-        type: "object"
-      type:
-        type: "string"
-        enum:
-        - "STRING"
-        - "DATE"
-        - "LONG"
-        - "DOUBLE"
-  StepExecutionProgressResponse:
-    type: "object"
-    properties:
-      stepExecutionProgress:
-        $ref: "#/definitions/StepExecutionProgress"
-      stepExecutionHistory:
-        $ref: "#/definitions/StepExecutionHistory"
-      stepExecutionInfoResponse:
-        $ref: "#/definitions/StepExecutionInfoResponse"
-  JobDetailsResponse:
-    type: "object"
-    properties:
-      jobInfo:
-        $ref: "#/definitions/JobInfo"
-      jobInstanceDetailsResponseList:
-        type: "array"
-        items:
-          $ref: "#/definitions/JobInstanceDetailsResponse"
diff --git a/ambari-infra-manager/pom.xml b/ambari-infra-manager/pom.xml
index 722bf51a..d595f997 100644
--- a/ambari-infra-manager/pom.xml
+++ b/ambari-infra-manager/pom.xml
@@ -30,18 +30,19 @@
   <artifactId>ambari-infra-manager</artifactId>
 
   <properties>
-    <spring.version>5.1.1.RELEASE</spring.version>
-    <spring.security.version>5.1.1.RELEASE</spring.security.version>
+    <spring.version>5.1.3.RELEASE</spring.version>
+    <spring.security.version>5.1.2.RELEASE</spring.security.version>
     <spring.ldap.version>2.3.2.RELEASE</spring.ldap.version>
     <jersey.version>2.27</jersey.version>
     <jetty.version>9.4.11.v20180605</jetty.version>
-    <spring-batch.version>3.0.7.RELEASE</spring-batch.version>
+    <spring-batch.version>4.1.0.RELEASE</spring-batch.version>
     <sqlite.version>3.8.11.2</sqlite.version>
     <spring-data-solr.version>3.0.10.RELEASE</spring-data-solr.version>
     <spring-boot.version>2.0.6.RELEASE</spring-boot.version>
     <swagger.version>1.5.16</swagger.version>
     <jjwt.version>0.6.0</jjwt.version>
     <aws-sdk.version>1.11.445</aws-sdk.version>
+    <swagger-maven-plugin-version>3.1.7</swagger-maven-plugin-version>
   </properties>
 
   <build>
@@ -117,6 +118,49 @@
           </execution>
         </executions>
       </plugin>
+      <plugin>
+        <groupId>com.github.kongchen</groupId>
+        <artifactId>swagger-maven-plugin</artifactId>
+        <version>${swagger-maven-plugin-version}</version>
+        <configuration>
+          <apiSources>
+            <apiSource>
+              <springmvc>false</springmvc>
+              <locations>org.apache.ambari.infra.rest</locations>
+              <schemes>
+                <scheme>http</scheme>
+                <scheme>https</scheme>
+              </schemes>
+              <basePath>/api/v1</basePath>
+              <info>
+                <title>Infra Manager REST API</title>
+                <version>1.0.0</version>
+                <description>Ambari Infra Manager REST APIs has inherent 
support for querying, sorting and pagination</description>
+                <license>
+                  <url>http://www.apache.org/licenses/LICENSE-2.0</url>
+                  <name>Apache License, Version 2.0</name>
+                </license>
+              </info>
+              <outputFormats>yaml,json</outputFormats>
+              
<swaggerDirectory>${project.basedir}/src/main/resources/swagger</swaggerDirectory>
+              <securityDefinitions>
+                <securityDefinition>
+                  <name>basicAuth</name>
+                  <type>basic</type>
+                </securityDefinition>
+              </securityDefinitions>
+            </apiSource>
+          </apiSources>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>compile</phase>
+            <goals>
+              <goal>generate</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
 
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/StaticResourceConfiguration.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/StaticResourceConfiguration.java
index f0cd3cf3..96512715 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/StaticResourceConfiguration.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/StaticResourceConfiguration.java
@@ -22,11 +22,11 @@
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 import 
org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import 
org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
-import 
org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 @EnableWebMvc
 @Configuration
-public class StaticResourceConfiguration extends WebMvcConfigurerAdapter {
+public class StaticResourceConfiguration implements WebMvcConfigurer {
 
   private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
     "classpath:/static/", 
"classpath:/swagger/","classpath:META-INF/resources/webjars/"
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/doc/InfraManagerApiDocStorage.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/doc/InfraManagerApiDocStorage.java
deleted file mode 100644
index 5ababcc1..00000000
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/doc/InfraManagerApiDocStorage.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.infra.doc;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import io.swagger.jaxrs.config.BeanConfig;
-import io.swagger.models.Swagger;
-import io.swagger.models.auth.BasicAuthDefinition;
-import io.swagger.util.Yaml;
-
-@Named
-public class InfraManagerApiDocStorage {
-
-  private static final Logger logger = 
LogManager.getLogger(InfraManagerApiDocStorage.class);
-
-  private final Map<String, Object> swaggerMap = new ConcurrentHashMap<>();
-
-  @Inject
-  private BeanConfig beanConfig;
-
-  @PostConstruct
-  private void postConstruct() {
-    Thread loadApiDocThread = new Thread("load_swagger_api_doc") {
-      @Override
-      public void run() {
-        logger.info("Start thread to scan REST API doc from endpoints.");
-        Swagger swagger = beanConfig.getSwagger();
-        swagger.addSecurityDefinition("basicAuth", new BasicAuthDefinition());
-        beanConfig.configure(swagger);
-        beanConfig.scanAndRead();
-        setSwagger(swagger);
-        try {
-          String yaml = Yaml.mapper().writeValueAsString(swagger);
-          StringBuilder b = new StringBuilder();
-          String[] parts = yaml.split("\n");
-          for (String part : parts) {
-            b.append(part);
-            b.append("\n");
-          }
-          setSwaggerYaml(b.toString());
-        } catch (Exception e) {
-          e.printStackTrace();
-        }
-        logger.info("Scanning REST API endpoints and generating docs has been 
successful.");
-      }
-    };
-    loadApiDocThread.setDaemon(true);
-    loadApiDocThread.start();
-  }
-
-  public Swagger getSwagger() {
-    return (Swagger) swaggerMap.get("swaggerObject");
-  }
-
-  public void setSwagger(final Swagger swagger) {
-    swaggerMap.put("swaggerObject", swagger);
-  }
-
-  public void setSwaggerYaml(final String swaggerYaml) {
-    swaggerMap.put("swaggerYaml", swaggerYaml);
-  }
-
-  public String getSwaggerYaml() {
-    return (String) swaggerMap.get("swaggerYaml");
-  }
-
-}
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/archive/DocumentExporter.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/archive/DocumentExporter.java
index f61746d2..8c4a0679 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/archive/DocumentExporter.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/archive/DocumentExporter.java
@@ -21,7 +21,6 @@
 import org.apache.ambari.infra.job.JobContextRepository;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.springframework.batch.core.BatchStatus;
 import org.springframework.batch.core.ExitStatus;
 import org.springframework.batch.core.StepContribution;
 import org.springframework.batch.core.StepExecution;
@@ -31,6 +30,7 @@
 import org.springframework.batch.item.ExecutionContext;
 import org.springframework.batch.item.ItemStreamReader;
 import org.springframework.batch.repeat.RepeatStatus;
+import org.springframework.lang.NonNull;
 
 public class DocumentExporter implements Tasklet, StepExecutionListener {
 
@@ -50,12 +50,12 @@ public DocumentExporter(ItemStreamReader<Document> 
documentReader, DocumentDesti
   }
 
   @Override
-  public void beforeStep(StepExecution stepExecution) {
+  public void beforeStep(@NonNull StepExecution stepExecution) {
 
   }
 
   @Override
-  public ExitStatus afterStep(StepExecution stepExecution) {
+  public ExitStatus afterStep(@NonNull StepExecution stepExecution) {
     if (complete) {
       return ExitStatus.COMPLETED;
     }
@@ -65,7 +65,7 @@ public ExitStatus afterStep(StepExecution stepExecution) {
   }
 
   @Override
-  public RepeatStatus execute(StepContribution contribution, ChunkContext 
chunkContext) throws Exception {
+  public RepeatStatus execute(@NonNull StepContribution contribution, @NonNull 
ChunkContext chunkContext) throws Exception {
     StepExecution stepExecution = 
chunkContext.getStepContext().getStepExecution();
     ExecutionContext executionContext = stepExecution.getExecutionContext();
     documentReader.open(executionContext);
@@ -77,7 +77,7 @@ public RepeatStatus execute(StepContribution contribution, 
ChunkContext chunkCon
       while ((document = documentReader.read()) != null) {
         if (writer != null && writtenCount >= writeBlockSize) {
           stepExecution = 
jobContextRepository.getStepExecution(stepExecution.getJobExecutionId(), 
stepExecution.getId());
-          if (stepExecution.getJobExecution().getStatus() == 
BatchStatus.STOPPING) {
+          if (stepExecution.isTerminateOnly()) {
             logger.info("Received stop signal.");
             writer.revert();
             writer = null;
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/cleanup/TaskHistoryWiper.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/cleanup/TaskHistoryWiper.java
index 5a296ded..2627f4c7 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/cleanup/TaskHistoryWiper.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/cleanup/TaskHistoryWiper.java
@@ -28,20 +28,21 @@
 import org.springframework.batch.core.scope.context.ChunkContext;
 import org.springframework.batch.core.step.tasklet.Tasklet;
 import org.springframework.batch.repeat.RepeatStatus;
+import org.springframework.lang.NonNull;
 
 public class TaskHistoryWiper implements Tasklet {
 
   private static final Logger logger = 
LogManager.getLogger(TaskHistoryWiper.class);
-  public static final Duration DEFAULT_TTL = Duration.ofHours(1);
+  private static final Duration MINIMUM_TTL = Duration.ofHours(1);
 
   private final InfraJobExecutionDao infraJobExecutionDao;
   private final Duration ttl;
 
   public TaskHistoryWiper(InfraJobExecutionDao infraJobExecutionDao, Duration 
ttl) {
     this.infraJobExecutionDao = infraJobExecutionDao;
-    if (ttl == null || ttl.compareTo(DEFAULT_TTL) < 0) {
-      logger.info("The ttl value ({}) less than the minimum required. Using 
the default ({}) instead", ttl, DEFAULT_TTL);
-      this.ttl = DEFAULT_TTL;
+    if (ttl == null || ttl.compareTo(MINIMUM_TTL) < 0) {
+      logger.info("The ttl value ({}) less than the minimum required. Using 
the minimum ({}) instead", ttl, MINIMUM_TTL);
+      this.ttl = MINIMUM_TTL;
     }
     else {
       this.ttl = ttl;
@@ -49,7 +50,7 @@ public TaskHistoryWiper(InfraJobExecutionDao 
infraJobExecutionDao, Duration ttl)
   }
 
   @Override
-  public RepeatStatus execute(StepContribution contribution, ChunkContext 
chunkContext) {
+  public RepeatStatus execute(@NonNull StepContribution contribution, @NonNull 
ChunkContext chunkContext) {
     infraJobExecutionDao.deleteJobExecutions(OffsetDateTime.now().minus(ttl));
     return RepeatStatus.FINISHED;
   }
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/deleting/DocumentWiperTasklet.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/deleting/DocumentWiperTasklet.java
index cae64f4c..9bde32a8 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/deleting/DocumentWiperTasklet.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/deleting/DocumentWiperTasklet.java
@@ -26,6 +26,7 @@
 import org.springframework.batch.core.scope.context.ChunkContext;
 import org.springframework.batch.core.step.tasklet.Tasklet;
 import org.springframework.batch.repeat.RepeatStatus;
+import org.springframework.lang.NonNull;
 
 public class DocumentWiperTasklet extends SolrDAOBase implements Tasklet {
   private final DeletingProperties parameters;
@@ -36,7 +37,7 @@ public DocumentWiperTasklet(DeletingProperties 
deletingProperties) {
   }
 
   @Override
-  public RepeatStatus execute(StepContribution contribution, ChunkContext 
chunkContext) {
+  public RepeatStatus execute(@NonNull StepContribution contribution, @NonNull 
ChunkContext chunkContext) {
     delete(String.format("%s:[%s TO %s]",
             parameters.getFilterField(),
             getValue(parameters.getStart()),
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/DurationConverter.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/OffsetDateTimeToStringConverter.java
similarity index 60%
rename from 
ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/DurationConverter.java
rename to 
ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/OffsetDateTimeToStringConverter.java
index 93b47948..25bdf195 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/DurationConverter.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/OffsetDateTimeToStringConverter.java
@@ -16,23 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.ambari.infra.conf;
+package org.apache.ambari.infra.json;
 
-import static 
org.apache.ambari.infra.json.StringToDurationConverter.toDuration;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
 
-import java.time.Duration;
+import com.fasterxml.jackson.databind.util.StdConverter;
 
-import javax.annotation.Nullable;
-import javax.inject.Named;
+public class OffsetDateTimeToStringConverter extends 
StdConverter<OffsetDateTime, String> {
 
-import 
org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
-import org.springframework.core.convert.converter.Converter;
-
-@Named
-@ConfigurationPropertiesBinding
-public class DurationConverter implements Converter<String, Duration> {
   @Override
-  public Duration convert(@Nullable String s) {
-    return toDuration(s);
+  public String convert(OffsetDateTime value) {
+    return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value);
   }
 }
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java
index 2a385cfb..47460484 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToDurationConverter.java
@@ -20,11 +20,19 @@
 
 import java.time.Duration;
 
+import javax.inject.Named;
+
+import 
org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.lang.NonNull;
+
 import com.fasterxml.jackson.databind.util.StdConverter;
 
-public class StringToDurationConverter extends StdConverter<String, Duration> {
+@Named
+@ConfigurationPropertiesBinding
+public class StringToDurationConverter extends StdConverter<String, Duration> 
implements Converter<String, Duration> {
   @Override
-  public Duration convert(String value) {
+  public Duration convert(@NonNull String value) {
     return toDuration(value);
   }
 
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToFsPermissionConverter.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToFsPermissionConverter.java
index 70e49028..e2713468 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToFsPermissionConverter.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/json/StringToFsPermissionConverter.java
@@ -20,13 +20,20 @@
 
 import static org.apache.commons.lang.StringUtils.isBlank;
 
+import javax.inject.Named;
+
 import org.apache.hadoop.fs.permission.FsPermission;
+import 
org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.lang.NonNull;
 
 import com.fasterxml.jackson.databind.util.StdConverter;
 
-public class StringToFsPermissionConverter extends StdConverter<String, 
FsPermission> {
+@Named
+@ConfigurationPropertiesBinding
+public class StringToFsPermissionConverter extends StdConverter<String, 
FsPermission> implements Converter<String, FsPermission> {
   @Override
-  public FsPermission convert(String value) {
+  public FsPermission convert(@NonNull String value) {
     return toFsPermission(value);
   }
 
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/manager/JobManager.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/manager/JobManager.java
index 86ffc1a5..78864529 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/manager/JobManager.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/manager/JobManager.java
@@ -18,6 +18,8 @@
  */
 package org.apache.ambari.infra.manager;
 
+import static java.util.Collections.unmodifiableList;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
@@ -26,13 +28,11 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.TimeZone;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.apache.ambari.infra.model.ExecutionContextResponse;
-import org.apache.ambari.infra.model.JobDetailsResponse;
 import org.apache.ambari.infra.model.JobExecutionDetailsResponse;
 import org.apache.ambari.infra.model.JobExecutionInfoResponse;
 import org.apache.ambari.infra.model.JobInstanceDetailsResponse;
@@ -78,8 +78,6 @@
   @Inject
   private JobExplorer jobExplorer;
 
-  private TimeZone timeZone = TimeZone.getDefault();
-
   public Set<String> getAllJobNames() {
     return jobOperator.getJobNames();
   }
@@ -97,7 +95,7 @@ public JobExecutionInfoResponse launchJob(String jobName, 
JobParameters jobParam
     if (!running.isEmpty())
       throw new JobExecutionAlreadyRunningException("An instance of this job 
is already active: "+jobName);
 
-    return new JobExecutionInfoResponse(jobService.launch(jobName, 
jobParameters), timeZone);
+    return new JobExecutionInfoResponse(jobService.launch(jobName, 
jobParameters));
   }
 
   @Override
@@ -143,10 +141,10 @@ public JobExecutionDetailsResponse getExecutionInfo(Long 
jobExecutionId) throws
     JobExecution jobExecution = jobService.getJobExecution(jobExecutionId);
     List<StepExecutionInfoResponse> stepExecutionInfoList = new ArrayList<>();
     for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
-      stepExecutionInfoList.add(new StepExecutionInfoResponse(stepExecution, 
timeZone));
+      stepExecutionInfoList.add(new StepExecutionInfoResponse(stepExecution));
     }
-    
stepExecutionInfoList.sort(Comparator.comparing(StepExecutionInfoResponse::getId));
-    return new JobExecutionDetailsResponse(new 
JobExecutionInfoResponse(jobExecution, timeZone), stepExecutionInfoList);
+    
stepExecutionInfoList.sort(Comparator.comparing(StepExecutionInfoResponse::getStepExecutionId));
+    return new JobExecutionDetailsResponse(new 
JobExecutionInfoResponse(jobExecution), stepExecutionInfoList);
   }
 
   /**
@@ -163,7 +161,7 @@ public JobExecutionInfoResponse 
stopOrAbandonJobByExecutionId(Long jobExecutionI
       throw new UnsupportedOperationException("Unsupported operaration");
     }
     logger.info("Job {} was marked {}", 
jobExecution.getJobInstance().getJobName(), operation.name());
-    return new JobExecutionInfoResponse(jobExecution, timeZone);
+    return new JobExecutionInfoResponse(jobExecution);
   }
 
   /**
@@ -188,7 +186,7 @@ public JobExecutionInfoResponse restart(Long jobInstanceId, 
String jobName,
       Collection<JobExecution> jobExecutions = 
jobService.getJobExecutionsForJobInstance(jobName, jobInstanceId);
       JobExecution jobExecution = jobExecutions.iterator().next();
       Long jobExecutionId = jobExecution.getId();
-      return new JobExecutionInfoResponse(jobService.restart(jobExecutionId), 
timeZone);
+      return new JobExecutionInfoResponse(jobService.restart(jobExecutionId));
     } else {
       throw new UnsupportedOperationException("Unsupported operation (try: 
RESTART)");
     }
@@ -223,7 +221,7 @@ public JobExecutionInfoResponse restart(Long jobInstanceId, 
String jobName,
     JobInstance jobInstance = jobService.getJobInstance(jobInstanceId);
     Collection<JobExecution> jobExecutions = 
jobService.getJobExecutionsForJobInstance(jobName, jobInstance.getInstanceId());
     for (JobExecution jobExecution : jobExecutions) {
-      result.add(new JobExecutionInfoResponse(jobExecution, timeZone));
+      result.add(new JobExecutionInfoResponse(jobExecution));
     }
     return result;
   }
@@ -231,25 +229,26 @@ public JobExecutionInfoResponse restart(Long 
jobInstanceId, String jobName,
   /**
    * Get job details for a specific job. (paged)
    */
-  public JobDetailsResponse getJobDetails(String jobName, int page, int size) 
throws NoSuchJobException {
+  public List<JobInstanceDetailsResponse> getJobDetails(String jobName, int 
page, int size) throws NoSuchJobException {
     List<JobInstanceDetailsResponse> jobInstanceResponses = 
Lists.newArrayList();
     Collection<JobInstance> jobInstances = 
jobService.listJobInstances(jobName, page, size);
 
-    int count = jobService.countJobExecutionsForJob(jobName);
     boolean launchable = jobService.isLaunchable(jobName);
-    boolean isIncrementable = jobService.isIncrementable(jobName);
+    boolean incrementable = jobService.isIncrementable(jobName);
 
     for (JobInstance jobInstance: jobInstances) {
-      List<JobExecutionInfoResponse> executionInfos = Lists.newArrayList();
+      List<JobExecutionInfoResponse> executionInfoResponses = 
Lists.newArrayList();
       Collection<JobExecution> jobExecutions = 
jobService.getJobExecutionsForJobInstance(jobName, jobInstance.getId());
       if (jobExecutions != null) {
         for (JobExecution jobExecution : jobExecutions) {
-          executionInfos.add(new JobExecutionInfoResponse(jobExecution, 
timeZone));
+          executionInfoResponses.add(new 
JobExecutionInfoResponse(jobExecution));
         }
       }
-      jobInstanceResponses.add(new JobInstanceDetailsResponse(jobInstance, 
executionInfos));
+      jobInstanceResponses.add(new JobInstanceDetailsResponse(
+              new JobInfo(jobName, executionInfoResponses.size(), 
jobInstance.getInstanceId(), launchable, incrementable),
+              executionInfoResponses));
     }
-    return new JobDetailsResponse(new JobInfo(jobName, count, launchable, 
isIncrementable), jobInstanceResponses);
+    return unmodifiableList(jobInstanceResponses);
   }
 
   /**
@@ -257,7 +256,7 @@ public JobDetailsResponse getJobDetails(String jobName, int 
page, int size) thro
    */
   public StepExecutionInfoResponse getStepExecution(Long jobExecutionId, Long 
stepExecutionId) throws NoSuchStepExecutionException, 
NoSuchJobExecutionException {
     StepExecution stepExecution = jobService.getStepExecution(jobExecutionId, 
stepExecutionId);
-    return new StepExecutionInfoResponse(stepExecution, timeZone);
+    return new StepExecutionInfoResponse(stepExecution);
   }
 
   /**
@@ -277,7 +276,7 @@ public StepExecutionContextResponse 
getStepExecutionContext(Long jobExecutionId,
    */
   public StepExecutionProgressResponse getStepExecutionProgress(Long 
jobExecutionId, Long stepExecutionId) throws NoSuchStepExecutionException, 
NoSuchJobExecutionException {
     StepExecution stepExecution = jobService.getStepExecution(jobExecutionId, 
stepExecutionId);
-    StepExecutionInfoResponse stepExecutionInfoResponse = new 
StepExecutionInfoResponse(stepExecution, timeZone);
+    StepExecutionInfoResponse stepExecutionInfoResponse = new 
StepExecutionInfoResponse(stepExecution);
     String stepName = stepExecution.getStepName();
     if (stepName.contains(":partition")) {
       stepName = stepName.replaceAll("(:partition).*", "$1*");
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/FsPermissionConverter.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/DateUtil.java
similarity index 57%
rename from 
ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/FsPermissionConverter.java
rename to 
ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/DateUtil.java
index 5e794a7f..810a95b4 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/FsPermissionConverter.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/DateUtil.java
@@ -16,22 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.ambari.infra.conf;
+package org.apache.ambari.infra.model;
 
-import static 
org.apache.ambari.infra.json.StringToFsPermissionConverter.toFsPermission;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.util.Date;
 
-import javax.annotation.Nullable;
-import javax.inject.Named;
-
-import org.apache.hadoop.fs.permission.FsPermission;
-import 
org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
-import org.springframework.core.convert.converter.Converter;
-
-@Named
-@ConfigurationPropertiesBinding
-public class FsPermissionConverter implements Converter<String, FsPermission> {
-  @Override
-  public FsPermission convert(@Nullable String s) {
-    return toFsPermission(s);
+public class DateUtil {
+  public static OffsetDateTime toOffsetDateTime(Date date) {
+    if (date == null)
+      return null;
+    return date.toInstant().atOffset(ZoneOffset.UTC);
   }
 }
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java
index 2d46c547..0eb1a164 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ExecutionContextResponse.java
@@ -18,6 +18,8 @@
  */
 package org.apache.ambari.infra.model;
 
+import static java.util.Collections.unmodifiableMap;
+
 import java.util.Map;
 
 public class ExecutionContextResponse {
@@ -27,7 +29,7 @@
 
   public ExecutionContextResponse(Long jobExecutionId, Map<String, Object> 
executionContextMap) {
     this.jobExecutionId = jobExecutionId;
-    this.executionContextMap = executionContextMap;
+    this.executionContextMap = unmodifiableMap(executionContextMap);
   }
 
   public Long getJobExecutionId() {
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ISO8601DateFormatter.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ISO8601DateFormatter.java
new file mode 100644
index 00000000..5f945b6d
--- /dev/null
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/ISO8601DateFormatter.java
@@ -0,0 +1,41 @@
+/*
+ * 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.infra.model;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+public class ISO8601DateFormatter extends DateFormat {
+  @Override
+  public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition 
fieldPosition) {
+    
toAppendTo.append(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(DateUtil.toOffsetDateTime(date)));
+    return toAppendTo;
+  }
+
+  @Override
+  public Date parse(String source, ParsePosition pos) {
+    OffsetDateTime offsetDateTime = OffsetDateTime.parse(source, 
DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+    pos.setIndex(pos.getIndex() + source.length());
+    return Date.from(offsetDateTime.toInstant());
+  }
+}
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobDetailsResponse.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobDetailsResponse.java
deleted file mode 100644
index cd34fefd..00000000
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobDetailsResponse.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.infra.model;
-
-import org.springframework.batch.admin.web.JobInfo;
-
-import java.util.List;
-
-public class JobDetailsResponse {
-
-  private JobInfo jobInfo;
-  private List<JobInstanceDetailsResponse> jobInstanceDetailsResponseList;
-
-  public JobDetailsResponse() {
-  }
-
-  public JobDetailsResponse(JobInfo jobInfo, List<JobInstanceDetailsResponse> 
jobInstanceDetailsResponseList) {
-    this.jobInfo = jobInfo;
-    this.jobInstanceDetailsResponseList = jobInstanceDetailsResponseList;
-  }
-
-  public JobInfo getJobInfo() {
-    return jobInfo;
-  }
-
-  public void setJobInfo(JobInfo jobInfo) {
-    this.jobInfo = jobInfo;
-  }
-
-  public List<JobInstanceDetailsResponse> getJobInstanceDetailsResponseList() {
-    return jobInstanceDetailsResponseList;
-  }
-
-  public void 
setJobInstanceDetailsResponseList(List<JobInstanceDetailsResponse> 
jobInstanceDetailsResponseList) {
-    this.jobInstanceDetailsResponseList = jobInstanceDetailsResponseList;
-  }
-}
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionDetailsResponse.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionDetailsResponse.java
index 695b57f0..9e19344f 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionDetailsResponse.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionDetailsResponse.java
@@ -18,32 +18,30 @@
  */
 package org.apache.ambari.infra.model;
 
+import static java.util.Collections.unmodifiableList;
+
 import java.util.List;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
+
 public class JobExecutionDetailsResponse {
 
-  private JobExecutionInfoResponse jobExecutionInfoResponse;
+  @JsonProperty("jobExecution")
+  private final JobExecutionInfoResponse jobExecutionInfoResponse;
 
-  private List<StepExecutionInfoResponse> stepExecutionInfoList;
+  @JsonProperty("stepExecutionList")
+  private final List<StepExecutionInfoResponse> stepExecutionInfoList;
 
   public JobExecutionDetailsResponse(JobExecutionInfoResponse 
jobExecutionInfoResponse, List<StepExecutionInfoResponse> 
stepExecutionInfoList) {
     this.jobExecutionInfoResponse = jobExecutionInfoResponse;
-    this.stepExecutionInfoList = stepExecutionInfoList;
+    this.stepExecutionInfoList = unmodifiableList(stepExecutionInfoList);
   }
 
   public JobExecutionInfoResponse getJobExecutionInfoResponse() {
     return jobExecutionInfoResponse;
   }
 
-  public void setJobExecutionInfoResponse(JobExecutionInfoResponse 
jobExecutionInfoResponse) {
-    this.jobExecutionInfoResponse = jobExecutionInfoResponse;
-  }
-
   public List<StepExecutionInfoResponse> getStepExecutionInfoList() {
     return stepExecutionInfoList;
   }
-
-  public void setStepExecutionInfoList(List<StepExecutionInfoResponse> 
stepExecutionInfoList) {
-    this.stepExecutionInfoList = stepExecutionInfoList;
-  }
 }
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionInfoResponse.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionInfoResponse.java
index a7e4a4f0..1034eb84 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionInfoResponse.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionInfoResponse.java
@@ -18,111 +18,122 @@
  */
 package org.apache.ambari.infra.model;
 
-import org.apache.ambari.infra.model.wrapper.JobExecutionData;
-import org.springframework.batch.admin.web.JobParametersExtractor;
+import static java.util.Collections.unmodifiableList;
+import static org.apache.ambari.infra.model.DateUtil.toOffsetDateTime;
+
+import java.time.Duration;
+import java.time.OffsetDateTime;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.ambari.infra.json.DurationToStringConverter;
+import org.apache.ambari.infra.json.OffsetDateTimeToStringConverter;
 import org.springframework.batch.core.BatchStatus;
 import org.springframework.batch.core.JobExecution;
 import org.springframework.batch.core.JobInstance;
 import org.springframework.batch.core.converter.DefaultJobParametersConverter;
-import org.springframework.batch.core.converter.JobParametersConverter;
 
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Properties;
-import java.util.TimeZone;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import io.swagger.annotations.ApiModelProperty;
 
 public class JobExecutionInfoResponse {
-  private Long id;
-  private int stepExecutionCount;
-  private Long jobId;
-  private String jobName;
-  private String startDate = "";
-  private String startTime = "";
-  private String duration = "";
-  private JobExecutionData jobExecutionData;
-  private Properties jobParameters;
-  private String jobParametersString;
-  private boolean restartable = false;
-  private boolean abandonable = false;
-  private boolean stoppable = false;
-  private final TimeZone timeZone;
-
-
-  public JobExecutionInfoResponse(JobExecution jobExecution, TimeZone 
timeZone) {
-    JobParametersConverter converter = new DefaultJobParametersConverter();
-    this.jobExecutionData = new JobExecutionData(jobExecution);
-    this.timeZone = timeZone;
-    this.id = jobExecutionData.getId();
-    this.jobId = jobExecutionData.getJobId();
-    this.stepExecutionCount = jobExecutionData.getStepExecutions().size();
-    this.jobParameters = 
converter.getProperties(jobExecutionData.getJobParameters());
-    this.jobParametersString = (new 
JobParametersExtractor()).fromJobParameters(jobExecutionData.getJobParameters());
-    JobInstance jobInstance = jobExecutionData.getJobInstance();
+  private static final DefaultJobParametersConverter 
DEFAULT_JOB_PARAMETERS_CONVERTER = new DefaultJobParametersConverter();
+
+  static {
+    DEFAULT_JOB_PARAMETERS_CONVERTER.setDateFormat(new ISO8601DateFormatter());
+  }
+
+  private final Long jobExecutionId;
+  private final Long jobInstanceId;
+  private final String jobName;
+  @JsonSerialize(converter = OffsetDateTimeToStringConverter.class)
+  private final OffsetDateTime creationTime;
+  @JsonSerialize(converter = OffsetDateTimeToStringConverter.class)
+  private final OffsetDateTime startTime;
+  @JsonSerialize(converter = OffsetDateTimeToStringConverter.class)
+  private final OffsetDateTime lastUpdatedTime;
+  @JsonSerialize(converter = OffsetDateTimeToStringConverter.class)
+  private final OffsetDateTime endTime;
+  @JsonSerialize(converter = DurationToStringConverter.class)
+  @ApiModelProperty(dataType = "java.lang.String", example = "PT5.311S")
+  private final Duration duration;
+  private final Properties jobParameters;
+  private final BatchStatus batchStatus;
+  @ApiModelProperty(example = "COMPLETED", allowableValues = "UNKNOWN, 
EXECUTING, COMPLETED, NOOP, FAILED, STOPPED")
+  private final String exitCode;
+  private final String exitDescription;
+  private final boolean restartable;
+  private final boolean abandonable;
+  private final boolean stoppable;
+  private final List<Throwable> failureExceptions;
+  private final String jobConfigurationName;
+
+
+  public JobExecutionInfoResponse(JobExecution jobExecution) {
+    this.jobExecutionId = jobExecution.getId();
+    this.jobInstanceId = jobExecution.getJobId();
+    this.jobParameters = 
DEFAULT_JOB_PARAMETERS_CONVERTER.getProperties(jobExecution.getJobParameters());
+    this.creationTime = toOffsetDateTime(jobExecution.getCreateTime());
+    this.startTime = toOffsetDateTime(jobExecution.getStartTime());
+    this.lastUpdatedTime = toOffsetDateTime(jobExecution.getLastUpdated());
+    this.endTime = toOffsetDateTime(jobExecution.getEndTime());
+    JobInstance jobInstance = jobExecution.getJobInstance();
+    this.batchStatus = jobExecution.getStatus();
+    this.restartable = batchStatus.isGreaterThan(BatchStatus.STOPPING) && 
batchStatus.isLessThan(BatchStatus.ABANDONED);
+    this.abandonable = batchStatus.isGreaterThan(BatchStatus.STARTED) && 
batchStatus != BatchStatus.ABANDONED;
+    this.stoppable = batchStatus.isLessThan(BatchStatus.STOPPING);
+
+    if (jobExecution.getExitStatus() != null) {
+      this.exitCode = jobExecution.getExitStatus().getExitCode();
+      this.exitDescription = jobExecution.getExitStatus().getExitDescription();
+    }
+    else {
+      this.exitCode = null;
+      this.exitDescription = null;
+    }
+
     if(jobInstance != null) {
       this.jobName = jobInstance.getJobName();
-      BatchStatus endTime = jobExecutionData.getStatus();
-      this.restartable = endTime.isGreaterThan(BatchStatus.STOPPING) && 
endTime.isLessThan(BatchStatus.ABANDONED);
-      this.abandonable = endTime.isGreaterThan(BatchStatus.STARTED) && endTime 
!= BatchStatus.ABANDONED;
-      this.stoppable = endTime.isLessThan(BatchStatus.STOPPING);
     } else {
       this.jobName = "?";
     }
 
-    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
-    SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
-    SimpleDateFormat durationFormat = new SimpleDateFormat("HH:mm:ss");
-
-    durationFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
-    timeFormat.setTimeZone(timeZone);
-    dateFormat.setTimeZone(timeZone);
-    if(jobExecutionData.getStartTime() != null) {
-      this.startDate = dateFormat.format(jobExecutionData.getStartTime());
-      this.startTime = timeFormat.format(jobExecutionData.getStartTime());
-      Date endTime1 = jobExecutionData.getEndTime() != null? 
jobExecutionData.getEndTime():new Date();
-      this.duration = durationFormat.format(new Date(endTime1.getTime() - 
jobExecutionData.getStartTime().getTime()));
+    if(startTime != null && endTime != null) {
+      this.duration = Duration.between(startTime, endTime);
+    }
+    else {
+      this.duration = null;
     }
-  }
 
-  public Long getId() {
-    return id;
+    this.failureExceptions = 
unmodifiableList(jobExecution.getFailureExceptions());
+    this.jobConfigurationName = jobExecution.getJobConfigurationName();
   }
 
-  public int getStepExecutionCount() {
-    return stepExecutionCount;
+  public Long getJobExecutionId() {
+    return jobExecutionId;
   }
 
-  public Long getJobId() {
-    return jobId;
+  public Long getJobInstanceId() {
+    return jobInstanceId;
   }
 
   public String getJobName() {
     return jobName;
   }
 
-  public String getStartDate() {
-    return startDate;
-  }
-
-  public String getStartTime() {
+  public OffsetDateTime getStartTime() {
     return startTime;
   }
 
-  public String getDuration() {
+  public Duration getDuration() {
     return duration;
   }
 
-  public JobExecutionData getJobExecutionData() {
-    return jobExecutionData;
-  }
-
   public Properties getJobParameters() {
     return jobParameters;
   }
 
-  public String getJobParametersString() {
-    return jobParametersString;
-  }
-
   public boolean isRestartable() {
     return restartable;
   }
@@ -135,7 +146,35 @@ public boolean isStoppable() {
     return stoppable;
   }
 
-  public TimeZone getTimeZone() {
-    return timeZone;
+  public BatchStatus getBatchStatus() {
+    return batchStatus;
+  }
+
+  public OffsetDateTime getCreationTime() {
+    return creationTime;
+  }
+
+  public OffsetDateTime getEndTime() {
+    return endTime;
+  }
+
+  public OffsetDateTime getLastUpdatedTime() {
+    return lastUpdatedTime;
+  }
+
+  public String getExitCode() {
+    return exitCode;
+  }
+
+  public String getExitDescription() {
+    return exitDescription;
+  }
+
+  public List<Throwable> getFailureExceptions() {
+    return this.failureExceptions;
+  }
+
+  public String getJobConfigurationName() {
+    return this.jobConfigurationName;
   }
 }
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionRestartRequest.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionRestartRequest.java
index 3eab25f4..fe36c5b6 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionRestartRequest.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionRestartRequest.java
@@ -22,6 +22,8 @@
 import javax.ws.rs.PathParam;
 import javax.ws.rs.QueryParam;
 
+import io.swagger.annotations.ApiParam;
+
 public class JobExecutionRestartRequest {
 
   @PathParam("jobName")
@@ -34,6 +36,7 @@
 
   @QueryParam("operation")
   @NotNull
+  @ApiParam(required = true)
   private JobOperationParams.JobRestartOperationParam operation;
 
   public String getJobName() {
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionStopRequest.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionStopRequest.java
index b176f125..510e6944 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionStopRequest.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobExecutionStopRequest.java
@@ -22,6 +22,8 @@
 import javax.ws.rs.PathParam;
 import javax.ws.rs.QueryParam;
 
+import io.swagger.annotations.ApiParam;
+
 public class JobExecutionStopRequest {
 
   @PathParam("jobExecutionId")
@@ -30,6 +32,7 @@
 
   @QueryParam("operation")
   @NotNull
+  @ApiParam(required = true)
   private JobOperationParams.JobStopOrAbandonOperationParam operation;
 
   public Long getJobExecutionId() {
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceDetailsResponse.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceDetailsResponse.java
index af886545..9bb79946 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceDetailsResponse.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceDetailsResponse.java
@@ -18,37 +18,31 @@
  */
 package org.apache.ambari.infra.model;
 
-import org.springframework.batch.core.JobInstance;
+import static java.util.Collections.unmodifiableList;
 
 import java.util.List;
 
-public class JobInstanceDetailsResponse {
+import org.springframework.batch.admin.web.JobInfo;
 
-  private JobInstance jobInstance;
+import com.fasterxml.jackson.annotation.JsonProperty;
 
-  private List<JobExecutionInfoResponse> jobExecutionInfoResponseList;
 
-  public JobInstanceDetailsResponse() {
-  }
+public class JobInstanceDetailsResponse {
 
-  public JobInstanceDetailsResponse(JobInstance jobInstance, 
List<JobExecutionInfoResponse> jobExecutionInfoResponseList) {
-    this.jobInstance = jobInstance;
-    this.jobExecutionInfoResponseList = jobExecutionInfoResponseList;
-  }
+  private final JobInfo jobInfo;
+  @JsonProperty("jobExecutionList")
+  private final List<JobExecutionInfoResponse> jobExecutionInfoResponseList;
 
-  public JobInstance getJobInstance() {
-    return jobInstance;
+  public JobInstanceDetailsResponse(JobInfo jobInfo, 
List<JobExecutionInfoResponse> jobExecutionInfoResponseList) {
+    this.jobInfo = jobInfo;
+    this.jobExecutionInfoResponseList = 
unmodifiableList(jobExecutionInfoResponseList);
   }
 
-  public void setJobInstance(JobInstance jobInstance) {
-    this.jobInstance = jobInstance;
+  public JobInfo getJobInfo() {
+    return jobInfo;
   }
 
   public List<JobExecutionInfoResponse> getJobExecutionInfoResponseList() {
     return jobExecutionInfoResponseList;
   }
-
-  public void setJobExecutionInfoResponseList(List<JobExecutionInfoResponse> 
jobExecutionInfoResponseList) {
-    this.jobExecutionInfoResponseList = jobExecutionInfoResponseList;
-  }
 }
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceStartRequest.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceStartRequest.java
index 905a4fa6..5c760f68 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceStartRequest.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/JobInstanceStartRequest.java
@@ -22,14 +22,17 @@
 import javax.ws.rs.PathParam;
 import javax.ws.rs.QueryParam;
 
+import io.swagger.annotations.ApiParam;
+
 public class JobInstanceStartRequest {
 
   @PathParam("jobName")
   @NotNull
+  @ApiParam(required = true)
   private String jobName;
 
   @QueryParam("params")
-  String params;
+  private String params;
 
   public String getJobName() {
     return jobName;
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionContextResponse.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionContextResponse.java
index 0e67a879..9ce56afa 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionContextResponse.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionContextResponse.java
@@ -18,23 +18,22 @@
  */
 package org.apache.ambari.infra.model;
 
+import static java.util.Collections.unmodifiableMap;
+
 import java.util.Map;
 
 public class StepExecutionContextResponse {
 
-  private Map<String, Object> executionContextMap;
-
-  private Long jobExecutionId;
+  private final Map<String, Object> executionContextMap;
 
-  private Long stepExecutionId;
+  private final Long jobExecutionId;
 
-  private String stepName;
+  private final Long stepExecutionId;
 
-  public StepExecutionContextResponse() {
-  }
+  private final String stepName;
 
   public StepExecutionContextResponse(Map<String, Object> executionContextMap, 
Long jobExecutionId, Long stepExecutionId, String stepName) {
-    this.executionContextMap = executionContextMap;
+    this.executionContextMap = unmodifiableMap(executionContextMap);
     this.jobExecutionId = jobExecutionId;
     this.stepExecutionId = stepExecutionId;
     this.stepName = stepName;
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionInfoResponse.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionInfoResponse.java
index ed04767b..011eaea7 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionInfoResponse.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionInfoResponse.java
@@ -18,98 +18,101 @@
  */
 package org.apache.ambari.infra.model;
 
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import org.apache.ambari.infra.model.wrapper.StepExecutionData;
-import org.springframework.batch.core.JobExecution;
+import static org.apache.ambari.infra.model.DateUtil.toOffsetDateTime;
+
+import java.time.Duration;
+import java.time.OffsetDateTime;
+
+import org.apache.ambari.infra.json.DurationToStringConverter;
+import org.apache.ambari.infra.json.OffsetDateTimeToStringConverter;
+import org.springframework.batch.core.BatchStatus;
 import org.springframework.batch.core.StepExecution;
 
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.TimeZone;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 
-public class StepExecutionInfoResponse {
-  private Long id;
-  private Long jobExecutionId;
-  private String jobName;
-  private String name;
-  private String startDate = "-";
-  private String startTime = "-";
-  private String duration = "-";
-  private StepExecutionData stepExecutionData;
-  private long durationMillis;
-
-  public StepExecutionInfoResponse(String jobName, Long jobExecutionId, String 
name, TimeZone timeZone) {
-    this.jobName = jobName;
-    this.jobExecutionId = jobExecutionId;
-    this.name = name;
-    this.stepExecutionData = new StepExecutionData(new StepExecution(name, new 
JobExecution(jobExecutionId)));
-  }
+import io.swagger.annotations.ApiModelProperty;
 
-  public StepExecutionInfoResponse(StepExecution stepExecution, TimeZone 
timeZone) {
-    this.stepExecutionData = new StepExecutionData(stepExecution);
-    this.id = stepExecutionData.getId();
-    this.name = stepExecutionData.getStepName();
-    this.jobName = stepExecutionData.getJobExecution() != null && 
stepExecutionData.getJobExecution().getJobInstance() != null? 
stepExecutionData.getJobExecution().getJobInstance().getJobName():"?";
-    this.jobExecutionId = stepExecutionData.getJobExecutionId();
-    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
-    SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
-    SimpleDateFormat durationFormat = new SimpleDateFormat("HH:mm:ss");
-
-    durationFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
-    timeFormat.setTimeZone(timeZone);
-    dateFormat.setTimeZone(timeZone);
-    if(stepExecutionData.getStartTime() != null) {
-      this.startDate = dateFormat.format(stepExecutionData.getStartTime());
-      this.startTime = timeFormat.format(stepExecutionData.getStartTime());
-      Date endTime = stepExecutionData.getEndTime() != null? 
stepExecutionData.getEndTime():new Date();
-      this.durationMillis = endTime.getTime() - 
stepExecutionData.getStartTime().getTime();
-      this.duration = durationFormat.format(new Date(this.durationMillis));
+public class StepExecutionInfoResponse {
+  private final Long stepExecutionId;
+  private final Long jobExecutionId;
+  private final String jobName;
+  private final String stepName;
+  @JsonSerialize(converter = OffsetDateTimeToStringConverter.class)
+  private final OffsetDateTime startTime;
+  @JsonSerialize(converter = OffsetDateTimeToStringConverter.class)
+  private final OffsetDateTime endTime;
+  @JsonSerialize(converter = DurationToStringConverter.class)
+  @ApiModelProperty(dataType = "java.lang.String", example = "PT5.311S")
+  private final Duration duration;
+  private final BatchStatus batchStatus;
+  @ApiModelProperty(example = "COMPLETED", allowableValues = "UNKNOWN, 
EXECUTING, COMPLETED, NOOP, FAILED, STOPPED")
+  private final String exitCode;
+  private final String exitDescription;
+
+
+  public StepExecutionInfoResponse(StepExecution stepExecution) {
+    this.stepExecutionId = stepExecution.getId();
+    this.stepName = stepExecution.getStepName();
+    this.jobName = stepExecution.getJobExecution() != null && 
stepExecution.getJobExecution().getJobInstance() != null ? 
stepExecution.getJobExecution().getJobInstance().getJobName() : "?";
+    this.jobExecutionId = stepExecution.getJobExecutionId();
+    this.startTime = toOffsetDateTime(stepExecution.getStartTime());
+    this.endTime = toOffsetDateTime(stepExecution.getEndTime());
+
+    if(this.startTime != null && this.endTime != null) {
+      this.duration = Duration.between(this.startTime, this.endTime);
+    }
+    else {
+      this.duration = null;
     }
 
+    this.batchStatus = stepExecution.getStatus();
+    if (stepExecution.getExitStatus() != null) {
+      this.exitCode = stepExecution.getExitStatus().getExitCode();
+      this.exitDescription = 
stepExecution.getExitStatus().getExitDescription();
+    }
+    else {
+      this.exitCode = null;
+      this.exitDescription = null;
+    }
   }
 
-  public Long getId() {
-    return this.id;
+  public Long getStepExecutionId() {
+    return this.stepExecutionId;
   }
 
   public Long getJobExecutionId() {
     return this.jobExecutionId;
   }
 
-  public String getName() {
-    return this.name;
+  public String getStepName() {
+    return this.stepName;
   }
 
   public String getJobName() {
     return this.jobName;
   }
 
-  public String getStartDate() {
-    return this.startDate;
-  }
-
-  public String getStartTime() {
-    return this.startTime;
+  public OffsetDateTime getStartTime() {
+    return startTime;
   }
 
-  public String getDuration() {
-    return this.duration;
+  public OffsetDateTime getEndTime() {
+    return endTime;
   }
 
-  public long getDurationMillis() {
-    return this.durationMillis;
+  public Duration getDuration() {
+    return duration;
   }
 
-  public String getStatus() {
-    return this.id != 
null?this.stepExecutionData.getStatus().toString():"NONE";
+  public BatchStatus getBatchStatus() {
+    return batchStatus;
   }
 
   public String getExitCode() {
-    return this.id != 
null?this.stepExecutionData.getExitStatus().getExitCode():"NONE";
+    return exitCode;
   }
 
-  @JsonIgnore
-  public StepExecutionData getStepExecution() {
-    return this.stepExecutionData;
+  public String getExitDescription() {
+    return exitDescription;
   }
 }
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionProgressResponse.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionProgressResponse.java
index 26f9ed4f..d2404e5e 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionProgressResponse.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/StepExecutionProgressResponse.java
@@ -21,16 +21,16 @@
 import org.springframework.batch.admin.history.StepExecutionHistory;
 import org.springframework.batch.admin.web.StepExecutionProgress;
 
-public class StepExecutionProgressResponse {
+import com.fasterxml.jackson.annotation.JsonProperty;
 
-  private StepExecutionProgress stepExecutionProgress;
+public class StepExecutionProgressResponse {
 
-  private StepExecutionHistory stepExecutionHistory;
+  private final StepExecutionProgress stepExecutionProgress;
 
-  private StepExecutionInfoResponse stepExecutionInfoResponse;
+  private final StepExecutionHistory stepExecutionHistory;
 
-  public StepExecutionProgressResponse() {
-  }
+  @JsonProperty("stepExecution")
+  private final StepExecutionInfoResponse stepExecutionInfoResponse;
 
   public StepExecutionProgressResponse(StepExecutionProgress 
stepExecutionProgress, StepExecutionHistory stepExecutionHistory,
                                        StepExecutionInfoResponse 
stepExecutionInfoResponse) {
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/JobExecutionData.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/JobExecutionData.java
deleted file mode 100644
index 28e262ae..00000000
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/JobExecutionData.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.infra.model.wrapper;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.google.common.collect.Lists;
-import org.springframework.batch.core.BatchStatus;
-import org.springframework.batch.core.ExitStatus;
-import org.springframework.batch.core.JobExecution;
-import org.springframework.batch.core.JobInstance;
-import org.springframework.batch.core.JobParameters;
-import org.springframework.batch.core.StepExecution;
-import org.springframework.batch.item.ExecutionContext;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-
-/**
- * Wrapper for #{{@link JobExecution}}
- */
-public class JobExecutionData {
-
-  private JobExecution jobExecution;
-
-  public JobExecutionData(JobExecution jobExecution) {
-    this.jobExecution = jobExecution;
-  }
-
-  @JsonIgnore
-  public JobExecution getJobExecution() {
-    return jobExecution;
-  }
-
-  @JsonIgnore
-  public Collection<StepExecution> getStepExecutions() {
-    return jobExecution.getStepExecutions();
-  }
-
-  public JobParameters getJobParameters() {
-    return jobExecution.getJobParameters();
-  }
-
-  public JobInstance getJobInstance() {
-    return jobExecution.getJobInstance();
-  }
-
-  public Collection<StepExecutionData> getStepExecutionDataList() {
-    List<StepExecutionData> stepExecutionDataList = Lists.newArrayList();
-    Collection<StepExecution> stepExecutions = getStepExecutions();
-    if (stepExecutions != null) {
-      for (StepExecution stepExecution : stepExecutions) {
-        stepExecutionDataList.add(new StepExecutionData(stepExecution));
-      }
-    }
-    return stepExecutionDataList;
-  }
-
-  public BatchStatus getStatus() {
-    return jobExecution.getStatus();
-  }
-
-  public Date getStartTime() {
-    return jobExecution.getStartTime();
-  }
-
-  public Date getCreateTime() {
-    return jobExecution.getCreateTime();
-  }
-
-  public Date getEndTime() {
-    return jobExecution.getEndTime();
-  }
-
-  public Date getLastUpdated() {
-    return jobExecution.getLastUpdated();
-  }
-
-  public ExitStatus getExitStatus() {
-    return jobExecution.getExitStatus();
-  }
-
-  public ExecutionContext getExecutionContext() {
-    return jobExecution.getExecutionContext();
-  }
-
-  public List<Throwable> getFailureExceptions() {
-    return jobExecution.getFailureExceptions();
-  }
-
-  public String getJobConfigurationName() {
-    return jobExecution.getJobConfigurationName();
-  }
-
-  public Long getId() {
-    return jobExecution.getId();
-  }
-
-  public Long getJobId() {
-    return jobExecution.getJobId();
-  }
-}
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/StepExecutionData.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/StepExecutionData.java
deleted file mode 100644
index 26552ae6..00000000
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/model/wrapper/StepExecutionData.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.infra.model.wrapper;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import org.springframework.batch.core.BatchStatus;
-import org.springframework.batch.core.ExitStatus;
-import org.springframework.batch.core.JobExecution;
-import org.springframework.batch.core.StepExecution;
-import org.springframework.batch.item.ExecutionContext;
-
-import java.util.Date;
-import java.util.List;
-
-/**
- * Wrapper for #{{@link StepExecution}}
- */
-public class StepExecutionData {
-
-  @JsonIgnore
-  private final JobExecution jobExecution;
-
-  @JsonIgnore
-  private final StepExecution stepExecution;
-
-
-  public StepExecutionData(StepExecution stepExecution) {
-    this.stepExecution = stepExecution;
-    this.jobExecution = stepExecution.getJobExecution();
-  }
-
-  @JsonIgnore
-  public JobExecution getJobExecution() {
-    return jobExecution;
-  }
-
-  @JsonIgnore
-  public StepExecution getStepExecution() {
-    return stepExecution;
-  }
-
-  public String getStepName() {
-    return stepExecution.getStepName();
-  }
-
-  public int getReadCount() {
-    return stepExecution.getReadCount();
-  }
-
-  public BatchStatus getStatus() {
-    return stepExecution.getStatus();
-  }
-
-  public int getWriteCount() {
-    return stepExecution.getWriteCount();
-  }
-
-  public int getCommitCount() {
-    return stepExecution.getCommitCount();
-  }
-
-  public int getRollbackCount() {
-    return stepExecution.getRollbackCount();
-  }
-
-  public int getReadSkipCount() {
-    return stepExecution.getReadSkipCount();
-  }
-
-  public int getProcessSkipCount() {
-    return stepExecution.getProcessSkipCount();
-  }
-
-  public Date getStartTime() {
-    return stepExecution.getStartTime();
-  }
-
-  public int getWriteSkipCount() {
-    return stepExecution.getWriteSkipCount();
-  }
-
-  public Date getEndTime() {
-    return stepExecution.getEndTime();
-  }
-
-  public Date getLastUpdated() {
-    return stepExecution.getLastUpdated();
-  }
-
-  public ExecutionContext getExecutionContext() {
-    return stepExecution.getExecutionContext();
-  }
-
-  public ExitStatus getExitStatus() {
-    return stepExecution.getExitStatus();
-  }
-
-  public boolean isTerminateOnly() {
-    return stepExecution.isTerminateOnly();
-  }
-
-  public int getFilterCount() {
-    return stepExecution.getFilterCount();
-  }
-
-  public List<Throwable> getFailureExceptions() {
-    return stepExecution.getFailureExceptions();
-  }
-
-  public Long getId() {
-    return stepExecution.getId();
-  }
-
-  public Long getJobExecutionId() {
-    return stepExecution.getJobExecutionId();
-  }
-}
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/ApiDocResource.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/ApiDocResource.java
deleted file mode 100644
index 18dfdd9d..00000000
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/ApiDocResource.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.infra.rest;
-
-import io.swagger.annotations.ApiOperation;
-import org.apache.ambari.infra.doc.InfraManagerApiDocStorage;
-import org.springframework.context.annotation.Scope;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-@Path("swagger.{type:json|yaml}")
-@Named
-@Scope("request")
-public class ApiDocResource {
-
-  @Inject
-  private InfraManagerApiDocStorage infraManagerApiDocStorage;
-
-  @GET
-  @Produces({MediaType.APPLICATION_JSON, "application/yaml"})
-  @ApiOperation(value = "The swagger definition in either JSON or YAML", 
hidden = true)
-  public Response swaggerDefinitionResponse(@PathParam("type") String type) {
-    Response response = Response.status(404).build();
-    if (infraManagerApiDocStorage.getSwagger() != null) {
-      if ("yaml".equalsIgnoreCase(type)) {
-        response = 
Response.ok().entity(infraManagerApiDocStorage.getSwaggerYaml()).type("application/yaml").build();
-      } else {
-        response = 
Response.ok().entity(infraManagerApiDocStorage.getSwagger()).build();
-      }
-    }
-    return response;
-  }
-}
diff --git 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java
 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java
index 43f7c41f..f5de3c0e 100644
--- 
a/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java
+++ 
b/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java
@@ -35,12 +35,12 @@
 
 import org.apache.ambari.infra.manager.JobManager;
 import org.apache.ambari.infra.model.ExecutionContextResponse;
-import org.apache.ambari.infra.model.JobDetailsResponse;
 import org.apache.ambari.infra.model.JobExecutionDetailsResponse;
 import org.apache.ambari.infra.model.JobExecutionInfoResponse;
 import org.apache.ambari.infra.model.JobExecutionRequest;
 import org.apache.ambari.infra.model.JobExecutionRestartRequest;
 import org.apache.ambari.infra.model.JobExecutionStopRequest;
+import org.apache.ambari.infra.model.JobInstanceDetailsResponse;
 import org.apache.ambari.infra.model.JobInstanceStartRequest;
 import org.apache.ambari.infra.model.JobRequest;
 import org.apache.ambari.infra.model.PageRequest;
@@ -120,7 +120,7 @@ public JobExecutionInfoResponse startJob(@BeanParam @Valid 
JobInstanceStartReque
   @Produces({"application/json"})
   @Path("{jobName}/info")
   @ApiOperation("Get job details by job name.")
-  public JobDetailsResponse getJobDetails(@BeanParam @Valid JobRequest 
jobRequest) throws NoSuchJobException {
+  public List<JobInstanceDetailsResponse> getJobDetails(@BeanParam @Valid 
JobRequest jobRequest) throws NoSuchJobException {
     return jobManager.getJobDetails(jobRequest.getJobName(), 
jobRequest.getPage(), jobRequest.getSize());
   }
 
@@ -136,7 +136,7 @@ public JobDetailsResponse getJobDetails(@BeanParam @Valid 
JobRequest jobRequest)
   @Produces({"application/json"})
   @Path("/executions/{jobExecutionId}")
   @ApiOperation("Get job and step details for job execution instance.")
-  public JobExecutionDetailsResponse 
getExectionInfo(@PathParam("jobExecutionId") @Valid Long jobExecutionId) throws 
NoSuchJobExecutionException {
+  public JobExecutionDetailsResponse 
getExecutionInfo(@PathParam("jobExecutionId") @Valid Long jobExecutionId) 
throws NoSuchJobExecutionException {
     return jobManager.getExecutionInfo(jobExecutionId);
   }
 
@@ -169,8 +169,8 @@ public Integer stopAll() {
   @GET
   @Produces({"application/json"})
   @Path("/{jobName}/{jobInstanceId}/executions")
-  @ApiOperation("Get execution for job instance.")
-  public List<JobExecutionInfoResponse> getExecutionsForInstance(@BeanParam 
@Valid JobExecutionRequest request) throws
+  @ApiOperation("Get execution of job instance.")
+  public List<JobExecutionInfoResponse> getExecutionsOfInstance(@BeanParam 
@Valid JobExecutionRequest request) throws
           NoSuchJobException, NoSuchJobInstanceException {
     return jobManager.getExecutionsForJobInstance(request.getJobName(), 
request.getJobInstanceId());
   }
diff --git a/ambari-infra-manager/src/main/resources/infra-manager.properties 
b/ambari-infra-manager/src/main/resources/infra-manager.properties
index 81339059..c3ec9d0d 100644
--- a/ambari-infra-manager/src/main/resources/infra-manager.properties
+++ b/ambari-infra-manager/src/main/resources/infra-manager.properties
@@ -18,6 +18,7 @@ infra-manager.batch.db.init=false
 infra-manager.batch.db.username=admin
 infra-manager.batch.db.password=admin
 infra-manager.server.data.folder=/tmp/ambariInfraManager
+infra-manager.admin-user.password=admin
 
 # Archive Service Logs
 infra-manager.jobs.solr_data_archiving.archive_service_logs.enabled=true
diff --git a/ambari-infra-manager/src/main/resources/swagger/swagger.html 
b/ambari-infra-manager/src/main/resources/swagger/swagger.html
index b24dd25c..69478fd0 100644
--- a/ambari-infra-manager/src/main/resources/swagger/swagger.html
+++ b/ambari-infra-manager/src/main/resources/swagger/swagger.html
@@ -57,7 +57,7 @@
       var urlPrefix = location.protocol +'//'+ 
location.hostname+(location.port ? ':'+location.port: '');
       // Build a system
       const ui = SwaggerUIBundle({
-        url: urlPrefix + "/api/v1/swagger.yaml",
+        url: urlPrefix + "/swagger.yaml",
         dom_id: '#swagger-ui',
         deepLinking: true,
         presets: [
diff --git 
a/ambari-infra-manager/src/test/java/org/apache/ambari/infra/job/archive/DocumentExporterTest.java
 
b/ambari-infra-manager/src/test/java/org/apache/ambari/infra/job/archive/DocumentExporterTest.java
index d2e7b04d..fe2b0375 100644
--- 
a/ambari-infra-manager/src/test/java/org/apache/ambari/infra/job/archive/DocumentExporterTest.java
+++ 
b/ambari-infra-manager/src/test/java/org/apache/ambari/infra/job/archive/DocumentExporterTest.java
@@ -36,8 +36,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.springframework.batch.core.BatchStatus;
 import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.StepContribution;
 import org.springframework.batch.core.StepExecution;
 import org.springframework.batch.core.scope.context.ChunkContext;
 import org.springframework.batch.core.scope.context.StepContext;
@@ -56,6 +56,8 @@
   private static final Document DOCUMENT_3 = new Document(new HashMap<String, 
Object>() {{
     put("id", "3");
   }});
+  private static final StepContribution ANY_STEP_CONTRIBUTION = new 
StepContribution(new StepExecution("any", new JobExecution(1L)));
+
   private DocumentExporter documentExporter;
   @Mock
   private ItemStreamReader<Document> reader;
@@ -74,8 +76,8 @@
   private static final Document DOCUMENT = new Document(new HashMap<String, 
Object>() {{ put("id", "1"); }});
 
   @Before
-  public void setUp() throws Exception {
-    chunkContext = chunkContext(BatchStatus.STARTED);
+  public void setUp() {
+    chunkContext = chunkContext(false);
     documentExporter = documentExporter(2);
   }
 
@@ -83,15 +85,16 @@ private DocumentExporter documentExporter(int 
writeBlockSize) {
     return new DocumentExporter(reader, documentDestination, writeBlockSize, 
jobContextRepository);
   }
 
-  private ChunkContext chunkContext(BatchStatus batchStatus) {
+  private ChunkContext chunkContext(boolean terminate) {
     StepExecution stepExecution = new StepExecution("exportDoc", new 
JobExecution(JOB_EXECUTION_ID));
     stepExecution.setId(STEP_EXECUTION_ID);
-    stepExecution.getJobExecution().setStatus(batchStatus);
+    if (terminate)
+      stepExecution.setTerminateOnly();
     return new ChunkContext(new StepContext(stepExecution));
   }
 
   @After
-  public void tearDown() throws Exception {
+  public void tearDown() {
     verifyAll();
   }
 
@@ -102,7 +105,7 @@ public void testNothingToRead() throws Exception {
     reader.close(); expectLastCall();
     replayAll();
 
-    documentExporter.execute(null, chunkContext);
+    documentExporter.execute(ANY_STEP_CONTRIBUTION, chunkContext);
   }
 
   private ExecutionContext executionContext(ChunkContext chunkContext) {
@@ -120,7 +123,7 @@ public void testWriteLessDocumentsThanWriteBlockSize() 
throws Exception {
     documentItemWriter.close(); expectLastCall();
     replayAll();
 
-    assertThat(documentExporter.execute(null, chunkContext), 
is(RepeatStatus.FINISHED));
+    assertThat(documentExporter.execute(ANY_STEP_CONTRIBUTION, chunkContext), 
is(RepeatStatus.FINISHED));
   }
 
   @Test
@@ -143,7 +146,7 @@ public void testWriteMoreDocumentsThanWriteBlockSize() 
throws Exception {
     documentItemWriter2.close(); expectLastCall();
     replayAll();
 
-    assertThat(documentExporter.execute(null, chunkContext), 
is(RepeatStatus.FINISHED));
+    assertThat(documentExporter.execute(ANY_STEP_CONTRIBUTION, chunkContext), 
is(RepeatStatus.FINISHED));
   }
 
   @Test(expected = IOException.class)
@@ -157,7 +160,7 @@ public void testReadError() throws Exception {
     reader.close(); expectLastCall();
     replayAll();
 
-    documentExporter.execute(null, chunkContext);
+    documentExporter.execute(ANY_STEP_CONTRIBUTION, chunkContext);
   }
 
   @Test(expected = UncheckedIOException.class)
@@ -170,12 +173,12 @@ public void testWriteError() throws Exception {
     reader.close(); expectLastCall();
     replayAll();
 
-    documentExporter.execute(null, chunkContext);
+    documentExporter.execute(ANY_STEP_CONTRIBUTION, chunkContext);
   }
 
   @Test
   public void testStopAndRestartExportsAllDocuments() throws Exception {
-    ChunkContext stoppingChunkContext = chunkContext(BatchStatus.STOPPING);
+    ChunkContext stoppingChunkContext = chunkContext(true);
     DocumentExporter documentExporter = documentExporter(1);
 
     reader.open(executionContext(chunkContext)); expectLastCall();
@@ -206,9 +209,9 @@ public void testStopAndRestartExportsAllDocuments() throws 
Exception {
     reader.close(); expectLastCall();
     replayAll();
 
-    RepeatStatus repeatStatus = documentExporter.execute(null, 
this.chunkContext);
+    RepeatStatus repeatStatus = 
documentExporter.execute(ANY_STEP_CONTRIBUTION, this.chunkContext);
     assertThat(repeatStatus, is(RepeatStatus.CONTINUABLE));
-    repeatStatus = documentExporter.execute(null, this.chunkContext);
+    repeatStatus = documentExporter.execute(ANY_STEP_CONTRIBUTION, 
this.chunkContext);
     assertThat(repeatStatus, is(RepeatStatus.FINISHED));
   }
 }
\ No newline at end of file
diff --git 
a/ambari-infra-manager/src/test/java/org/apache/ambari/infra/model/ISO8601DateFormatterTest.java
 
b/ambari-infra-manager/src/test/java/org/apache/ambari/infra/model/ISO8601DateFormatterTest.java
new file mode 100644
index 00000000..b2bb0e8a
--- /dev/null
+++ 
b/ambari-infra-manager/src/test/java/org/apache/ambari/infra/model/ISO8601DateFormatterTest.java
@@ -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.
+ */
+package org.apache.ambari.infra.model;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+import java.text.ParseException;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.util.Date;
+
+import org.junit.Test;
+
+public class ISO8601DateFormatterTest {
+
+  @Test
+  public void testFormat() {
+    OffsetDateTime offsetDateTime = OffsetDateTime.of(
+            2018, 11, 30,
+            2, 30, 11, 0,
+            ZoneOffset.ofHoursMinutes(1, 30));
+    String text = new 
ISO8601DateFormatter().format(Date.from(offsetDateTime.toInstant()));
+    assertThat(text, is("2018-11-30T01:00:11Z"));
+  }
+
+  @Test
+  public void testParse() throws ParseException {
+    Date now = new Date();
+    ISO8601DateFormatter iso8601DateFormatter = new ISO8601DateFormatter();
+    Date parsed = iso8601DateFormatter.parse(iso8601DateFormatter.format(now));
+    assertThat(parsed, is(now));
+  }
+}
\ No newline at end of file


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to