This is an automated email from the ASF dual-hosted git repository. machristie pushed a commit to branch airavata-v2-refactoring in repository https://gitbox.apache.org/repos/asf/airavata.git
commit 2e7e51a1b85254d4d0b7e02f400487b4b711d404 Author: Marcus Christie <[email protected]> AuthorDate: Wed Jun 14 18:24:31 2023 -0400 Adding transactional service layer for ExecutionHandler --- .../apis/exception/EntityNotFoundException.java | 23 +++++++++ .../airavata/apis/handlers/ExecutionHandler.java | 43 +++++----------- .../airavata/apis/service/ExecutionService.java | 13 +++++ .../apis/service/impl/ExecutionServiceImpl.java | 57 ++++++++++++++++++++++ 4 files changed, 106 insertions(+), 30 deletions(-) diff --git a/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/exception/EntityNotFoundException.java b/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/exception/EntityNotFoundException.java new file mode 100644 index 0000000000..c4bc4bcc50 --- /dev/null +++ b/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/exception/EntityNotFoundException.java @@ -0,0 +1,23 @@ +package org.apache.airavata.apis.exception; + +public class EntityNotFoundException extends RuntimeException { + public EntityNotFoundException() { + } + + public EntityNotFoundException(String message) { + super(message); + } + + public EntityNotFoundException(Throwable cause) { + super(cause); + } + + public EntityNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public EntityNotFoundException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/handlers/ExecutionHandler.java b/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/handlers/ExecutionHandler.java index 634dc41f2b..e6a48aff1b 100644 --- a/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/handlers/ExecutionHandler.java +++ b/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/handlers/ExecutionHandler.java @@ -1,18 +1,15 @@ package org.apache.airavata.apis.handlers; +import io.grpc.Status; import io.grpc.stub.StreamObserver; import org.apache.airavata.api.execution.*; import org.apache.airavata.api.execution.stubs.Experiment; -import org.apache.airavata.apis.db.entity.ExperimentEntity; -import org.apache.airavata.apis.db.repository.ExperimentRepository; -import org.apache.airavata.apis.db.repository.RunConfigurationRepository; -import org.apache.airavata.apis.mapper.ExperimentMapper; +import org.apache.airavata.apis.exception.EntityNotFoundException; import org.apache.airavata.apis.scheduling.MetaScheduler; +import org.apache.airavata.apis.service.ExecutionService; import org.lognet.springboot.grpc.GRpcService; import org.springframework.beans.factory.annotation.Autowired; -import java.util.Optional; - @GRpcService public class ExecutionHandler extends ExecutionServiceGrpc.ExecutionServiceImplBase { @@ -20,25 +17,17 @@ public class ExecutionHandler extends ExecutionServiceGrpc.ExecutionServiceImplB private MetaScheduler metaScheduler; @Autowired - ExperimentRepository experimentRepository; - - @Autowired - RunConfigurationRepository runConfigurationRepository; + private ExecutionService executionService; - @Autowired - ExperimentMapper experimentMapper; - - // TODO: factor out database stuff into a transactional service layer @Override public void registerExperiment(ExperimentRegisterRequest request, StreamObserver<ExperimentRegisterResponse> responseObserver) { Experiment experiment = request.getExperiment(); - ExperimentEntity experimentEntity = experimentMapper.mapModelToEntity(experiment); - ExperimentEntity savedExperimentEntity = experimentRepository.save(experimentEntity); + Experiment savedExperiment = executionService.createExperiment(experiment); responseObserver.onNext(ExperimentRegisterResponse.newBuilder() - .setExperimentId(savedExperimentEntity.getExperimentId()).build()); + .setExperimentId(savedExperiment.getExperimentId()).build()); responseObserver.onCompleted(); } @@ -47,19 +36,13 @@ public class ExecutionHandler extends ExecutionServiceGrpc.ExecutionServiceImplB StreamObserver<ExperimentUpdateResponse> responseObserver) { Experiment experiment = request.getExperiment(); - Optional<ExperimentEntity> maybeExperimentEntity = experimentRepository.findById(experiment.getExperimentId()); - // TODO: handle experiment not found - maybeExperimentEntity.ifPresent(entity -> { - // First delete any existing run configs - if (entity.getRunConfigs() != null && !entity.getRunConfigs().isEmpty()) { - runConfigurationRepository.deleteAll(entity.getRunConfigs()); - entity.setRunConfigs(null); - } - experimentMapper.mapModelToEntity(experiment, entity); - experimentRepository.save(entity); - }); - responseObserver.onNext(ExperimentUpdateResponse.getDefaultInstance()); - responseObserver.onCompleted(); + try { + executionService.updateExperiment(experiment); + responseObserver.onNext(ExperimentUpdateResponse.getDefaultInstance()); + responseObserver.onCompleted(); + } catch (EntityNotFoundException e) { + responseObserver.onError(Status.NOT_FOUND.withDescription(e.getMessage()).asException()); + } } @Override diff --git a/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/service/ExecutionService.java b/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/service/ExecutionService.java new file mode 100644 index 0000000000..fe37625491 --- /dev/null +++ b/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/service/ExecutionService.java @@ -0,0 +1,13 @@ +package org.apache.airavata.apis.service; + +import org.apache.airavata.api.execution.stubs.Experiment; + +/** + * Transactional service layer for CRUD operations on database. + */ +public interface ExecutionService { + + Experiment createExperiment(Experiment experiment); + + Experiment updateExperiment(Experiment experiment); +} diff --git a/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/service/impl/ExecutionServiceImpl.java b/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/service/impl/ExecutionServiceImpl.java new file mode 100644 index 0000000000..ed94225800 --- /dev/null +++ b/modules/airavata-apis/airavata-apis-server/src/main/java/org/apache/airavata/apis/service/impl/ExecutionServiceImpl.java @@ -0,0 +1,57 @@ +package org.apache.airavata.apis.service.impl; + +import org.apache.airavata.api.execution.stubs.Experiment; +import org.apache.airavata.apis.db.entity.ExperimentEntity; +import org.apache.airavata.apis.db.repository.ExperimentRepository; +import org.apache.airavata.apis.db.repository.RunConfigurationRepository; +import org.apache.airavata.apis.exception.EntityNotFoundException; +import org.apache.airavata.apis.mapper.ExperimentMapper; +import org.apache.airavata.apis.service.ExecutionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; + +import java.util.Optional; + +// See https://github.com/LogNet/grpc-spring-boot-starter#9-grpc-response-observer--and-spring-transactional-caveats +@Service +@Transactional +public class ExecutionServiceImpl implements ExecutionService { + + @Autowired + ExperimentRepository experimentRepository; + + @Autowired + RunConfigurationRepository runConfigurationRepository; + + @Autowired + ExperimentMapper experimentMapper; + + @Override + public Experiment createExperiment(Experiment experiment) { + + ExperimentEntity experimentEntity = experimentMapper.mapModelToEntity(experiment); + ExperimentEntity savedExperimentEntity = experimentRepository.save(experimentEntity); + return experimentMapper.mapEntityToModel(savedExperimentEntity); + } + + @Override + public Experiment updateExperiment(Experiment experiment) { + Optional<ExperimentEntity> maybeExperimentEntity = experimentRepository.findById(experiment.getExperimentId()); + if (maybeExperimentEntity.isEmpty()) { + throw new EntityNotFoundException("No experiment exists with id " + experiment.getExperimentId()); + } + ExperimentEntity experimentEntity = maybeExperimentEntity.get(); + // First delete any existing run configs + if (experimentEntity.getRunConfigs() != null && !experimentEntity.getRunConfigs().isEmpty()) { + runConfigurationRepository.deleteAll(experimentEntity.getRunConfigs()); + experimentEntity.setRunConfigs(null); + } + experimentMapper.mapModelToEntity(experiment, experimentEntity); + experimentRepository.save(experimentEntity); + return experimentMapper.mapEntityToModel(experimentEntity); + + } + +}
