You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by di...@apache.org on 2026/08/10 00:18:40 UTC

(airavata) branch refactor-single-module updated: Adding batch job process and related controller methods

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

DImuthuUpe pushed a commit to branch refactor-single-module
in repository https://gitbox.apache.org/repos/asf/airavata.git


The following commit(s) were added to refs/heads/refactor-single-module by this push:
     new cd6a8c7c07 Adding batch job process and related controller methods
cd6a8c7c07 is described below

commit cd6a8c7c07ea8201aec549ebea6e95b1546646a8
Author: DImuthuUpe <di...@gmail.com>
AuthorDate: Sun Aug 9 20:18:33 2026 -0400

    Adding batch job process and related controller methods
---
 ...a => BatchApplicationDeploymentController.java} |  38 +++---
 .../airavata/api/BatchJobProcessController.java    |  61 +++++++++
 .../BatchApplicationDeploymentRequestDto.java      |  86 ++++++++++++
 .../BatchApplicationDeploymentResponseDto.java     |  78 +++++++++++
 ...questDto.java => BatchJobConfigRequestDto.java} |  68 +---------
 ...onseDto.java => BatchJobConfigResponseDto.java} |  68 +---------
 .../mapper/BatchApplicationDeploymentMapper.java   |  44 ++++++
 .../application/mapper/BatchJobConfigMapper.java   |  26 ++++
 .../mapper/SlurmApplicationDeploymentMapper.java   |  32 -----
 .../BatchApplicationDeploymentEntity.java          | 128 ++++++++++++++++++
 ...nDeploymentEntity.java => BatchJobConfigs.java} |  96 ++-----------
 .../model/template/ApplicationTemplateEntity.java  |   8 +-
 ...a => BatchApplicationDeploymentRepository.java} |   8 +-
 .../service/ApplicationTemplateService.java        |   6 +-
 ...java => BatchApplicationDeploymentService.java} |  56 +++++---
 .../process/dto/BatchJobProcessRequestDto.java     |  35 +++++
 .../process/dto/BatchJobProcessResponseDto.java    |  57 ++++++++
 .../process/mapper/BatchJobProcessMapper.java      |  30 +++++
 .../airavata/process/model/BatchJobProcess.java    |  78 +++++++++++
 .../repository/BatchJobProcessRepository.java      |  14 ++
 .../process/service/BatchJobProcessService.java    | 149 +++++++++++++++++++++
 21 files changed, 871 insertions(+), 295 deletions(-)

diff --git a/src/main/java/org/apache/airavata/api/SlurmApplicationDeploymentController.java b/src/main/java/org/apache/airavata/api/BatchApplicationDeploymentController.java
similarity index 57%
rename from src/main/java/org/apache/airavata/api/SlurmApplicationDeploymentController.java
rename to src/main/java/org/apache/airavata/api/BatchApplicationDeploymentController.java
index a44a1c8c98..d6cb6b6f3f 100644
--- a/src/main/java/org/apache/airavata/api/SlurmApplicationDeploymentController.java
+++ b/src/main/java/org/apache/airavata/api/BatchApplicationDeploymentController.java
@@ -2,9 +2,9 @@ package org.apache.airavata.api;
 
 import jakarta.validation.Valid;
 import java.util.List;
-import org.apache.airavata.application.dto.deployment.SlurmApplicationDeploymentRequestDto;
-import org.apache.airavata.application.dto.deployment.SlurmApplicationDeploymentResponseDto;
-import org.apache.airavata.application.service.SlurmApplicationDeploymentService;
+import org.apache.airavata.application.dto.deployment.BatchApplicationDeploymentRequestDto;
+import org.apache.airavata.application.dto.deployment.BatchApplicationDeploymentResponseDto;
+import org.apache.airavata.application.service.BatchApplicationDeploymentService;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -19,44 +19,44 @@ import org.springframework.web.bind.annotation.RestController;
 
 @RestController
 @RequestMapping("/api/v1/slurm-deployments")
-public class SlurmApplicationDeploymentController {
+public class BatchApplicationDeploymentController {
 
-    private final SlurmApplicationDeploymentService slurmApplicationDeploymentService;
+    private final BatchApplicationDeploymentService batchApplicationDeploymentService;
 
-    public SlurmApplicationDeploymentController(SlurmApplicationDeploymentService slurmApplicationDeploymentService) {
-        this.slurmApplicationDeploymentService = slurmApplicationDeploymentService;
+    public BatchApplicationDeploymentController(BatchApplicationDeploymentService batchApplicationDeploymentService) {
+        this.batchApplicationDeploymentService = batchApplicationDeploymentService;
     }
 
     /** Lists every deployment, or only those of {@code templateId} when supplied. */
     @GetMapping
-    public List<SlurmApplicationDeploymentResponseDto> getAllDeployments(
+    public List<BatchApplicationDeploymentResponseDto> getAllDeployments(
             @RequestParam(required = false) String templateId) {
         return templateId == null
-                ? slurmApplicationDeploymentService.getAllDeployments()
-                : slurmApplicationDeploymentService.getDeploymentsByTemplate(templateId);
+                ? batchApplicationDeploymentService.getAllDeployments()
+                : batchApplicationDeploymentService.getDeploymentsByTemplate(templateId);
     }
 
     @GetMapping("/{deploymentId}")
-    public SlurmApplicationDeploymentResponseDto getDeployment(@PathVariable String deploymentId) {
-        return slurmApplicationDeploymentService.getDeployment(deploymentId);
+    public BatchApplicationDeploymentResponseDto getDeployment(@PathVariable String deploymentId) {
+        return batchApplicationDeploymentService.getDeployment(deploymentId);
     }
 
     @PostMapping
     @ResponseStatus(HttpStatus.CREATED)
-    public SlurmApplicationDeploymentResponseDto createDeployment(
-            @Valid @RequestBody SlurmApplicationDeploymentRequestDto request) {
-        return slurmApplicationDeploymentService.createDeployment(request);
+    public BatchApplicationDeploymentResponseDto createDeployment(
+            @Valid @RequestBody BatchApplicationDeploymentRequestDto request) {
+        return batchApplicationDeploymentService.createDeployment(request);
     }
 
     @PutMapping("/{deploymentId}")
-    public SlurmApplicationDeploymentResponseDto updateDeployment(
-            @PathVariable String deploymentId, @Valid @RequestBody SlurmApplicationDeploymentRequestDto request) {
-        return slurmApplicationDeploymentService.updateDeployment(deploymentId, request);
+    public BatchApplicationDeploymentResponseDto updateDeployment(
+            @PathVariable String deploymentId, @Valid @RequestBody BatchApplicationDeploymentRequestDto request) {
+        return batchApplicationDeploymentService.updateDeployment(deploymentId, request);
     }
 
     @DeleteMapping("/{deploymentId}")
     @ResponseStatus(HttpStatus.NO_CONTENT)
     public void deleteDeployment(@PathVariable String deploymentId) {
-        slurmApplicationDeploymentService.deleteDeployment(deploymentId);
+        batchApplicationDeploymentService.deleteDeployment(deploymentId);
     }
 }
diff --git a/src/main/java/org/apache/airavata/api/BatchJobProcessController.java b/src/main/java/org/apache/airavata/api/BatchJobProcessController.java
new file mode 100644
index 0000000000..3d49e918ec
--- /dev/null
+++ b/src/main/java/org/apache/airavata/api/BatchJobProcessController.java
@@ -0,0 +1,61 @@
+package org.apache.airavata.api;
+
+import jakarta.validation.Valid;
+import java.util.List;
+import org.apache.airavata.process.dto.BatchJobProcessRequestDto;
+import org.apache.airavata.process.dto.BatchJobProcessResponseDto;
+import org.apache.airavata.process.service.BatchJobProcessService;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+/** The owning user is never a request parameter — see {@code BatchJobProcessService.createProcess}. */
+@RestController
+@RequestMapping("/api/v1/batch-job-processes")
+public class BatchJobProcessController {
+
+    private final BatchJobProcessService batchJobProcessService;
+
+    public BatchJobProcessController(BatchJobProcessService batchJobProcessService) {
+        this.batchJobProcessService = batchJobProcessService;
+    }
+
+    /** Lists every process, or only those of {@code deploymentId} when supplied. */
+    @GetMapping
+    public List<BatchJobProcessResponseDto> getAllProcesses(@RequestParam(required = false) String deploymentId) {
+        return deploymentId == null
+                ? batchJobProcessService.getAllProcesses()
+                : batchJobProcessService.getProcessesByDeployment(deploymentId);
+    }
+
+    @GetMapping("/{processId}")
+    public BatchJobProcessResponseDto getProcess(@PathVariable String processId) {
+        return batchJobProcessService.getProcess(processId);
+    }
+
+    @PostMapping
+    @ResponseStatus(HttpStatus.CREATED)
+    public BatchJobProcessResponseDto createProcess(@Valid @RequestBody BatchJobProcessRequestDto request) {
+        return batchJobProcessService.createProcess(request);
+    }
+
+    @PutMapping("/{processId}")
+    public BatchJobProcessResponseDto updateProcess(
+            @PathVariable String processId, @Valid @RequestBody BatchJobProcessRequestDto request) {
+        return batchJobProcessService.updateProcess(processId, request);
+    }
+
+    @DeleteMapping("/{processId}")
+    @ResponseStatus(HttpStatus.NO_CONTENT)
+    public void deleteProcess(@PathVariable String processId) {
+        batchJobProcessService.deleteProcess(processId);
+    }
+}
diff --git a/src/main/java/org/apache/airavata/application/dto/deployment/BatchApplicationDeploymentRequestDto.java b/src/main/java/org/apache/airavata/application/dto/deployment/BatchApplicationDeploymentRequestDto.java
new file mode 100644
index 0000000000..d3f07f6cae
--- /dev/null
+++ b/src/main/java/org/apache/airavata/application/dto/deployment/BatchApplicationDeploymentRequestDto.java
@@ -0,0 +1,86 @@
+package org.apache.airavata.application.dto.deployment;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+/**
+ * Create/update payload for a batch application deployment of an application template.
+ * Required fields mirror the {@code nullable = false} columns on the entity.
+ */
+public class BatchApplicationDeploymentRequestDto {
+
+    @NotBlank(message = "Template id cannot be blank")
+    private String templateId;
+
+    private String slurmClusterId;
+
+    @NotBlank(message = "Slurm run section cannot be blank")
+    private String slurmRunSection;
+
+    @NotNull(message = "Batch job config cannot be null")
+    @Valid
+    private BatchJobConfigRequestDto batchJobConfig;
+
+    @NotBlank(message = "Default submission credential id cannot be blank")
+    private String defaultSubmissionCredentialId;
+
+    private String workDir;
+    private String partition;
+
+    public String getTemplateId() {
+        return templateId;
+    }
+
+    public void setTemplateId(String templateId) {
+        this.templateId = templateId;
+    }
+
+    public String getSlurmClusterId() {
+        return slurmClusterId;
+    }
+
+    public void setSlurmClusterId(String slurmClusterId) {
+        this.slurmClusterId = slurmClusterId;
+    }
+
+    public String getSlurmRunSection() {
+        return slurmRunSection;
+    }
+
+    public void setSlurmRunSection(String slurmRunSection) {
+        this.slurmRunSection = slurmRunSection;
+    }
+
+    public BatchJobConfigRequestDto getBatchJobConfig() {
+        return batchJobConfig;
+    }
+
+    public void setBatchJobConfig(BatchJobConfigRequestDto batchJobConfig) {
+        this.batchJobConfig = batchJobConfig;
+    }
+
+    public String getDefaultSubmissionCredentialId() {
+        return defaultSubmissionCredentialId;
+    }
+
+    public void setDefaultSubmissionCredentialId(String defaultSubmissionCredentialId) {
+        this.defaultSubmissionCredentialId = defaultSubmissionCredentialId;
+    }
+
+    public String getWorkDir() {
+        return workDir;
+    }
+
+    public void setWorkDir(String workDir) {
+        this.workDir = workDir;
+    }
+
+    public String getPartition() {
+        return partition;
+    }
+
+    public void setPartition(String partition) {
+        this.partition = partition;
+    }
+}
diff --git a/src/main/java/org/apache/airavata/application/dto/deployment/BatchApplicationDeploymentResponseDto.java b/src/main/java/org/apache/airavata/application/dto/deployment/BatchApplicationDeploymentResponseDto.java
new file mode 100644
index 0000000000..15e15648b6
--- /dev/null
+++ b/src/main/java/org/apache/airavata/application/dto/deployment/BatchApplicationDeploymentResponseDto.java
@@ -0,0 +1,78 @@
+package org.apache.airavata.application.dto.deployment;
+
+/** Read view of a batch application deployment; associations are referenced by id only. */
+public class BatchApplicationDeploymentResponseDto {
+
+    private String deploymentId;
+    private String templateId;
+    private String slurmClusterId;
+    private String slurmRunSection;
+    private BatchJobConfigResponseDto batchJobConfig;
+    private String defaultSubmissionCredentialId;
+    private String workDir;
+    private String partition;
+
+    public String getDeploymentId() {
+        return deploymentId;
+    }
+
+    public void setDeploymentId(String deploymentId) {
+        this.deploymentId = deploymentId;
+    }
+
+    public String getTemplateId() {
+        return templateId;
+    }
+
+    public void setTemplateId(String templateId) {
+        this.templateId = templateId;
+    }
+
+    public String getSlurmClusterId() {
+        return slurmClusterId;
+    }
+
+    public void setSlurmClusterId(String slurmClusterId) {
+        this.slurmClusterId = slurmClusterId;
+    }
+
+    public String getSlurmRunSection() {
+        return slurmRunSection;
+    }
+
+    public void setSlurmRunSection(String slurmRunSection) {
+        this.slurmRunSection = slurmRunSection;
+    }
+
+    public BatchJobConfigResponseDto getBatchJobConfig() {
+        return batchJobConfig;
+    }
+
+    public void setBatchJobConfig(BatchJobConfigResponseDto batchJobConfig) {
+        this.batchJobConfig = batchJobConfig;
+    }
+
+    public String getDefaultSubmissionCredentialId() {
+        return defaultSubmissionCredentialId;
+    }
+
+    public void setDefaultSubmissionCredentialId(String defaultSubmissionCredentialId) {
+        this.defaultSubmissionCredentialId = defaultSubmissionCredentialId;
+    }
+
+    public String getWorkDir() {
+        return workDir;
+    }
+
+    public void setWorkDir(String workDir) {
+        this.workDir = workDir;
+    }
+
+    public String getPartition() {
+        return partition;
+    }
+
+    public void setPartition(String partition) {
+        this.partition = partition;
+    }
+}
diff --git a/src/main/java/org/apache/airavata/application/dto/deployment/SlurmApplicationDeploymentRequestDto.java b/src/main/java/org/apache/airavata/application/dto/deployment/BatchJobConfigRequestDto.java
similarity index 67%
rename from src/main/java/org/apache/airavata/application/dto/deployment/SlurmApplicationDeploymentRequestDto.java
rename to src/main/java/org/apache/airavata/application/dto/deployment/BatchJobConfigRequestDto.java
index 47576f0266..d7f848b7f3 100644
--- a/src/main/java/org/apache/airavata/application/dto/deployment/SlurmApplicationDeploymentRequestDto.java
+++ b/src/main/java/org/apache/airavata/application/dto/deployment/BatchJobConfigRequestDto.java
@@ -4,19 +4,8 @@ import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Positive;
 
-/**
- * Create/update payload for a Slurm deployment of an application template.
- * Required fields mirror the {@code nullable = false} columns on the entity.
- */
-public class SlurmApplicationDeploymentRequestDto {
-
-    @NotBlank(message = "Template id cannot be blank")
-    private String templateId;
-
-    private String slurmClusterId;
-
-    @NotBlank(message = "Slurm run section cannot be blank")
-    private String slurmRunSection;
+/** Create/update payload for a deployment's batch-scheduler resource request. */
+public class BatchJobConfigRequestDto {
 
     @NotNull(message = "Wall time cannot be null")
     @Positive(message = "Wall time must be positive")
@@ -25,9 +14,6 @@ public class SlurmApplicationDeploymentRequestDto {
     @NotBlank(message = "Allocation cannot be blank")
     private String allocation;
 
-    @NotBlank(message = "User cannot be blank")
-    private String user;
-
     // Slurm resource allocation parameters
     private Integer cpus;
     private String mem;
@@ -45,32 +31,6 @@ public class SlurmApplicationDeploymentRequestDto {
     private Integer gpusPerNode;
 
     private String constraints;
-    private String workDir;
-    private String partition;
-
-    public String getTemplateId() {
-        return templateId;
-    }
-
-    public void setTemplateId(String templateId) {
-        this.templateId = templateId;
-    }
-
-    public String getSlurmClusterId() {
-        return slurmClusterId;
-    }
-
-    public void setSlurmClusterId(String slurmClusterId) {
-        this.slurmClusterId = slurmClusterId;
-    }
-
-    public String getSlurmRunSection() {
-        return slurmRunSection;
-    }
-
-    public void setSlurmRunSection(String slurmRunSection) {
-        this.slurmRunSection = slurmRunSection;
-    }
 
     public Long getWallTimeMinutes() {
         return wallTimeMinutes;
@@ -88,14 +48,6 @@ public class SlurmApplicationDeploymentRequestDto {
         this.allocation = allocation;
     }
 
-    public String getUser() {
-        return user;
-    }
-
-    public void setUser(String user) {
-        this.user = user;
-    }
-
     public Integer getCpus() {
         return cpus;
     }
@@ -199,20 +151,4 @@ public class SlurmApplicationDeploymentRequestDto {
     public void setConstraints(String constraints) {
         this.constraints = constraints;
     }
-
-    public String getWorkDir() {
-        return workDir;
-    }
-
-    public void setWorkDir(String workDir) {
-        this.workDir = workDir;
-    }
-
-    public String getPartition() {
-        return partition;
-    }
-
-    public void setPartition(String partition) {
-        this.partition = partition;
-    }
 }
diff --git a/src/main/java/org/apache/airavata/application/dto/deployment/SlurmApplicationDeploymentResponseDto.java b/src/main/java/org/apache/airavata/application/dto/deployment/BatchJobConfigResponseDto.java
similarity index 66%
rename from src/main/java/org/apache/airavata/application/dto/deployment/SlurmApplicationDeploymentResponseDto.java
rename to src/main/java/org/apache/airavata/application/dto/deployment/BatchJobConfigResponseDto.java
index ce81d4c6d0..03b8d29588 100644
--- a/src/main/java/org/apache/airavata/application/dto/deployment/SlurmApplicationDeploymentResponseDto.java
+++ b/src/main/java/org/apache/airavata/application/dto/deployment/BatchJobConfigResponseDto.java
@@ -1,15 +1,11 @@
 package org.apache.airavata.application.dto.deployment;
 
-/** Read view of a Slurm deployment; the owning template is referenced by id only. */
-public class SlurmApplicationDeploymentResponseDto {
+/** Read view of a deployment's batch-scheduler resource request. */
+public class BatchJobConfigResponseDto {
 
-    private String deploymentId;
-    private String templateId;
-    private String slurmClusterId;
-    private String slurmRunSection;
+    private String batchJobConfigId;
     private Long wallTimeMinutes;
     private String allocation;
-    private String user;
 
     // Slurm resource allocation parameters
     private Integer cpus;
@@ -28,39 +24,13 @@ public class SlurmApplicationDeploymentResponseDto {
     private Integer gpusPerNode;
 
     private String constraints;
-    private String workDir;
-    private String partition;
 
-    public String getDeploymentId() {
-        return deploymentId;
+    public String getBatchJobConfigId() {
+        return batchJobConfigId;
     }
 
-    public void setDeploymentId(String deploymentId) {
-        this.deploymentId = deploymentId;
-    }
-
-    public String getTemplateId() {
-        return templateId;
-    }
-
-    public void setTemplateId(String templateId) {
-        this.templateId = templateId;
-    }
-
-    public String getSlurmClusterId() {
-        return slurmClusterId;
-    }
-
-    public void setSlurmClusterId(String slurmClusterId) {
-        this.slurmClusterId = slurmClusterId;
-    }
-
-    public String getSlurmRunSection() {
-        return slurmRunSection;
-    }
-
-    public void setSlurmRunSection(String slurmRunSection) {
-        this.slurmRunSection = slurmRunSection;
+    public void setBatchJobConfigId(String batchJobConfigId) {
+        this.batchJobConfigId = batchJobConfigId;
     }
 
     public Long getWallTimeMinutes() {
@@ -79,14 +49,6 @@ public class SlurmApplicationDeploymentResponseDto {
         this.allocation = allocation;
     }
 
-    public String getUser() {
-        return user;
-    }
-
-    public void setUser(String user) {
-        this.user = user;
-    }
-
     public Integer getCpus() {
         return cpus;
     }
@@ -190,20 +152,4 @@ public class SlurmApplicationDeploymentResponseDto {
     public void setConstraints(String constraints) {
         this.constraints = constraints;
     }
-
-    public String getWorkDir() {
-        return workDir;
-    }
-
-    public void setWorkDir(String workDir) {
-        this.workDir = workDir;
-    }
-
-    public String getPartition() {
-        return partition;
-    }
-
-    public void setPartition(String partition) {
-        this.partition = partition;
-    }
 }
diff --git a/src/main/java/org/apache/airavata/application/mapper/BatchApplicationDeploymentMapper.java b/src/main/java/org/apache/airavata/application/mapper/BatchApplicationDeploymentMapper.java
new file mode 100644
index 0000000000..501b03c451
--- /dev/null
+++ b/src/main/java/org/apache/airavata/application/mapper/BatchApplicationDeploymentMapper.java
@@ -0,0 +1,44 @@
+package org.apache.airavata.application.mapper;
+
+import org.apache.airavata.application.dto.deployment.BatchApplicationDeploymentRequestDto;
+import org.apache.airavata.application.dto.deployment.BatchApplicationDeploymentResponseDto;
+import org.apache.airavata.application.model.deployment.BatchApplicationDeploymentEntity;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * Entity/DTO conversions for batch application deployments.
+ *
+ * <p>The application template, the Slurm cluster and the default submission credential
+ * are shared aggregates resolved by the service from their ids, so none of them is ever
+ * mapped straight off the request. {@code batchJobConfig} is different: it is owned
+ * outright by the deployment (no id to resolve), so it maps directly via
+ * {@link BatchJobConfigMapper} — on update, MapStruct updates the existing nested entity
+ * in place rather than replacing it, preserving its row across edits.
+ */
+@Mapper(
+        componentModel = "spring",
+        uses = BatchJobConfigMapper.class,
+        unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface BatchApplicationDeploymentMapper {
+
+    @Mapping(target = "templateId", source = "applicationTemplate.templateId")
+    @Mapping(target = "slurmClusterId", source = "slurmCluster.clusterId")
+    @Mapping(target = "defaultSubmissionCredentialId", source = "defaultSubmissionCredential.sshCredentialId")
+    BatchApplicationDeploymentResponseDto toResponseDto(BatchApplicationDeploymentEntity entity);
+
+    @Mapping(target = "deploymentId", ignore = true)
+    @Mapping(target = "applicationTemplate", ignore = true)
+    @Mapping(target = "slurmCluster", ignore = true)
+    @Mapping(target = "defaultSubmissionCredential", ignore = true)
+    BatchApplicationDeploymentEntity toEntity(BatchApplicationDeploymentRequestDto dto);
+
+    @Mapping(target = "deploymentId", ignore = true)
+    @Mapping(target = "applicationTemplate", ignore = true)
+    @Mapping(target = "slurmCluster", ignore = true)
+    @Mapping(target = "defaultSubmissionCredential", ignore = true)
+    void updateEntity(
+            BatchApplicationDeploymentRequestDto dto, @MappingTarget BatchApplicationDeploymentEntity entity);
+}
diff --git a/src/main/java/org/apache/airavata/application/mapper/BatchJobConfigMapper.java b/src/main/java/org/apache/airavata/application/mapper/BatchJobConfigMapper.java
new file mode 100644
index 0000000000..025ed099db
--- /dev/null
+++ b/src/main/java/org/apache/airavata/application/mapper/BatchJobConfigMapper.java
@@ -0,0 +1,26 @@
+package org.apache.airavata.application.mapper;
+
+import org.apache.airavata.application.dto.deployment.BatchJobConfigRequestDto;
+import org.apache.airavata.application.dto.deployment.BatchJobConfigResponseDto;
+import org.apache.airavata.application.model.deployment.BatchJobConfigs;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * Entity/DTO conversions for {@link BatchJobConfigs}. Unlike the deployment's other
+ * associations (template, cluster, credential), this one is owned outright — there is no
+ * id to resolve, so it is mapped directly rather than by the service.
+ */
+@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface BatchJobConfigMapper {
+
+    BatchJobConfigResponseDto toResponseDto(BatchJobConfigs entity);
+
+    @Mapping(target = "batchJobConfigId", ignore = true)
+    BatchJobConfigs toEntity(BatchJobConfigRequestDto dto);
+
+    @Mapping(target = "batchJobConfigId", ignore = true)
+    void updateEntity(BatchJobConfigRequestDto dto, @MappingTarget BatchJobConfigs entity);
+}
diff --git a/src/main/java/org/apache/airavata/application/mapper/SlurmApplicationDeploymentMapper.java b/src/main/java/org/apache/airavata/application/mapper/SlurmApplicationDeploymentMapper.java
deleted file mode 100644
index a20209c5c7..0000000000
--- a/src/main/java/org/apache/airavata/application/mapper/SlurmApplicationDeploymentMapper.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.apache.airavata.application.mapper;
-
-import org.apache.airavata.application.dto.deployment.SlurmApplicationDeploymentRequestDto;
-import org.apache.airavata.application.dto.deployment.SlurmApplicationDeploymentResponseDto;
-import org.apache.airavata.application.model.deployment.SlurmApplicationDeploymentEntity;
-import org.mapstruct.Mapper;
-import org.mapstruct.Mapping;
-import org.mapstruct.MappingTarget;
-import org.mapstruct.ReportingPolicy;
-
-/**
- * Entity/DTO conversions for Slurm deployments. Both associations — the application
- * template and the Slurm cluster — are resolved by the service from their ids, so
- * neither is ever mapped straight off the request.
- */
-@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
-public interface SlurmApplicationDeploymentMapper {
-
-    @Mapping(target = "templateId", source = "applicationTemplate.templateId")
-    @Mapping(target = "slurmClusterId", source = "slurmCluster.clusterId")
-    SlurmApplicationDeploymentResponseDto toResponseDto(SlurmApplicationDeploymentEntity entity);
-
-    @Mapping(target = "deploymentId", ignore = true)
-    @Mapping(target = "applicationTemplate", ignore = true)
-    @Mapping(target = "slurmCluster", ignore = true)
-    SlurmApplicationDeploymentEntity toEntity(SlurmApplicationDeploymentRequestDto dto);
-
-    @Mapping(target = "deploymentId", ignore = true)
-    @Mapping(target = "applicationTemplate", ignore = true)
-    @Mapping(target = "slurmCluster", ignore = true)
-    void updateEntity(SlurmApplicationDeploymentRequestDto dto, @MappingTarget SlurmApplicationDeploymentEntity entity);
-}
diff --git a/src/main/java/org/apache/airavata/application/model/deployment/BatchApplicationDeploymentEntity.java b/src/main/java/org/apache/airavata/application/model/deployment/BatchApplicationDeploymentEntity.java
new file mode 100644
index 0000000000..7060283806
--- /dev/null
+++ b/src/main/java/org/apache/airavata/application/model/deployment/BatchApplicationDeploymentEntity.java
@@ -0,0 +1,128 @@
+package org.apache.airavata.application.model.deployment;
+
+import org.apache.airavata.application.model.template.ApplicationTemplateEntity;
+import org.apache.airavata.compute.model.SlurmClusterEntity;
+import org.apache.airavata.credentials.model.SSHUserCredential;
+
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.ForeignKey;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.Lob;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.OneToOne;
+
+@Entity
+public class BatchApplicationDeploymentEntity {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.UUID)
+    private String deploymentId;
+
+    @ManyToOne
+    @JoinColumn(name = "cluster_id", foreignKey = @ForeignKey(name = "fk_dep_slurm_cluster"))
+    private SlurmClusterEntity slurmCluster;
+
+    @ManyToOne
+    @JoinColumn(name = "template_id", foreignKey = @ForeignKey(name = "fk_dep_app_template"))
+    private ApplicationTemplateEntity applicationTemplate;
+
+    /*
+     * All execution commands go into this. This includes module loads, clean up
+     * commands, actual run command. Script can be parameterized using jinja
+     * template
+     */
+    @Lob
+    @Column(nullable = false)
+    private String slurmRunSection;
+
+    // Batch-scheduler resource request. Owned by this deployment: created, replaced and
+    // deleted with it, so cascades fully and has no life of its own.
+    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
+    @JoinColumn(
+            name = "batch_job_config_id",
+            nullable = false,
+            foreignKey = @ForeignKey(name = "fk_dep_batch_job_config"))
+    private BatchJobConfigs batchJobConfig;
+
+    @ManyToOne
+    @JoinColumn(
+            name = "default_submission_credential_id",
+            nullable = false,
+            foreignKey = @ForeignKey(name = "fk_dep_submission_credential"))
+    private SSHUserCredential defaultSubmissionCredential;
+
+    @Column(nullable = true)
+    private String workDir; // Additional subdir for execution will be created inside this
+    @Column(nullable = true)
+    private String partition;
+
+    // Getters and Setters
+    public String getDeploymentId() {
+        return deploymentId;
+    }
+
+    public void setDeploymentId(String deploymentId) {
+        this.deploymentId = deploymentId;
+    }
+
+    public SlurmClusterEntity getSlurmCluster() {
+        return slurmCluster;
+    }
+
+    public void setSlurmCluster(SlurmClusterEntity slurmCluster) {
+        this.slurmCluster = slurmCluster;
+    }
+
+    public ApplicationTemplateEntity getApplicationTemplate() {
+        return applicationTemplate;
+    }
+
+    public void setApplicationTemplate(ApplicationTemplateEntity applicationTemplate) {
+        this.applicationTemplate = applicationTemplate;
+    }
+
+    public String getSlurmRunSection() {
+        return slurmRunSection;
+    }
+
+    public void setSlurmRunSection(String slurmRunSection) {
+        this.slurmRunSection = slurmRunSection;
+    }
+
+    public BatchJobConfigs getBatchJobConfig() {
+        return batchJobConfig;
+    }
+
+    public void setBatchJobConfig(BatchJobConfigs batchJobConfig) {
+        this.batchJobConfig = batchJobConfig;
+    }
+
+    public SSHUserCredential getDefaultSubmissionCredential() {
+        return defaultSubmissionCredential;
+    }
+
+    public void setDefaultSubmissionCredential(SSHUserCredential defaultSubmissionCredential) {
+        this.defaultSubmissionCredential = defaultSubmissionCredential;
+    }
+
+    public String getWorkDir() {
+        return workDir;
+    }
+
+    public void setWorkDir(String workDir) {
+        this.workDir = workDir;
+    }
+
+    public String getPartition() {
+        return partition;
+    }
+
+    public void setPartition(String partition) {
+        this.partition = partition;
+    }
+}
diff --git a/src/main/java/org/apache/airavata/application/model/deployment/SlurmApplicationDeploymentEntity.java b/src/main/java/org/apache/airavata/application/model/deployment/BatchJobConfigs.java
similarity index 59%
rename from src/main/java/org/apache/airavata/application/model/deployment/SlurmApplicationDeploymentEntity.java
rename to src/main/java/org/apache/airavata/application/model/deployment/BatchJobConfigs.java
index 547e402341..a20bc9449f 100644
--- a/src/main/java/org/apache/airavata/application/model/deployment/SlurmApplicationDeploymentEntity.java
+++ b/src/main/java/org/apache/airavata/application/model/deployment/BatchJobConfigs.java
@@ -1,41 +1,22 @@
 package org.apache.airavata.application.model.deployment;
 
-import org.apache.airavata.application.model.template.ApplicationTemplateEntity;
-import org.apache.airavata.compute.model.SlurmClusterEntity;
-
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
-import jakarta.persistence.ForeignKey;
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.Lob;
-import jakarta.persistence.ManyToOne;
 
+/**
+ * Batch-scheduler resource request for a {@link BatchApplicationDeploymentEntity}. Owned
+ * by its deployment — created, replaced and deleted with it — rather than a shareable
+ * aggregate like {@code ApplicationTemplateEntity} or {@code SlurmClusterEntity}.
+ */
 @Entity
-public class SlurmApplicationDeploymentEntity {
+public class BatchJobConfigs {
 
     @Id
     @GeneratedValue(strategy = GenerationType.UUID)
-    private String deploymentId;
-
-    @ManyToOne
-    @JoinColumn(name = "cluster_id", foreignKey = @ForeignKey(name = "fk_dep_slurm_cluster"))
-    private SlurmClusterEntity slurmCluster;
-
-    @ManyToOne
-    @JoinColumn(name = "template_id", foreignKey = @ForeignKey(name = "fk_dep_app_template"))
-    private ApplicationTemplateEntity applicationTemplate;
-
-    /*
-     * All execution commands go into this. This includes module loads, clean up
-     * commands, actual run command. Script can be parameterized using jinja
-     * template
-     */
-    @Lob
-    @Column(nullable = false)
-    private String slurmRunSection;
+    private String batchJobConfigId;
 
     // Slurm resource allocation parameters
     @Column(nullable = true)
@@ -73,44 +54,13 @@ public class SlurmApplicationDeploymentEntity {
 
     @Column(nullable = false)
     private String allocation;
-    @Column(nullable = false)
-    private String user; // User who will submit the job
-    @Column(nullable = true)
-    private String workDir; // Additional subdir for execution will be created inside this
-    @Column(nullable = true)
-    private String partition;
-
-    // Getters and Setters
-    public String getDeploymentId() {
-        return deploymentId;
-    }
-
-    public void setDeploymentId(String deploymentId) {
-        this.deploymentId = deploymentId;
-    }
-
-    public SlurmClusterEntity getSlurmCluster() {
-        return slurmCluster;
-    }
-
-    public void setSlurmCluster(SlurmClusterEntity slurmCluster) {
-        this.slurmCluster = slurmCluster;
-    }
-
-    public ApplicationTemplateEntity getApplicationTemplate() {
-        return applicationTemplate;
-    }
 
-    public void setApplicationTemplate(ApplicationTemplateEntity applicationTemplate) {
-        this.applicationTemplate = applicationTemplate;
+    public String getBatchJobConfigId() {
+        return batchJobConfigId;
     }
 
-    public String getSlurmRunSection() {
-        return slurmRunSection;
-    }
-
-    public void setSlurmRunSection(String slurmRunSection) {
-        this.slurmRunSection = slurmRunSection;
+    public void setBatchJobConfigId(String batchJobConfigId) {
+        this.batchJobConfigId = batchJobConfigId;
     }
 
     public Integer getCpus() {
@@ -232,28 +182,4 @@ public class SlurmApplicationDeploymentEntity {
     public void setAllocation(String allocation) {
         this.allocation = allocation;
     }
-
-    public String getUser() {
-        return user;
-    }
-
-    public void setUser(String user) {
-        this.user = user;
-    }
-
-    public String getWorkDir() {
-        return workDir;
-    }
-
-    public void setWorkDir(String workDir) {
-        this.workDir = workDir;
-    }
-
-    public String getPartition() {
-        return partition;
-    }
-
-    public void setPartition(String partition) {
-        this.partition = partition;
-    }
 }
diff --git a/src/main/java/org/apache/airavata/application/model/template/ApplicationTemplateEntity.java b/src/main/java/org/apache/airavata/application/model/template/ApplicationTemplateEntity.java
index d068d42ea7..9cbc7fdf49 100644
--- a/src/main/java/org/apache/airavata/application/model/template/ApplicationTemplateEntity.java
+++ b/src/main/java/org/apache/airavata/application/model/template/ApplicationTemplateEntity.java
@@ -8,7 +8,7 @@ import jakarta.persistence.GenerationType;
 import jakarta.persistence.OneToMany;
 import java.util.ArrayList;
 import java.util.List;
-import org.apache.airavata.application.model.deployment.SlurmApplicationDeploymentEntity;
+import org.apache.airavata.application.model.deployment.BatchApplicationDeploymentEntity;
 
 @Entity
 public class ApplicationTemplateEntity {
@@ -29,7 +29,7 @@ public class ApplicationTemplateEntity {
     private List<ApplicationTemplateOutputEntity> outputs = new ArrayList<>();
 
     @OneToMany(mappedBy = "applicationTemplate")
-    private List<SlurmApplicationDeploymentEntity> deployments;
+    private List<BatchApplicationDeploymentEntity> deployments;
 
     public String getTemplateId() {
         return templateId;
@@ -71,11 +71,11 @@ public class ApplicationTemplateEntity {
         this.outputs = outputs;
     }
 
-    public List<SlurmApplicationDeploymentEntity> getDeployments() {
+    public List<BatchApplicationDeploymentEntity> getDeployments() {
         return deployments;
     }
 
-    public void setDeployments(List<SlurmApplicationDeploymentEntity> deployments) {
+    public void setDeployments(List<BatchApplicationDeploymentEntity> deployments) {
         this.deployments = deployments;
     }
 }
diff --git a/src/main/java/org/apache/airavata/application/repository/SlurmApplicationDeploymentRepository.java b/src/main/java/org/apache/airavata/application/repository/BatchApplicationDeploymentRepository.java
similarity index 59%
rename from src/main/java/org/apache/airavata/application/repository/SlurmApplicationDeploymentRepository.java
rename to src/main/java/org/apache/airavata/application/repository/BatchApplicationDeploymentRepository.java
index 29e2964cb1..9bf321c872 100644
--- a/src/main/java/org/apache/airavata/application/repository/SlurmApplicationDeploymentRepository.java
+++ b/src/main/java/org/apache/airavata/application/repository/BatchApplicationDeploymentRepository.java
@@ -1,15 +1,15 @@
 package org.apache.airavata.application.repository;
 
 import java.util.List;
-import org.apache.airavata.application.model.deployment.SlurmApplicationDeploymentEntity;
+import org.apache.airavata.application.model.deployment.BatchApplicationDeploymentEntity;
 import org.springframework.data.repository.ListCrudRepository;
 import org.springframework.stereotype.Repository;
 
 @Repository
-public interface SlurmApplicationDeploymentRepository
-        extends ListCrudRepository<SlurmApplicationDeploymentEntity, String> {
+public interface BatchApplicationDeploymentRepository
+        extends ListCrudRepository<BatchApplicationDeploymentEntity, String> {
 
-    List<SlurmApplicationDeploymentEntity> findByApplicationTemplate_TemplateId(String templateId);
+    List<BatchApplicationDeploymentEntity> findByApplicationTemplate_TemplateId(String templateId);
 
     boolean existsByApplicationTemplate_TemplateId(String templateId);
 }
diff --git a/src/main/java/org/apache/airavata/application/service/ApplicationTemplateService.java b/src/main/java/org/apache/airavata/application/service/ApplicationTemplateService.java
index d9d91c9c37..67f4da5755 100644
--- a/src/main/java/org/apache/airavata/application/service/ApplicationTemplateService.java
+++ b/src/main/java/org/apache/airavata/application/service/ApplicationTemplateService.java
@@ -10,7 +10,7 @@ import org.apache.airavata.application.model.template.ApplicationTemplateEntity;
 import org.apache.airavata.application.model.template.ApplicationTemplateInputEntity;
 import org.apache.airavata.application.model.template.ApplicationTemplateOutputEntity;
 import org.apache.airavata.application.repository.ApplicationTemplateRepository;
-import org.apache.airavata.application.repository.SlurmApplicationDeploymentRepository;
+import org.apache.airavata.application.repository.BatchApplicationDeploymentRepository;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
@@ -28,12 +28,12 @@ import org.springframework.web.server.ResponseStatusException;
 public class ApplicationTemplateService {
 
     private final ApplicationTemplateRepository templateRepository;
-    private final SlurmApplicationDeploymentRepository deploymentRepository;
+    private final BatchApplicationDeploymentRepository deploymentRepository;
     private final ApplicationTemplateMapper mapper;
 
     public ApplicationTemplateService(
             ApplicationTemplateRepository templateRepository,
-            SlurmApplicationDeploymentRepository deploymentRepository,
+            BatchApplicationDeploymentRepository deploymentRepository,
             ApplicationTemplateMapper mapper) {
         this.templateRepository = templateRepository;
         this.deploymentRepository = deploymentRepository;
diff --git a/src/main/java/org/apache/airavata/application/service/SlurmApplicationDeploymentService.java b/src/main/java/org/apache/airavata/application/service/BatchApplicationDeploymentService.java
similarity index 63%
rename from src/main/java/org/apache/airavata/application/service/SlurmApplicationDeploymentService.java
rename to src/main/java/org/apache/airavata/application/service/BatchApplicationDeploymentService.java
index 02a6ed70de..0767d46820 100644
--- a/src/main/java/org/apache/airavata/application/service/SlurmApplicationDeploymentService.java
+++ b/src/main/java/org/apache/airavata/application/service/BatchApplicationDeploymentService.java
@@ -1,75 +1,82 @@
 package org.apache.airavata.application.service;
 
 import java.util.List;
-import org.apache.airavata.application.dto.deployment.SlurmApplicationDeploymentRequestDto;
-import org.apache.airavata.application.dto.deployment.SlurmApplicationDeploymentResponseDto;
-import org.apache.airavata.application.mapper.SlurmApplicationDeploymentMapper;
-import org.apache.airavata.application.model.deployment.SlurmApplicationDeploymentEntity;
+import org.apache.airavata.application.dto.deployment.BatchApplicationDeploymentRequestDto;
+import org.apache.airavata.application.dto.deployment.BatchApplicationDeploymentResponseDto;
+import org.apache.airavata.application.mapper.BatchApplicationDeploymentMapper;
+import org.apache.airavata.application.model.deployment.BatchApplicationDeploymentEntity;
 import org.apache.airavata.application.model.template.ApplicationTemplateEntity;
 import org.apache.airavata.application.repository.ApplicationTemplateRepository;
-import org.apache.airavata.application.repository.SlurmApplicationDeploymentRepository;
+import org.apache.airavata.application.repository.BatchApplicationDeploymentRepository;
 import org.apache.airavata.compute.model.SlurmClusterEntity;
 import org.apache.airavata.compute.repository.SlurmClusterRepository;
+import org.apache.airavata.credentials.model.SSHUserCredential;
+import org.apache.airavata.credentials.repository.SSHUserCredentialRepository;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.server.ResponseStatusException;
 
-/** CRUD for Slurm deployments of an application template. */
+/** CRUD for batch application deployments of an application template. */
 @Service
-public class SlurmApplicationDeploymentService {
+public class BatchApplicationDeploymentService {
 
-    private final SlurmApplicationDeploymentRepository deploymentRepository;
+    private final BatchApplicationDeploymentRepository deploymentRepository;
     private final ApplicationTemplateRepository templateRepository;
     private final SlurmClusterRepository clusterRepository;
-    private final SlurmApplicationDeploymentMapper mapper;
+    private final SSHUserCredentialRepository credentialRepository;
+    private final BatchApplicationDeploymentMapper mapper;
 
-    public SlurmApplicationDeploymentService(
-            SlurmApplicationDeploymentRepository deploymentRepository,
+    public BatchApplicationDeploymentService(
+            BatchApplicationDeploymentRepository deploymentRepository,
             ApplicationTemplateRepository templateRepository,
             SlurmClusterRepository clusterRepository,
-            SlurmApplicationDeploymentMapper mapper) {
+            SSHUserCredentialRepository credentialRepository,
+            BatchApplicationDeploymentMapper mapper) {
         this.deploymentRepository = deploymentRepository;
         this.templateRepository = templateRepository;
         this.clusterRepository = clusterRepository;
+        this.credentialRepository = credentialRepository;
         this.mapper = mapper;
     }
 
     @Transactional(readOnly = true)
-    public List<SlurmApplicationDeploymentResponseDto> getAllDeployments() {
+    public List<BatchApplicationDeploymentResponseDto> getAllDeployments() {
         return deploymentRepository.findAll().stream().map(mapper::toResponseDto).toList();
     }
 
     @Transactional(readOnly = true)
-    public List<SlurmApplicationDeploymentResponseDto> getDeploymentsByTemplate(String templateId) {
+    public List<BatchApplicationDeploymentResponseDto> getDeploymentsByTemplate(String templateId) {
         return deploymentRepository.findByApplicationTemplate_TemplateId(templateId).stream()
                 .map(mapper::toResponseDto)
                 .toList();
     }
 
     @Transactional(readOnly = true)
-    public SlurmApplicationDeploymentResponseDto getDeployment(String deploymentId) {
+    public BatchApplicationDeploymentResponseDto getDeployment(String deploymentId) {
         return mapper.toResponseDto(findOrThrow(deploymentId));
     }
 
     @PreAuthorize("hasAnyAuthority('ADMIN', 'SUPER_ADMIN')")
     @Transactional
-    public SlurmApplicationDeploymentResponseDto createDeployment(SlurmApplicationDeploymentRequestDto request) {
-        SlurmApplicationDeploymentEntity entity = mapper.toEntity(request);
+    public BatchApplicationDeploymentResponseDto createDeployment(BatchApplicationDeploymentRequestDto request) {
+        BatchApplicationDeploymentEntity entity = mapper.toEntity(request);
         entity.setApplicationTemplate(resolveTemplate(request.getTemplateId()));
         entity.setSlurmCluster(resolveCluster(request.getSlurmClusterId()));
+        entity.setDefaultSubmissionCredential(resolveCredential(request.getDefaultSubmissionCredentialId()));
         return mapper.toResponseDto(deploymentRepository.save(entity));
     }
 
     @PreAuthorize("hasAnyAuthority('ADMIN', 'SUPER_ADMIN')")
     @Transactional
-    public SlurmApplicationDeploymentResponseDto updateDeployment(
-            String deploymentId, SlurmApplicationDeploymentRequestDto request) {
-        SlurmApplicationDeploymentEntity entity = findOrThrow(deploymentId);
+    public BatchApplicationDeploymentResponseDto updateDeployment(
+            String deploymentId, BatchApplicationDeploymentRequestDto request) {
+        BatchApplicationDeploymentEntity entity = findOrThrow(deploymentId);
         mapper.updateEntity(request, entity);
         entity.setApplicationTemplate(resolveTemplate(request.getTemplateId()));
         entity.setSlurmCluster(resolveCluster(request.getSlurmClusterId()));
+        entity.setDefaultSubmissionCredential(resolveCredential(request.getDefaultSubmissionCredentialId()));
         return mapper.toResponseDto(deploymentRepository.save(entity));
     }
 
@@ -79,7 +86,7 @@ public class SlurmApplicationDeploymentService {
         deploymentRepository.delete(findOrThrow(deploymentId));
     }
 
-    private SlurmApplicationDeploymentEntity findOrThrow(String deploymentId) {
+    private BatchApplicationDeploymentEntity findOrThrow(String deploymentId) {
         return deploymentRepository
                 .findById(deploymentId)
                 .orElseThrow(() ->
@@ -105,4 +112,11 @@ public class SlurmApplicationDeploymentService {
                 .findById(clusterId)
                 .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Cluster not found: " + clusterId));
     }
+
+    private SSHUserCredential resolveCredential(String credentialId) {
+        return credentialRepository
+                .findById(credentialId)
+                .orElseThrow(() -> new ResponseStatusException(
+                        HttpStatus.NOT_FOUND, "SSH credential not found: " + credentialId));
+    }
 }
diff --git a/src/main/java/org/apache/airavata/process/dto/BatchJobProcessRequestDto.java b/src/main/java/org/apache/airavata/process/dto/BatchJobProcessRequestDto.java
new file mode 100644
index 0000000000..c6cfb7e4be
--- /dev/null
+++ b/src/main/java/org/apache/airavata/process/dto/BatchJobProcessRequestDto.java
@@ -0,0 +1,35 @@
+package org.apache.airavata.process.dto;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * Create/update payload for a batch job process.
+ *
+ * <p>There is no {@code userId} field: ownership is derived from the caller's access
+ * token, never from client input, so a caller cannot submit a process on another user's
+ * behalf.
+ */
+public class BatchJobProcessRequestDto {
+
+    @NotBlank(message = "Deployment id cannot be blank")
+    private String deploymentId;
+
+    /** Optional; defaults to the deployment's default submission credential when omitted. */
+    private String sshCredentialId;
+
+    public String getDeploymentId() {
+        return deploymentId;
+    }
+
+    public void setDeploymentId(String deploymentId) {
+        this.deploymentId = deploymentId;
+    }
+
+    public String getSshCredentialId() {
+        return sshCredentialId;
+    }
+
+    public void setSshCredentialId(String sshCredentialId) {
+        this.sshCredentialId = sshCredentialId;
+    }
+}
diff --git a/src/main/java/org/apache/airavata/process/dto/BatchJobProcessResponseDto.java b/src/main/java/org/apache/airavata/process/dto/BatchJobProcessResponseDto.java
new file mode 100644
index 0000000000..a2ce520af5
--- /dev/null
+++ b/src/main/java/org/apache/airavata/process/dto/BatchJobProcessResponseDto.java
@@ -0,0 +1,57 @@
+package org.apache.airavata.process.dto;
+
+import org.apache.airavata.application.dto.deployment.BatchJobConfigResponseDto;
+
+/**
+ * Read view of a batch job process. The batch job config is inlined as a snapshot of
+ * what the process actually ran with, since the deployment's own config can change
+ * after the process is created.
+ */
+public class BatchJobProcessResponseDto {
+
+    private String processId;
+    private String deploymentId;
+    private String userId;
+    private String sshCredentialId;
+    private BatchJobConfigResponseDto batchJobConfig;
+
+    public String getProcessId() {
+        return processId;
+    }
+
+    public void setProcessId(String processId) {
+        this.processId = processId;
+    }
+
+    public String getDeploymentId() {
+        return deploymentId;
+    }
+
+    public void setDeploymentId(String deploymentId) {
+        this.deploymentId = deploymentId;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getSshCredentialId() {
+        return sshCredentialId;
+    }
+
+    public void setSshCredentialId(String sshCredentialId) {
+        this.sshCredentialId = sshCredentialId;
+    }
+
+    public BatchJobConfigResponseDto getBatchJobConfig() {
+        return batchJobConfig;
+    }
+
+    public void setBatchJobConfig(BatchJobConfigResponseDto batchJobConfig) {
+        this.batchJobConfig = batchJobConfig;
+    }
+}
diff --git a/src/main/java/org/apache/airavata/process/mapper/BatchJobProcessMapper.java b/src/main/java/org/apache/airavata/process/mapper/BatchJobProcessMapper.java
new file mode 100644
index 0000000000..906d063929
--- /dev/null
+++ b/src/main/java/org/apache/airavata/process/mapper/BatchJobProcessMapper.java
@@ -0,0 +1,30 @@
+package org.apache.airavata.process.mapper;
+
+import org.apache.airavata.application.mapper.BatchJobConfigMapper;
+import org.apache.airavata.process.dto.BatchJobProcessResponseDto;
+import org.apache.airavata.process.model.BatchJobProcess;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * Entity/DTO conversion for batch job processes.
+ *
+ * <p>There is no {@code toEntity}/{@code updateEntity} here: every field on
+ * {@link BatchJobProcess} is an association (deployment, user, credential, batch job
+ * config) and every one of them is resolved by {@code BatchJobProcessService} — from the
+ * request DTO's ids, from the deployment, or from the caller's access token — so there is
+ * nothing left for a mapper to write onto the entity.
+ */
+@Mapper(
+        componentModel = "spring",
+        uses = BatchJobConfigMapper.class,
+        unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface BatchJobProcessMapper {
+
+    @Mapping(target = "deploymentId", source = "batchApplicationDeployment.deploymentId")
+    @Mapping(target = "userId", source = "user.userId")
+    @Mapping(target = "sshCredentialId", source = "sshUserCredential.sshCredentialId")
+    @Mapping(target = "batchJobConfig", source = "batchJobConfigs")
+    BatchJobProcessResponseDto toResponseDto(BatchJobProcess entity);
+}
diff --git a/src/main/java/org/apache/airavata/process/model/BatchJobProcess.java b/src/main/java/org/apache/airavata/process/model/BatchJobProcess.java
new file mode 100644
index 0000000000..997c3f7fc3
--- /dev/null
+++ b/src/main/java/org/apache/airavata/process/model/BatchJobProcess.java
@@ -0,0 +1,78 @@
+package org.apache.airavata.process.model;
+
+import org.apache.airavata.iam.model.UserEntity;
+import org.apache.airavata.application.model.deployment.BatchJobConfigs;
+import org.apache.airavata.application.model.deployment.BatchApplicationDeploymentEntity;
+import org.apache.airavata.credentials.model.SSHUserCredential;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.ForeignKey;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+
+@Entity
+public class BatchJobProcess {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.UUID)
+    private String processId;
+
+    @ManyToOne
+    @JoinColumn(name = "deployment_id", foreignKey = @ForeignKey(name = "fk_process_deployment"))
+    private BatchApplicationDeploymentEntity batchApplicationDeployment;
+
+    @ManyToOne
+    @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "fk_process_user"))
+    private UserEntity user;
+
+    @ManyToOne
+    @JoinColumn(name = "ssh_credential_id", foreignKey = @ForeignKey(name = "fk_process_ssh_credential"))
+    private SSHUserCredential sshUserCredential;
+
+    @ManyToOne
+    @JoinColumn(name = "batch_job_config_id", foreignKey = @ForeignKey(name = "fk_process_batch_job_config"))
+    private BatchJobConfigs batchJobConfigs;
+
+    public String getProcessId() {
+        return processId;
+    }
+
+    public void setProcessId(String processId) {
+        this.processId = processId;
+    }
+
+    public BatchApplicationDeploymentEntity getBatchApplicationDeployment() {
+        return batchApplicationDeployment;
+    }
+
+    public void setBatchApplicationDeployment(BatchApplicationDeploymentEntity batchApplicationDeployment) {
+        this.batchApplicationDeployment = batchApplicationDeployment;
+    }
+
+    public UserEntity getUser() {
+        return user;
+    }
+
+    public void setUser(UserEntity user) {
+        this.user = user;
+    }
+
+    public SSHUserCredential getSshUserCredential() {
+        return sshUserCredential;
+    }
+
+    public void setSshUserCredential(SSHUserCredential sshUserCredential) {
+        this.sshUserCredential = sshUserCredential;
+    }
+
+    public BatchJobConfigs getBatchJobConfigs() {
+        return batchJobConfigs;
+    }
+
+    public void setBatchJobConfigs(BatchJobConfigs batchJobConfigs) {
+        this.batchJobConfigs = batchJobConfigs;
+    }
+}
diff --git a/src/main/java/org/apache/airavata/process/repository/BatchJobProcessRepository.java b/src/main/java/org/apache/airavata/process/repository/BatchJobProcessRepository.java
new file mode 100644
index 0000000000..f7b439436f
--- /dev/null
+++ b/src/main/java/org/apache/airavata/process/repository/BatchJobProcessRepository.java
@@ -0,0 +1,14 @@
+package org.apache.airavata.process.repository;
+
+import java.util.List;
+import org.apache.airavata.process.model.BatchJobProcess;
+import org.springframework.data.repository.ListCrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface BatchJobProcessRepository extends ListCrudRepository<BatchJobProcess, String> {
+
+    List<BatchJobProcess> findByBatchApplicationDeployment_DeploymentId(String deploymentId);
+
+    List<BatchJobProcess> findByUser_UserId(String userId);
+}
diff --git a/src/main/java/org/apache/airavata/process/service/BatchJobProcessService.java b/src/main/java/org/apache/airavata/process/service/BatchJobProcessService.java
new file mode 100644
index 0000000000..9dee5ca5b8
--- /dev/null
+++ b/src/main/java/org/apache/airavata/process/service/BatchJobProcessService.java
@@ -0,0 +1,149 @@
+package org.apache.airavata.process.service;
+
+import java.util.List;
+import org.apache.airavata.application.model.deployment.BatchApplicationDeploymentEntity;
+import org.apache.airavata.application.repository.BatchApplicationDeploymentRepository;
+import org.apache.airavata.credentials.model.SSHUserCredential;
+import org.apache.airavata.credentials.repository.SSHUserCredentialRepository;
+import org.apache.airavata.iam.model.UserEntity;
+import org.apache.airavata.iam.repository.UserRepository;
+import org.apache.airavata.process.dto.BatchJobProcessRequestDto;
+import org.apache.airavata.process.dto.BatchJobProcessResponseDto;
+import org.apache.airavata.process.mapper.BatchJobProcessMapper;
+import org.apache.airavata.process.model.BatchJobProcess;
+import org.apache.airavata.process.repository.BatchJobProcessRepository;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.server.ResponseStatusException;
+
+/**
+ * CRUD for batch job processes.
+ *
+ * <p>Ownership is never taken from client input: {@code createProcess} derives the owning
+ * user from the caller's access token, so any authenticated caller can submit a process
+ * for themselves but can never submit one on someone else's behalf.
+ */
+@Service
+public class BatchJobProcessService {
+
+    private final BatchJobProcessRepository processRepository;
+    private final BatchApplicationDeploymentRepository deploymentRepository;
+    private final SSHUserCredentialRepository credentialRepository;
+    private final UserRepository userRepository;
+    private final BatchJobProcessMapper mapper;
+
+    public BatchJobProcessService(
+            BatchJobProcessRepository processRepository,
+            BatchApplicationDeploymentRepository deploymentRepository,
+            SSHUserCredentialRepository credentialRepository,
+            UserRepository userRepository,
+            BatchJobProcessMapper mapper) {
+        this.processRepository = processRepository;
+        this.deploymentRepository = deploymentRepository;
+        this.credentialRepository = credentialRepository;
+        this.userRepository = userRepository;
+        this.mapper = mapper;
+    }
+
+    /** Lists every process across every user. */
+    @PreAuthorize("hasAnyAuthority('ADMIN', 'SUPER_ADMIN')")
+    @Transactional(readOnly = true)
+    public List<BatchJobProcessResponseDto> getAllProcesses() {
+        return processRepository.findAll().stream().map(mapper::toResponseDto).toList();
+    }
+
+    @Transactional(readOnly = true)
+    public List<BatchJobProcessResponseDto> getProcessesByDeployment(String deploymentId) {
+        return processRepository.findByBatchApplicationDeployment_DeploymentId(deploymentId).stream()
+                .map(mapper::toResponseDto)
+                .toList();
+    }
+
+    @Transactional(readOnly = true)
+    public BatchJobProcessResponseDto getProcess(String processId) {
+        return mapper.toResponseDto(findOrThrow(processId));
+    }
+
+    /**
+     * Any authenticated caller may create a process — this is a self-service submission,
+     * not an admin operation. The owning user is always the caller, taken from the
+     * access token; the batch job config is snapshotted from the deployment at creation
+     * time, since the deployment's own config can change afterward.
+     */
+    @PreAuthorize("isAuthenticated()")
+    @Transactional
+    public BatchJobProcessResponseDto createProcess(BatchJobProcessRequestDto request) {
+        BatchApplicationDeploymentEntity deployment = resolveDeployment(request.getDeploymentId());
+        BatchJobProcess entity = new BatchJobProcess();
+        entity.setBatchApplicationDeployment(deployment);
+        entity.setUser(resolveCurrentUser());
+        entity.setSshUserCredential(resolveCredential(request.getSshCredentialId(), deployment));
+        entity.setBatchJobConfigs(deployment.getBatchJobConfig());
+        return mapper.toResponseDto(processRepository.save(entity));
+    }
+
+    /**
+     * Administrative correction of a process's deployment/credential references. Ownership
+     * is deliberately immutable here — re-deriving it from the caller's token on update
+     * would reassign a process to whichever admin happens to issue the PUT.
+     */
+    @PreAuthorize("hasAnyAuthority('ADMIN', 'SUPER_ADMIN')")
+    @Transactional
+    public BatchJobProcessResponseDto updateProcess(String processId, BatchJobProcessRequestDto request) {
+        BatchJobProcess entity = findOrThrow(processId);
+        BatchApplicationDeploymentEntity deployment = resolveDeployment(request.getDeploymentId());
+        entity.setBatchApplicationDeployment(deployment);
+        entity.setSshUserCredential(resolveCredential(request.getSshCredentialId(), deployment));
+        entity.setBatchJobConfigs(deployment.getBatchJobConfig());
+        return mapper.toResponseDto(processRepository.save(entity));
+    }
+
+    @PreAuthorize("hasAnyAuthority('ADMIN', 'SUPER_ADMIN')")
+    @Transactional
+    public void deleteProcess(String processId) {
+        processRepository.delete(findOrThrow(processId));
+    }
+
+    private BatchJobProcess findOrThrow(String processId) {
+        return processRepository
+                .findById(processId)
+                .orElseThrow(
+                        () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Process not found: " + processId));
+    }
+
+    private BatchApplicationDeploymentEntity resolveDeployment(String deploymentId) {
+        return deploymentRepository
+                .findById(deploymentId)
+                .orElseThrow(() ->
+                        new ResponseStatusException(HttpStatus.NOT_FOUND, "Deployment not found: " + deploymentId));
+    }
+
+    private SSHUserCredential resolveCredential(String credentialId, BatchApplicationDeploymentEntity deployment) {
+        if (credentialId == null || credentialId.isBlank()) {
+            return deployment.getDefaultSubmissionCredential();
+        }
+        return credentialRepository
+                .findById(credentialId)
+                .orElseThrow(() ->
+                        new ResponseStatusException(HttpStatus.NOT_FOUND, "SSH credential not found: " + credentialId));
+    }
+
+    /**
+     * Resolves the caller's {@link UserEntity} from the principal name that
+     * {@code UserRoleOpaqueTokenIntrospector} put on the security context — the same
+     * identifier used as {@code UserEntity.userId} (see {@code UserService.getUserById}'s
+     * "isSelf" check for the other place this equivalence is relied on).
+     */
+    private UserEntity resolveCurrentUser() {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        String username = authentication.getName();
+        return userRepository
+                .findById(username)
+                .orElseThrow(() -> new ResponseStatusException(
+                        HttpStatus.NOT_FOUND, "No user record found for authenticated principal: " + username));
+    }
+}