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));
+ }
+}