This is an automated email from the ASF dual-hosted git repository. yasith pushed a commit to branch service-layer-improvements in repository https://gitbox.apache.org/repos/asf/airavata.git
commit 352542b84b514f102b0a27d9b69579e9f8d83f37 Author: yasithdev <[email protected]> AuthorDate: Tue Dec 9 19:08:48 2025 -0600 reduce wrapper codes, merge duplicates and cleanup structure. --- .../org/apache/airavata/agents/api/AgentUtils.java | 46 ---- .../api/thrift/handler/AiravataServiceHandler.java | 6 +- .../thrift/handler/CredentialServiceHandler.java | 3 +- .../thrift/handler/GroupManagerServiceHandler.java | 11 +- .../api/thrift/handler/IamAdminServiceHandler.java | 6 +- .../api/thrift/handler/RegistryServiceHandler.java | 11 +- .../handler/SharingRegistryServerHandler.java | 13 +- .../handler/TenantProfileServiceHandler.java | 11 +- .../thrift/handler/UserProfileServiceHandler.java | 3 +- .../api/thrift/server/SharingRegistryServer.java | 9 +- .../common/exception/ErrorCodeGenerator.java | 68 ++++++ .../common/exception/ExceptionHandlerUtil.java | 149 +++++++++++++ .../airavata/common/logging/LoggingUtil.java | 247 +++++++++++++++++++++ .../apache/airavata/common/logging/MDCUtil.java | 47 ---- .../common/repositories/AbstractRepository.java | 173 +++++++++++++++ .../common/validation/ValidationResult.java | 148 ++++++++++++ .../common/validation/ValidationService.java | 141 ++++++++++++ .../helix/impl/task/aws/AWSCompletingTask.java | 4 + .../task/cancel/RemoteJobCancellationTask.java | 8 +- .../impl/task/cancel/WorkflowCancellationTask.java | 4 + .../helix/impl/task/parsing/DataParsingTask.java | 4 + .../task/submission/DefaultJobSubmissionTask.java | 4 + .../parser/AiravataCustomCommandOutputParser.java | 9 + .../messaging/client/RabbitMQListener.java | 9 - .../monitor/email/parser/PBSEmailParser.java | 3 +- .../monitor/kafka/JobStatusResultDeserializer.java | 8 + .../monitor/kafka/JobStatusResultSerializer.java | 8 + .../utils/TenantManagementKeycloakImpl.java | 3 +- .../airavata/registry/cpi/ReplicaCatalog.java | 28 --- .../registry/services/BaseErrorService.java | 119 ++++++++++ .../registry/services/ExperimentErrorService.java | 70 ++++-- .../registry/services/ExperimentService.java | 10 +- .../services/GwyResourceProfileService.java | 14 +- .../registry/services/ProcessErrorService.java | 70 ++++-- .../airavata/registry/services/ProcessService.java | 12 +- .../registry/services/TaskErrorService.java | 70 ++++-- .../services/UserResourceProfileService.java | 16 +- .../apache/airavata/service/AiravataService.java | 131 +++++------ .../airavata/service/OrchestratorService.java | 4 +- .../messaging/SharingServiceDBEventHandler.java | 3 +- .../sharing/repositories/AbstractRepository.java | 70 +++++- 41 files changed, 1448 insertions(+), 325 deletions(-) diff --git a/airavata-api/src/main/java/org/apache/airavata/agents/api/AgentUtils.java b/airavata-api/src/main/java/org/apache/airavata/agents/api/AgentUtils.java deleted file mode 100644 index c78831033c..0000000000 --- a/airavata-api/src/main/java/org/apache/airavata/agents/api/AgentUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -/** -* -* Licensed to the Apache Software Foundation (ASF) under one -* or more contributor license agreements. See the NOTICE file -* distributed with this work for additional information -* regarding copyright ownership. The ASF licenses this file -* to you under the Apache License, Version 2.0 (the -* "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.apache.airavata.agents.api; - -import org.apache.airavata.service.CredentialStoreService; -import org.apache.airavata.service.RegistryService; -import org.springframework.stereotype.Component; - -@Component -public class AgentUtils { - - private final RegistryService registryService; - private final CredentialStoreService credentialStoreService; - - public AgentUtils( - RegistryService registryService, - CredentialStoreService credentialStoreService) { - this.registryService = registryService; - this.credentialStoreService = credentialStoreService; - } - - public RegistryService getRegistryService() { - return registryService; - } - - public CredentialStoreService getCredentialService() { - return credentialStoreService; - } -} diff --git a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/AiravataServiceHandler.java b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/AiravataServiceHandler.java index 41aa5527e0..75255155be 100644 --- a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/AiravataServiceHandler.java +++ b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/AiravataServiceHandler.java @@ -23,8 +23,6 @@ import java.util.*; import org.apache.airavata.accountprovisioning.ConfigParam; import org.apache.airavata.accountprovisioning.SSHAccountProvisionerFactory; import org.apache.airavata.accountprovisioning.SSHAccountProvisionerProvider; -import org.apache.airavata.api.Airavata; -import org.apache.airavata.api.airavata_apiConstants; import org.apache.airavata.common.exception.AiravataException; import org.apache.airavata.common.exception.ApplicationSettingsException; import org.apache.airavata.common.utils.Constants; @@ -77,7 +75,7 @@ import org.apache.airavata.sharing.models.SharingRegistryException; import org.springframework.stereotype.Component; @Component -public class AiravataServiceHandler implements Airavata.Iface { +public class AiravataServiceHandler implements org.apache.airavata.api.Airavata.Iface { private final AiravataService airavataService; @@ -91,7 +89,7 @@ public class AiravataServiceHandler implements Airavata.Iface { */ @Override public String getAPIVersion() throws AiravataSystemException { - return airavata_apiConstants.AIRAVATA_API_VERSION; + return org.apache.airavata.api.airavata_apiConstants.AIRAVATA_API_VERSION; } /** diff --git a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/CredentialServiceHandler.java b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/CredentialServiceHandler.java index 168858cc3c..b222cd3afa 100644 --- a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/CredentialServiceHandler.java +++ b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/CredentialServiceHandler.java @@ -21,7 +21,6 @@ package org.apache.airavata.api.thrift.handler; import java.util.List; import java.util.Map; -import org.apache.airavata.credential.cpi.credential_store_cpiConstants; import org.apache.airavata.credential.exception.CredentialStoreException; import org.apache.airavata.model.credential.store.*; import org.apache.airavata.model.error.AiravataSystemException; @@ -42,7 +41,7 @@ public class CredentialServiceHandler implements org.apache.airavata.credential. @Override public String getAPIVersion() throws AiravataSystemException { - return credential_store_cpiConstants.CS_CPI_VERSION; + return org.apache.airavata.credential.cpi.credential_store_cpiConstants.CS_CPI_VERSION; } private CredentialStoreException wrapException( diff --git a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/GroupManagerServiceHandler.java b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/GroupManagerServiceHandler.java index 884b35eb16..82b45aae6e 100644 --- a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/GroupManagerServiceHandler.java +++ b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/GroupManagerServiceHandler.java @@ -24,29 +24,28 @@ import org.apache.airavata.model.error.AiravataSystemException; import org.apache.airavata.model.error.AuthorizationException; import org.apache.airavata.model.group.GroupModel; import org.apache.airavata.model.security.AuthzToken; -import org.apache.airavata.profile.groupmanager.cpi.GroupManagerService; import org.apache.airavata.profile.groupmanager.cpi.exception.GroupManagerServiceException; -import org.apache.airavata.profile.groupmanager.cpi.group_manager_cpiConstants; import org.apache.airavata.security.interceptor.SecurityCheck; +import org.apache.airavata.service.GroupManagerService; import org.apache.airavata.sharing.models.SharingRegistryException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component -public class GroupManagerServiceHandler implements GroupManagerService.Iface { +public class GroupManagerServiceHandler implements org.apache.airavata.profile.groupmanager.cpi.GroupManagerService.Iface { private static final Logger logger = LoggerFactory.getLogger(GroupManagerServiceHandler.class); - private final org.apache.airavata.service.GroupManagerService groupManagerService; + private final GroupManagerService groupManagerService; - public GroupManagerServiceHandler(org.apache.airavata.service.GroupManagerService groupManagerService) { + public GroupManagerServiceHandler(GroupManagerService groupManagerService) { this.groupManagerService = groupManagerService; logger.info("GroupManagerServiceHandler initialized with Spring-injected GroupManagerService"); } @Override public String getAPIVersion() throws AiravataSystemException { - return group_manager_cpiConstants.GROUP_MANAGER_CPI_VERSION; + return org.apache.airavata.profile.groupmanager.cpi.group_manager_cpiConstants.GROUP_MANAGER_CPI_VERSION; } @Override diff --git a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/IamAdminServiceHandler.java b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/IamAdminServiceHandler.java index 57f8066453..5fdb186750 100644 --- a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/IamAdminServiceHandler.java +++ b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/IamAdminServiceHandler.java @@ -26,9 +26,7 @@ import org.apache.airavata.model.error.AuthorizationException; import org.apache.airavata.model.security.AuthzToken; import org.apache.airavata.model.user.UserProfile; import org.apache.airavata.model.workspace.Gateway; -import org.apache.airavata.profile.iam.admin.services.cpi.IamAdminServices; import org.apache.airavata.profile.iam.admin.services.cpi.exception.IamAdminServicesException; -import org.apache.airavata.profile.iam.admin.services.cpi.iam_admin_services_cpiConstants; import org.apache.airavata.registry.api.exception.RegistryServiceException; import org.apache.airavata.security.interceptor.SecurityCheck; import org.apache.airavata.service.IamAdminService; @@ -37,7 +35,7 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component -public class IamAdminServiceHandler implements IamAdminServices.Iface { +public class IamAdminServiceHandler implements org.apache.airavata.profile.iam.admin.services.cpi.IamAdminServices.Iface { private static final Logger logger = LoggerFactory.getLogger(IamAdminServiceHandler.class); private final IamAdminService iamAdminService; @@ -49,7 +47,7 @@ public class IamAdminServiceHandler implements IamAdminServices.Iface { @Override public String getAPIVersion() throws AiravataSystemException { - return iam_admin_services_cpiConstants.IAM_ADMIN_SERVICES_CPI_VERSION; + return org.apache.airavata.profile.iam.admin.services.cpi.iam_admin_services_cpiConstants.IAM_ADMIN_SERVICES_CPI_VERSION; } @Override diff --git a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/RegistryServiceHandler.java b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/RegistryServiceHandler.java index e5acc00b17..9b35f38d80 100644 --- a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/RegistryServiceHandler.java +++ b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/RegistryServiceHandler.java @@ -61,20 +61,19 @@ import org.apache.airavata.model.workspace.Gateway; import org.apache.airavata.model.workspace.GatewayUsageReportingCommand; import org.apache.airavata.model.workspace.Notification; import org.apache.airavata.model.workspace.Project; -import org.apache.airavata.registry.api.RegistryService; import org.apache.airavata.registry.api.exception.RegistryServiceException; -import org.apache.airavata.registry.api.registry_apiConstants; +import org.apache.airavata.service.RegistryService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component -public class RegistryServiceHandler implements RegistryService.Iface { +public class RegistryServiceHandler implements org.apache.airavata.registry.api.RegistryService.Iface { private static final Logger logger = LoggerFactory.getLogger(RegistryServiceHandler.class); - private final org.apache.airavata.service.RegistryService registryService; + private final RegistryService registryService; - public RegistryServiceHandler(org.apache.airavata.service.RegistryService registryService) { + public RegistryServiceHandler(RegistryService registryService) { this.registryService = registryService; } @@ -91,7 +90,7 @@ public class RegistryServiceHandler implements RegistryService.Iface { */ @Override public String getAPIVersion() throws AiravataSystemException { - return registry_apiConstants.REGISTRY_API_VERSION; + return org.apache.airavata.registry.api.registry_apiConstants.REGISTRY_API_VERSION; } /** diff --git a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/SharingRegistryServerHandler.java b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/SharingRegistryServerHandler.java index f81c10b593..781b9d7637 100644 --- a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/SharingRegistryServerHandler.java +++ b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/SharingRegistryServerHandler.java @@ -21,24 +21,23 @@ package org.apache.airavata.api.thrift.handler; import java.util.List; import org.apache.airavata.sharing.models.*; -import org.apache.airavata.sharing.service.cpi.SharingRegistryService; -import org.apache.airavata.sharing.service.cpi.sharing_cpiConstants; import org.springframework.stereotype.Component; +import org.apache.airavata.service.SharingRegistryService; @Component -public class SharingRegistryServerHandler implements SharingRegistryService.Iface { - private final org.apache.airavata.service.SharingRegistryService sharingRegistryService; +public class SharingRegistryServerHandler implements org.apache.airavata.sharing.service.cpi.SharingRegistryService.Iface { + private final SharingRegistryService sharingRegistryService; - public SharingRegistryServerHandler(org.apache.airavata.service.SharingRegistryService sharingRegistryService) { + public SharingRegistryServerHandler(SharingRegistryService sharingRegistryService) { this.sharingRegistryService = sharingRegistryService; } public static String OWNER_PERMISSION_NAME = - org.apache.airavata.service.SharingRegistryService.OWNER_PERMISSION_NAME; + SharingRegistryService.OWNER_PERMISSION_NAME; @Override public String getAPIVersion() { - return sharing_cpiConstants.SHARING_CPI_VERSION; + return org.apache.airavata.sharing.service.cpi.sharing_cpiConstants.SHARING_CPI_VERSION; } /** diff --git a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/TenantProfileServiceHandler.java b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/TenantProfileServiceHandler.java index 59a41e8cd2..dc87104bbc 100644 --- a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/TenantProfileServiceHandler.java +++ b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/TenantProfileServiceHandler.java @@ -25,10 +25,9 @@ import org.apache.airavata.model.error.AiravataSystemException; import org.apache.airavata.model.error.AuthorizationException; import org.apache.airavata.model.security.AuthzToken; import org.apache.airavata.model.workspace.Gateway; -import org.apache.airavata.profile.tenant.cpi.TenantProfileService; import org.apache.airavata.profile.tenant.cpi.exception.TenantProfileServiceException; -import org.apache.airavata.profile.tenant.cpi.profile_tenant_cpiConstants; import org.apache.airavata.security.interceptor.SecurityCheck; +import org.apache.airavata.service.TenantProfileService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @@ -37,20 +36,20 @@ import org.springframework.stereotype.Component; * Created by goshenoy on 3/6/17. */ @Component -public class TenantProfileServiceHandler implements TenantProfileService.Iface { +public class TenantProfileServiceHandler implements org.apache.airavata.profile.tenant.cpi.TenantProfileService.Iface { private static final Logger logger = LoggerFactory.getLogger(TenantProfileServiceHandler.class); - private final org.apache.airavata.service.TenantProfileService tenantProfileService; + private final TenantProfileService tenantProfileService; - public TenantProfileServiceHandler(org.apache.airavata.service.TenantProfileService tenantProfileService) { + public TenantProfileServiceHandler(TenantProfileService tenantProfileService) { this.tenantProfileService = tenantProfileService; logger.debug("Initializing TenantProfileServiceHandler"); } @Override public String getAPIVersion() throws AiravataSystemException { - return profile_tenant_cpiConstants.TENANT_PROFILE_CPI_VERSION; + return org.apache.airavata.profile.tenant.cpi.profile_tenant_cpiConstants.TENANT_PROFILE_CPI_VERSION; } @Override diff --git a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/UserProfileServiceHandler.java b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/UserProfileServiceHandler.java index fbb72566ab..6e7ac93284 100644 --- a/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/UserProfileServiceHandler.java +++ b/airavata-api/src/main/java/org/apache/airavata/api/thrift/handler/UserProfileServiceHandler.java @@ -26,7 +26,6 @@ import org.apache.airavata.model.security.AuthzToken; import org.apache.airavata.model.user.UserProfile; import org.apache.airavata.profile.iam.admin.services.cpi.exception.IamAdminServicesException; import org.apache.airavata.profile.user.cpi.exception.UserProfileServiceException; -import org.apache.airavata.profile.user.cpi.profile_user_cpiConstants; import org.apache.airavata.security.interceptor.SecurityCheck; import org.springframework.stereotype.Component; @@ -41,7 +40,7 @@ public class UserProfileServiceHandler implements org.apache.airavata.profile.us @Override public String getAPIVersion() throws AiravataSystemException { - return profile_user_cpiConstants.USER_PROFILE_CPI_VERSION; + return org.apache.airavata.profile.user.cpi.profile_user_cpiConstants.USER_PROFILE_CPI_VERSION; } @Override diff --git a/airavata-api/src/main/java/org/apache/airavata/api/thrift/server/SharingRegistryServer.java b/airavata-api/src/main/java/org/apache/airavata/api/thrift/server/SharingRegistryServer.java index 34099b2697..c6116a93e5 100644 --- a/airavata-api/src/main/java/org/apache/airavata/api/thrift/server/SharingRegistryServer.java +++ b/airavata-api/src/main/java/org/apache/airavata/api/thrift/server/SharingRegistryServer.java @@ -27,8 +27,6 @@ import org.apache.airavata.config.AiravataServerProperties; import org.apache.airavata.sharing.messaging.SharingServiceDBEventMessagingFactory; import org.apache.airavata.sharing.models.SharingRegistryException; import org.apache.airavata.sharing.service.cpi.SharingRegistryService; -// Unused import - commented out -// import org.apache.airavata.sharing.utils.SharingRegistryDBInitConfig; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.transport.TSSLTransportFactory; @@ -82,8 +80,7 @@ public class SharingRegistryServer implements IServer { final int serverPort = properties.services.sharing.serverPort; // SharingRegistryServerHandler doesn't need DBInitConfig anymore - it's a Spring bean SharingRegistryServerHandler handler = applicationContext.getBean(SharingRegistryServerHandler.class); - SharingRegistryService.Processor<SharingRegistryServerHandler> processor = - new SharingRegistryService.Processor<>(handler); + var processor = new SharingRegistryService.Processor<>(handler); TServerTransport serverTransport; TThreadPoolServer.Args options; @@ -159,6 +156,10 @@ public class SharingRegistryServer implements IServer { start(); } + /** + * Configure the server. Required by IServer interface. + * No configuration needed for SharingRegistryServer. + */ @Override public void configure() throws Exception {} diff --git a/airavata-api/src/main/java/org/apache/airavata/common/exception/ErrorCodeGenerator.java b/airavata-api/src/main/java/org/apache/airavata/common/exception/ErrorCodeGenerator.java new file mode 100644 index 0000000000..f0aff0af65 --- /dev/null +++ b/airavata-api/src/main/java/org/apache/airavata/common/exception/ErrorCodeGenerator.java @@ -0,0 +1,68 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.common.exception; + +import java.util.UUID; + +/** + * Utility class for generating standardized error codes. + * + * <p>Error codes are used for tracking and correlating errors across the system. + * They provide a unique identifier for each error occurrence, making it easier + * to trace errors in logs and support requests. + */ +public class ErrorCodeGenerator { + + private ErrorCodeGenerator() { + // Utility class - prevent instantiation + } + + /** + * Generate a unique error code. + * Uses UUID to ensure uniqueness across the system. + * + * @return A unique error code string + */ + public static String generateErrorCode() { + return UUID.randomUUID().toString(); + } + + /** + * Generate an error code with a prefix for categorization. + * + * @param prefix The prefix to categorize the error (e.g., "EXP", "PROC", "TASK") + * @return A unique error code with prefix + */ + public static String generateErrorCode(String prefix) { + return String.format("%s-%s", prefix, UUID.randomUUID().toString()); + } + + /** + * Generate an error code with prefix and timestamp. + * + * @param prefix The prefix to categorize the error + * @return A unique error code with prefix and timestamp + */ + public static String generateErrorCodeWithTimestamp(String prefix) { + long timestamp = System.currentTimeMillis(); + return String.format("%s-%d-%s", prefix, timestamp, UUID.randomUUID().toString().substring(0, 8)); + } +} + diff --git a/airavata-api/src/main/java/org/apache/airavata/common/exception/ExceptionHandlerUtil.java b/airavata-api/src/main/java/org/apache/airavata/common/exception/ExceptionHandlerUtil.java new file mode 100644 index 0000000000..4e53d90146 --- /dev/null +++ b/airavata-api/src/main/java/org/apache/airavata/common/exception/ExceptionHandlerUtil.java @@ -0,0 +1,149 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.common.exception; + +import org.apache.airavata.common.logging.LoggingUtil; +import org.apache.airavata.model.error.AiravataErrorType; +import org.apache.airavata.model.error.AiravataSystemException; +import org.slf4j.Logger; + +/** + * Utility class for standardized exception handling patterns. + * + * <p>This class provides helper methods to ensure consistent exception handling: + * <ul> + * <li>Convert exceptions to AiravataSystemException with proper error types</li> + * <li>Log exceptions with context using MDC</li> + * <li>Wrap exceptions with appropriate error codes</li> + * <li>Provide standardized error message formatting</li> + * </ul> + */ +public class ExceptionHandlerUtil { + + private ExceptionHandlerUtil() { + // Utility class - prevent instantiation + } + + /** + * Handle an exception by logging it and converting to AiravataSystemException. + * + * @param logger The logger instance + * @param operation The operation being performed + * @param errorType The Airavata error type + * @param exception The exception that occurred + * @return AiravataSystemException with the error details + */ + public static AiravataSystemException handleException( + Logger logger, String operation, AiravataErrorType errorType, Throwable exception) { + String message = String.format("[%s] failed: %s", operation, exception.getMessage()); + LoggingUtil.logError(logger, operation, exception.getMessage(), exception); + + AiravataSystemException airavataException = new AiravataSystemException(errorType); + airavataException.setMessage(message); + airavataException.initCause(exception); + return airavataException; + } + + /** + * Handle an exception with context information. + * + * @param logger The logger instance + * @param operation The operation being performed + * @param errorType The Airavata error type + * @param exception The exception that occurred + * @param context Context parameters (key-value pairs) + * @return AiravataSystemException with the error details + */ + public static AiravataSystemException handleExceptionWithContext( + Logger logger, String operation, AiravataErrorType errorType, + Throwable exception, java.util.Map<String, String> context) { + LoggingUtil.logErrorWithContext(logger, operation, exception.getMessage(), exception, context); + + StringBuilder message = new StringBuilder(); + message.append(String.format("[%s] failed: %s", operation, exception.getMessage())); + if (context != null && !context.isEmpty()) { + message.append(" | Context: "); + context.forEach((key, value) -> message.append(String.format("%s=%s, ", key, value))); + message.setLength(message.length() - 2); + } + + AiravataSystemException airavataException = new AiravataSystemException(errorType); + airavataException.setMessage(message.toString()); + airavataException.initCause(exception); + return airavataException; + } + + /** + * Handle an exception with MDC context (experiment, gateway, process IDs). + * + * @param logger The logger instance + * @param operation The operation being performed + * @param errorType The Airavata error type + * @param exception The exception that occurred + * @param experimentId The experiment ID (optional) + * @param gatewayId The gateway ID (optional) + * @param processId The process ID (optional) + * @return AiravataSystemException with the error details + */ + public static AiravataSystemException handleExceptionWithMDC( + Logger logger, String operation, AiravataErrorType errorType, + Throwable exception, String experimentId, String gatewayId, String processId) { + LoggingUtil.setExperimentContext(experimentId, gatewayId, processId); + try { + return handleException(logger, operation, errorType, exception); + } finally { + LoggingUtil.clearContext(); + } + } + + /** + * Wrap a generic exception as AiravataSystemException with a specific error type. + * + * @param errorType The Airavata error type + * @param message The error message + * @param cause The underlying exception + * @return AiravataSystemException + */ + public static AiravataSystemException wrapAsAiravataException( + AiravataErrorType errorType, String message, Throwable cause) { + AiravataSystemException exception = new AiravataSystemException(errorType); + exception.setMessage(message); + if (cause != null) { + exception.initCause(cause); + } + return exception; + } + + /** + * Create an AiravataSystemException with an error code. + * + * @param errorType The Airavata error type + * @param message The error message + * @param errorCode The error code (generated by ErrorCodeGenerator) + * @param cause The underlying exception + * @return AiravataSystemException + */ + public static AiravataSystemException createExceptionWithErrorCode( + AiravataErrorType errorType, String message, String errorCode, Throwable cause) { + String messageWithCode = String.format("Error Code: %s, %s", errorCode, message); + return wrapAsAiravataException(errorType, messageWithCode, cause); + } +} + diff --git a/airavata-api/src/main/java/org/apache/airavata/common/logging/LoggingUtil.java b/airavata-api/src/main/java/org/apache/airavata/common/logging/LoggingUtil.java new file mode 100644 index 0000000000..dfe2e1f631 --- /dev/null +++ b/airavata-api/src/main/java/org/apache/airavata/common/logging/LoggingUtil.java @@ -0,0 +1,247 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.common.logging; + +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.MDC; + +/** + * Utility class for standardized logging patterns across the Airavata API. + * + * <p>This class provides helper methods to ensure consistent logging: + * <ul> + * <li>Always include exceptions in error logs</li> + * <li>Use structured logging with MDC for correlation IDs</li> + * <li>Standardize log message format: [Operation] failed: [reason]</li> + * <li>Provide context-aware logging methods</li> + * </ul> + * + * <p>Usage example: + * <pre> + * LoggingUtil.logError(logger, "Experiment launch", "Failed to validate experiment", exception); + * LoggingUtil.logErrorWithContext(logger, "Process execution", "Task failed", exception, + * Map.of("processId", processId, "taskId", taskId)); + * </pre> + */ +public class LoggingUtil { + + private LoggingUtil() { + // Utility class - prevent instantiation + } + + /** + * Log an error with standardized format: [Operation] failed: [reason] + * Always includes the exception in the log. + * + * @param logger The logger instance + * @param operation The operation being performed (e.g., "Experiment launch", "Process execution") + * @param reason The reason for the error + * @param exception The exception that occurred (may be null) + */ + public static void logError(Logger logger, String operation, String reason, Throwable exception) { + String message = String.format("[%s] failed: %s", operation, reason); + if (exception != null) { + logger.error(message, exception); + } else { + logger.error(message); + } + } + + /** + * Log an error with context information from MDC. + * The MDC context is automatically included in the log output. + * + * @param logger The logger instance + * @param operation The operation being performed + * @param reason The reason for the error + * @param exception The exception that occurred (may be null) + */ + public static void logErrorWithMDC(Logger logger, String operation, String reason, Throwable exception) { + logError(logger, operation, reason, exception); + } + + /** + * Log an error with additional context parameters. + * Context parameters are included in the log message for better traceability. + * + * @param logger The logger instance + * @param operation The operation being performed + * @param reason The reason for the error + * @param exception The exception that occurred (may be null) + * @param context Context parameters to include in the log message (key-value pairs) + */ + public static void logErrorWithContext(Logger logger, String operation, String reason, + Throwable exception, java.util.Map<String, String> context) { + StringBuilder message = new StringBuilder(); + message.append(String.format("[%s] failed: %s", operation, reason)); + + if (context != null && !context.isEmpty()) { + message.append(" | Context: "); + context.forEach((key, value) -> + message.append(String.format("%s=%s, ", key, value))); + // Remove trailing comma and space + message.setLength(message.length() - 2); + } + + if (exception != null) { + logger.error(message.toString(), exception); + } else { + logger.error(message.toString()); + } + } + + /** + * Log a warning with standardized format: [Operation] warning: [message] + * + * @param logger The logger instance + * @param operation The operation being performed + * @param message The warning message + */ + public static void logWarning(Logger logger, String operation, String message) { + logger.warn(String.format("[%s] warning: %s", operation, message)); + } + + /** + * Log a warning with context information. + * + * @param logger The logger instance + * @param operation The operation being performed + * @param message The warning message + * @param context Context parameters to include in the log message + */ + public static void logWarningWithContext(Logger logger, String operation, String message, + java.util.Map<String, String> context) { + StringBuilder logMessage = new StringBuilder(); + logMessage.append(String.format("[%s] warning: %s", operation, message)); + + if (context != null && !context.isEmpty()) { + logMessage.append(" | Context: "); + context.forEach((key, value) -> + logMessage.append(String.format("%s=%s, ", key, value))); + logMessage.setLength(logMessage.length() - 2); + } + + logger.warn(logMessage.toString()); + } + + /** + * Log an info message with standardized format: [Operation]: [message] + * + * @param logger The logger instance + * @param operation The operation being performed + * @param message The info message + */ + public static void logInfo(Logger logger, String operation, String message) { + logger.info(String.format("[%s]: %s", operation, message)); + } + + /** + * Log an info message with context information. + * + * @param logger The logger instance + * @param operation The operation being performed + * @param message The info message + * @param context Context parameters to include in the log message + */ + public static void logInfoWithContext(Logger logger, String operation, String message, + java.util.Map<String, String> context) { + StringBuilder logMessage = new StringBuilder(); + logMessage.append(String.format("[%s]: %s", operation, message)); + + if (context != null && !context.isEmpty()) { + logMessage.append(" | Context: "); + context.forEach((key, value) -> + logMessage.append(String.format("%s=%s, ", key, value))); + logMessage.setLength(logMessage.length() - 2); + } + + logger.info(logMessage.toString()); + } + + /** + * Log a debug message with standardized format: [Operation]: [message] + * + * @param logger The logger instance + * @param operation The operation being performed + * @param message The debug message + */ + public static void logDebug(Logger logger, String operation, String message) { + logger.debug(String.format("[%s]: %s", operation, message)); + } + + /** + * Set MDC context for experiment-related operations. + * This helps with log correlation and tracing. + * + * @param experimentId The experiment ID + * @param gatewayId The gateway ID (optional, may be null) + * @param processId The process ID (optional, may be null) + */ + public static void setExperimentContext(String experimentId, String gatewayId, String processId) { + if (experimentId != null) { + MDC.put(MDCConstants.EXPERIMENT_ID, experimentId); + } + if (gatewayId != null) { + MDC.put(MDCConstants.GATEWAY_ID, gatewayId); + } + if (processId != null) { + MDC.put(MDCConstants.PROCESS_ID, processId); + } + } + + /** + * Clear MDC context. + * Should be called at the end of request processing or in finally blocks. + */ + public static void clearContext() { + MDC.clear(); + } + + /** + * Execute a runnable with MDC context preserved. + * Useful for async operations where MDC context might be lost. + * + * @param runnable The runnable to execute + * @return A wrapped runnable that preserves MDC context + */ + public static Runnable withMDC(Runnable runnable) { + Map<String, String> mdc = MDC.getCopyOfContextMap(); + return () -> { + Map<String, String> oldMdc = MDC.getCopyOfContextMap(); + + if (mdc == null) { + MDC.clear(); + } else { + MDC.setContextMap(mdc); + } + try { + runnable.run(); + } finally { + if (oldMdc == null) { + MDC.clear(); + } else { + MDC.setContextMap(oldMdc); + } + } + }; + } +} + diff --git a/airavata-api/src/main/java/org/apache/airavata/common/logging/MDCUtil.java b/airavata-api/src/main/java/org/apache/airavata/common/logging/MDCUtil.java deleted file mode 100644 index aa43edd06c..0000000000 --- a/airavata-api/src/main/java/org/apache/airavata/common/logging/MDCUtil.java +++ /dev/null @@ -1,47 +0,0 @@ -/** -* -* Licensed to the Apache Software Foundation (ASF) under one -* or more contributor license agreements. See the NOTICE file -* distributed with this work for additional information -* regarding copyright ownership. The ASF licenses this file -* to you under the Apache License, Version 2.0 (the -* "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.apache.airavata.common.logging; - -import java.util.Map; -import org.slf4j.MDC; - -public class MDCUtil { - public static Runnable wrapWithMDC(Runnable r) { - Map<String, String> mdc = MDC.getCopyOfContextMap(); - return () -> { - Map<String, String> oldMdc = MDC.getCopyOfContextMap(); - - if (mdc == null) { - MDC.clear(); - } else { - MDC.setContextMap(mdc); - } - try { - r.run(); - } finally { - if (oldMdc == null) { - MDC.clear(); - } else { - MDC.setContextMap(oldMdc); - } - } - }; - } -} diff --git a/airavata-api/src/main/java/org/apache/airavata/common/repositories/AbstractRepository.java b/airavata-api/src/main/java/org/apache/airavata/common/repositories/AbstractRepository.java new file mode 100644 index 0000000000..a4b92f4b97 --- /dev/null +++ b/airavata-api/src/main/java/org/apache/airavata/common/repositories/AbstractRepository.java @@ -0,0 +1,173 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.common.repositories; + +import com.github.dozermapper.core.Mapper; +import jakarta.persistence.EntityManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; + +/** + * Common base class for repository implementations across Airavata modules. + * + * <p>This class provides core CRUD operations that are common across all repository + * implementations. Module-specific repositories should extend this class and add + * module-specific query methods and exception handling. + * + * <p>The class uses a generic type system: + * <ul> + * <li>T - The Thrift model type (e.g., ExperimentModel, ProcessModel)</li> + * <li>E - The JPA entity type (e.g., ExperimentEntity, ProcessEntity)</li> + * <li>Id - The primary key type (e.g., String, ProcessPK)</li> + * </ul> + * + * <p>Subclasses must implement: + * <ul> + * <li>{@link #getMapper()} - Returns the Dozer mapper instance</li> + * <li>{@link #getEntityManager()} - Returns the EntityManager for this persistence unit</li> + * </ul> + * + * @param <T> The Thrift model type + * @param <E> The JPA entity type + * @param <Id> The primary key type + */ +public abstract class AbstractRepository<T, E, Id> { + protected static final Logger logger = LoggerFactory.getLogger(AbstractRepository.class); + + protected final Class<T> thriftGenericClass; + protected final Class<E> dbEntityGenericClass; + + /** + * Constructor for the abstract repository. + * + * @param thriftGenericClass The Thrift model class + * @param dbEntityGenericClass The JPA entity class + */ + protected AbstractRepository(Class<T> thriftGenericClass, Class<E> dbEntityGenericClass) { + this.thriftGenericClass = thriftGenericClass; + this.dbEntityGenericClass = dbEntityGenericClass; + } + + /** + * Get the Dozer mapper instance for converting between Thrift models and entities. + * + * @return The mapper instance + */ + protected abstract Mapper getMapper(); + + /** + * Get the EntityManager for this persistence unit. + * Each module should provide its own EntityManager implementation. + * + * @return The EntityManager instance + */ + protected abstract EntityManager getEntityManager(); + + /** + * Create a new entity. This is equivalent to update() for most implementations. + * + * @param t The Thrift model to persist + * @return The persisted Thrift model + */ + @Transactional + public T create(T t) { + return update(t); + } + + /** + * Update or create an entity. + * + * @param t The Thrift model to persist + * @return The persisted Thrift model + */ + @Transactional + public T update(T t) { + E entity = mapToEntity(t); + return mergeEntity(entity); + } + + /** + * Map a Thrift model to a JPA entity. + * + * @param t The Thrift model + * @return The JPA entity + */ + protected E mapToEntity(T t) { + return getMapper().map(t, dbEntityGenericClass); + } + + /** + * Merge an entity into the persistence context and map back to Thrift model. + * + * @param entity The JPA entity + * @return The persisted Thrift model + */ + protected T mergeEntity(E entity) { + EntityManager em = getEntityManager(); + E persistedCopy = em.merge(entity); + return getMapper().map(persistedCopy, thriftGenericClass); + } + + /** + * Delete an entity by its primary key. + * + * @param id The primary key + * @return true if the entity was deleted, false if it didn't exist + */ + @Transactional + public boolean delete(Id id) { + EntityManager em = getEntityManager(); + E entity = em.find(dbEntityGenericClass, id); + if (entity != null) { + em.remove(entity); + return true; + } + return false; + } + + /** + * Retrieve an entity by its primary key. + * + * @param id The primary key + * @return The Thrift model, or null if not found + */ + @Transactional(readOnly = true) + public T get(Id id) { + EntityManager em = getEntityManager(); + E entity = em.find(dbEntityGenericClass, id); + if (entity == null) { + return null; + } + return getMapper().map(entity, thriftGenericClass); + } + + /** + * Check if an entity exists by its primary key. + * + * @param id The primary key + * @return true if the entity exists, false otherwise + */ + @Transactional(readOnly = true) + public boolean isExists(Id id) { + return get(id) != null; + } +} + diff --git a/airavata-api/src/main/java/org/apache/airavata/common/validation/ValidationResult.java b/airavata-api/src/main/java/org/apache/airavata/common/validation/ValidationResult.java new file mode 100644 index 0000000000..c3d378a6f2 --- /dev/null +++ b/airavata-api/src/main/java/org/apache/airavata/common/validation/ValidationResult.java @@ -0,0 +1,148 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.common.validation; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents the result of a validation operation. + * + * <p>Contains information about whether validation passed or failed, + * and a list of validation errors if any occurred. + */ +public class ValidationResult { + private boolean valid; + private List<ValidationError> errors; + + public ValidationResult() { + this.valid = true; + this.errors = new ArrayList<>(); + } + + public ValidationResult(boolean valid) { + this.valid = valid; + this.errors = new ArrayList<>(); + } + + /** + * Check if validation passed. + * + * @return true if valid, false otherwise + */ + public boolean isValid() { + return valid; + } + + /** + * Set the validation status. + * + * @param valid true if valid, false otherwise + */ + public void setValid(boolean valid) { + this.valid = valid; + } + + /** + * Get the list of validation errors. + * + * @return List of validation errors + */ + public List<ValidationError> getErrors() { + return errors; + } + + /** + * Set the list of validation errors. + * + * @param errors List of validation errors + */ + public void setErrors(List<ValidationError> errors) { + this.errors = errors != null ? errors : new ArrayList<>(); + } + + /** + * Add a validation error. + * + * @param field The field that failed validation + * @param message The error message + */ + public void addError(String field, String message) { + this.valid = false; + this.errors.add(new ValidationError(field, message)); + } + + /** + * Add a validation error. + * + * @param error The validation error + */ + public void addError(ValidationError error) { + this.valid = false; + this.errors.add(error); + } + + /** + * Check if there are any errors. + * + * @return true if there are errors, false otherwise + */ + public boolean hasErrors() { + return !errors.isEmpty(); + } + + /** + * Represents a single validation error. + */ + public static class ValidationError { + private String field; + private String message; + + public ValidationError() { + } + + public ValidationError(String field, String message) { + this.field = field; + this.message = message; + } + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return String.format("ValidationError{field='%s', message='%s'}", field, message); + } + } +} + diff --git a/airavata-api/src/main/java/org/apache/airavata/common/validation/ValidationService.java b/airavata-api/src/main/java/org/apache/airavata/common/validation/ValidationService.java new file mode 100644 index 0000000000..43f7896ab9 --- /dev/null +++ b/airavata-api/src/main/java/org/apache/airavata/common/validation/ValidationService.java @@ -0,0 +1,141 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.common.validation; + +import org.apache.airavata.common.utils.NameValidator; + +/** + * Service for performing common validation operations. + * + * <p>This service provides standardized validation methods that can be used + * across the Airavata API. It centralizes common validation logic and provides + * a consistent interface for validation operations. + */ +public class ValidationService { + + /** + * Validate that a string is not null, empty, or blank. + * + * @param value The string to validate + * @param fieldName The name of the field (for error messages) + * @param result The validation result to add errors to + * @return true if valid, false otherwise + */ + public static boolean validateRequiredString(String value, String fieldName, ValidationResult result) { + if (value == null || value.trim().isEmpty()) { + result.addError(fieldName, String.format("%s is required and cannot be empty", fieldName)); + return false; + } + return true; + } + + /** + * Validate that a string matches a name pattern (alphanumeric, underscore, dot). + * + * @param value The string to validate + * @param fieldName The name of the field (for error messages) + * @param result The validation result to add errors to + * @return true if valid, false otherwise + */ + public static boolean validateName(String value, String fieldName, ValidationResult result) { + if (!NameValidator.validate(value)) { + result.addError(fieldName, + String.format("%s must start with a letter and contain only letters, numbers, underscores, and dots", fieldName)); + return false; + } + return true; + } + + /** + * Validate that a string is within a specified length range. + * + * @param value The string to validate + * @param fieldName The name of the field + * @param minLength Minimum length (inclusive) + * @param maxLength Maximum length (inclusive) + * @param result The validation result to add errors to + * @return true if valid, false otherwise + */ + public static boolean validateLength(String value, String fieldName, int minLength, int maxLength, ValidationResult result) { + if (value == null) { + result.addError(fieldName, String.format("%s cannot be null", fieldName)); + return false; + } + int length = value.length(); + if (length < minLength || length > maxLength) { + result.addError(fieldName, + String.format("%s must be between %d and %d characters long", fieldName, minLength, maxLength)); + return false; + } + return true; + } + + /** + * Validate that an object is not null. + * + * @param value The object to validate + * @param fieldName The name of the field + * @param result The validation result to add errors to + * @return true if valid, false otherwise + */ + public static boolean validateNotNull(Object value, String fieldName, ValidationResult result) { + if (value == null) { + result.addError(fieldName, String.format("%s cannot be null", fieldName)); + return false; + } + return true; + } + + /** + * Validate that a number is within a specified range. + * + * @param value The number to validate + * @param fieldName The name of the field + * @param min Minimum value (inclusive) + * @param max Maximum value (inclusive) + * @param result The validation result to add errors to + * @return true if valid, false otherwise + */ + public static boolean validateRange(long value, String fieldName, long min, long max, ValidationResult result) { + if (value < min || value > max) { + result.addError(fieldName, + String.format("%s must be between %d and %d", fieldName, min, max)); + return false; + } + return true; + } + + /** + * Validate that a number is positive (greater than zero). + * + * @param value The number to validate + * @param fieldName The name of the field + * @param result The validation result to add errors to + * @return true if valid, false otherwise + */ + public static boolean validatePositive(long value, String fieldName, ValidationResult result) { + if (value <= 0) { + result.addError(fieldName, String.format("%s must be greater than zero", fieldName)); + return false; + } + return true; + } +} + diff --git a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/aws/AWSCompletingTask.java b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/aws/AWSCompletingTask.java index 397588880b..4a8549a01c 100644 --- a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/aws/AWSCompletingTask.java +++ b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/aws/AWSCompletingTask.java @@ -55,6 +55,10 @@ public class AWSCompletingTask extends AiravataTask { return onSuccess("Process " + getProcessId() + " successfully completed"); } + /** + * Called when the task is cancelled. + * No cleanup needed for AWS completing tasks. + */ @Override public void onCancel(TaskContext taskContext) {} } diff --git a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/cancel/RemoteJobCancellationTask.java b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/cancel/RemoteJobCancellationTask.java index 6ca1394b9f..f519ce2f8a 100644 --- a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/cancel/RemoteJobCancellationTask.java +++ b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/cancel/RemoteJobCancellationTask.java @@ -197,8 +197,8 @@ public class RemoteJobCancellationTask extends AiravataTask { ex); } - // TODO this is temporary fix. Remove this line when the schedulers are configured to notify when an job - // is externally cancelled + // TODO: Remove this temporary fix once schedulers are configured to notify when a job is externally cancelled + // This is a workaround until proper job cancellation notification is implemented // forcefully make the job state as cancelled as some schedulers do not notify when the job is // cancelled. saveAndPublishJobStatus( @@ -219,6 +219,10 @@ public class RemoteJobCancellationTask extends AiravataTask { } } + /** + * Called when the task is cancelled. + * Cancellation logic is handled in the onRun method's catch block. + */ @Override public void onCancel(TaskContext taskContext) {} } diff --git a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/cancel/WorkflowCancellationTask.java b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/cancel/WorkflowCancellationTask.java index 65cd130fc0..d7da8a7f47 100644 --- a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/cancel/WorkflowCancellationTask.java +++ b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/cancel/WorkflowCancellationTask.java @@ -147,6 +147,10 @@ public class WorkflowCancellationTask extends AbstractTask { } } + /** + * Called when the task is cancelled. + * No cleanup needed for workflow cancellation tasks. + */ @Override public void onCancel() {} diff --git a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/parsing/DataParsingTask.java b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/parsing/DataParsingTask.java index f7eca334dc..b0fc421bf2 100644 --- a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/parsing/DataParsingTask.java +++ b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/parsing/DataParsingTask.java @@ -260,6 +260,10 @@ public class DataParsingTask extends AbstractTask { } } + /** + * Called when the task is cancelled. + * No cleanup needed for data parsing tasks. + */ @Override public void onCancel() {} diff --git a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/DefaultJobSubmissionTask.java b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/DefaultJobSubmissionTask.java index 9f70f28bd2..c243fb82c4 100644 --- a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/DefaultJobSubmissionTask.java +++ b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/DefaultJobSubmissionTask.java @@ -296,6 +296,10 @@ public class DefaultJobSubmissionTask extends JobSubmissionTask { return jobId; } + /** + * Called when the task is cancelled. + * No cleanup needed for job submission tasks. + */ @Override public void onCancel(TaskContext taskContext) {} } diff --git a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/config/app/parser/AiravataCustomCommandOutputParser.java b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/config/app/parser/AiravataCustomCommandOutputParser.java index b05350913c..6f8b7ae4dc 100644 --- a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/config/app/parser/AiravataCustomCommandOutputParser.java +++ b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/config/app/parser/AiravataCustomCommandOutputParser.java @@ -25,6 +25,15 @@ import org.apache.airavata.model.status.JobStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Placeholder implementation of OutputParser for Airavata custom command output parsing. + * + * <p>This class is currently a stub implementation where all methods throw + * UnsupportedOperationException. It is used by CloudJobManagerConfiguration but + * requires implementation to be functional. + * + * <p>TODO: Implement the parsing logic for Airavata custom command output format. + */ public class AiravataCustomCommandOutputParser implements OutputParser { private static final Logger log = LoggerFactory.getLogger(AiravataCustomCommandOutputParser.class); diff --git a/airavata-api/src/main/java/org/apache/airavata/messaging/client/RabbitMQListener.java b/airavata-api/src/main/java/org/apache/airavata/messaging/client/RabbitMQListener.java index fbf625b718..855b9547ad 100644 --- a/airavata-api/src/main/java/org/apache/airavata/messaging/client/RabbitMQListener.java +++ b/airavata-api/src/main/java/org/apache/airavata/messaging/client/RabbitMQListener.java @@ -88,13 +88,4 @@ public class RabbitMQListener { } } - // Unused enum - commented out - /* - private enum LEVEL { - ALL, - GATEWAY, - EXPERIMENT, - JOB; - } - */ } diff --git a/airavata-api/src/main/java/org/apache/airavata/monitor/email/parser/PBSEmailParser.java b/airavata-api/src/main/java/org/apache/airavata/monitor/email/parser/PBSEmailParser.java index 00d9edaf9e..a3a08293e4 100644 --- a/airavata-api/src/main/java/org/apache/airavata/monitor/email/parser/PBSEmailParser.java +++ b/airavata-api/src/main/java/org/apache/airavata/monitor/email/parser/PBSEmailParser.java @@ -77,7 +77,8 @@ public class PBSEmailParser implements EmailParser { case EXECUTION_TERMINATED: int exitStatus = getExitStatus(content); if (exitStatus == 0) { - // TODO - Remove rabbitmq client script line from the script. + // TODO: Remove rabbitmq client script line from the script + // This is a workaround that should be removed once script generation is updated return JobState.COMPLETE; } else if (exitStatus == 271) { return JobState.CANCELED; diff --git a/airavata-api/src/main/java/org/apache/airavata/monitor/kafka/JobStatusResultDeserializer.java b/airavata-api/src/main/java/org/apache/airavata/monitor/kafka/JobStatusResultDeserializer.java index 4ad03112e2..971b263420 100644 --- a/airavata-api/src/main/java/org/apache/airavata/monitor/kafka/JobStatusResultDeserializer.java +++ b/airavata-api/src/main/java/org/apache/airavata/monitor/kafka/JobStatusResultDeserializer.java @@ -25,6 +25,10 @@ import org.apache.airavata.monitor.JobStatusResult; import org.apache.kafka.common.serialization.Deserializer; public class JobStatusResultDeserializer implements Deserializer<JobStatusResult> { + /** + * Configure the deserializer. Required by Kafka Deserializer interface. + * No configuration needed for this deserializer. + */ @Override public void configure(Map<String, ?> map, boolean b) {} @@ -40,6 +44,10 @@ public class JobStatusResultDeserializer implements Deserializer<JobStatusResult return jobStatusResult; } + /** + * Close the deserializer. Required by Kafka Deserializer interface. + * No cleanup needed for this deserializer. + */ @Override public void close() {} } diff --git a/airavata-api/src/main/java/org/apache/airavata/monitor/kafka/JobStatusResultSerializer.java b/airavata-api/src/main/java/org/apache/airavata/monitor/kafka/JobStatusResultSerializer.java index 7ebfaf5bfa..e7dc15a70a 100644 --- a/airavata-api/src/main/java/org/apache/airavata/monitor/kafka/JobStatusResultSerializer.java +++ b/airavata-api/src/main/java/org/apache/airavata/monitor/kafka/JobStatusResultSerializer.java @@ -25,6 +25,10 @@ import org.apache.kafka.common.serialization.Serializer; public class JobStatusResultSerializer implements Serializer<JobStatusResult> { + /** + * Configure the serializer. Required by Kafka Serializer interface. + * No configuration needed for this serializer. + */ @Override public void configure(Map<String, ?> map, boolean b) {} @@ -36,6 +40,10 @@ public class JobStatusResultSerializer implements Serializer<JobStatusResult> { return serializedData.getBytes(); } + /** + * Close the serializer. Required by Kafka Serializer interface. + * No cleanup needed for this serializer. + */ @Override public void close() {} } diff --git a/airavata-api/src/main/java/org/apache/airavata/profile/utils/TenantManagementKeycloakImpl.java b/airavata-api/src/main/java/org/apache/airavata/profile/utils/TenantManagementKeycloakImpl.java index 645b7ea660..e66966f71f 100644 --- a/airavata-api/src/main/java/org/apache/airavata/profile/utils/TenantManagementKeycloakImpl.java +++ b/airavata-api/src/main/java/org/apache/airavata/profile/utils/TenantManagementKeycloakImpl.java @@ -667,7 +667,8 @@ public class TenantManagementKeycloakImpl implements TenantManagementInterface { } } - // TODO: this is needed for migrating from roles to group-based auth but after migration we can remove this + // TODO: Remove after migration to group-based auth is complete + // This method is needed for backward compatibility during migration from role-based to group-based authentication @Override public List<UserProfile> getUsersWithRole(PasswordCredential realmAdminCreds, String tenantId, String roleName) throws IamAdminServicesException { diff --git a/airavata-api/src/main/java/org/apache/airavata/registry/cpi/ReplicaCatalog.java b/airavata-api/src/main/java/org/apache/airavata/registry/cpi/ReplicaCatalog.java index d7e5421b8c..f27df1bbac 100644 --- a/airavata-api/src/main/java/org/apache/airavata/registry/cpi/ReplicaCatalog.java +++ b/airavata-api/src/main/java/org/apache/airavata/registry/cpi/ReplicaCatalog.java @@ -24,32 +24,4 @@ Included for backwards compatibility TODO: Remove interface once registry refactoring is complete */ public interface ReplicaCatalog extends DataProductInterface, DataReplicaLocationInterface { - /*String schema = "airavata-dp"; - - String registerDataProduct(DataProductModel product) throws ReplicaCatalogException; - - boolean removeDataProduct(String productUri) throws ReplicaCatalogException; - - boolean updateDataProduct(DataProductModel product) throws ReplicaCatalogException; - - DataProductModel getDataProduct(String productUri) throws ReplicaCatalogException; - - boolean isDataProductExists(String productUri) throws ReplicaCatalogException; - - String registerReplicaLocation(DataReplicaLocationModel dataReplicaLocationModel) throws ReplicaCatalogException; - - boolean removeReplicaLocation(String replicaId) throws ReplicaCatalogException; - - boolean updateReplicaLocation(DataReplicaLocationModel dataReplicaLocationModel) throws ReplicaCatalogException; - - DataReplicaLocationModel getReplicaLocation(String replicaId) throws ReplicaCatalogException; - - List<DataReplicaLocationModel> getAllReplicaLocations(String productUri) throws ReplicaCatalogException; - - DataProductModel getParentDataProduct(String productUri) throws ReplicaCatalogException; - - List<DataProductModel> getChildDataProducts(String productUri) throws ReplicaCatalogException; - - List<DataProductModel> searchDataProductsByName(String gatewayId, String userId, String productName, - int limit, int offset) throws ReplicaCatalogException;*/ } diff --git a/airavata-api/src/main/java/org/apache/airavata/registry/services/BaseErrorService.java b/airavata-api/src/main/java/org/apache/airavata/registry/services/BaseErrorService.java new file mode 100644 index 0000000000..1dafbca9fe --- /dev/null +++ b/airavata-api/src/main/java/org/apache/airavata/registry/services/BaseErrorService.java @@ -0,0 +1,119 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.registry.services; + +import com.github.dozermapper.core.Mapper; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.apache.airavata.model.commons.ErrorModel; +import org.apache.airavata.registry.exceptions.RegistryException; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.transaction.annotation.Transactional; + +/** + * Base service class for error management operations. + * Provides common functionality for adding, updating, and retrieving errors + * associated with different entity types (Process, Task, Experiment). + * + * @param <TEntity> The error entity type (e.g., ProcessErrorEntity, TaskErrorEntity) + * @param <TRepository> The repository interface extending JpaRepository + * @param <TPK> The primary key type for the entity + */ +public abstract class BaseErrorService<TEntity, TRepository extends JpaRepository<TEntity, TPK>, TPK> { + + protected final TRepository repository; + protected final Mapper mapper; + + /** + * Functional interface to set the parent entity ID on the error entity. + * For example: entity.setProcessId(parentId) + */ + protected abstract BiConsumer<TEntity, String> getParentIdSetter(); + + /** + * Functional interface to retrieve errors by parent entity ID. + * For example: repository.findByProcessId(parentId) + */ + protected abstract Function<String, List<TEntity>> getFindByParentIdFunction(); + + /** + * Get the entity class for Dozer mapping. + */ + protected abstract Class<TEntity> getEntityClass(); + + /** + * Functional interface to extract the error ID from the entity. + * For example: entity.getErrorId() + */ + protected abstract Function<TEntity, String> getErrorIdExtractor(); + + protected BaseErrorService(TRepository repository, Mapper mapper) { + this.repository = repository; + this.mapper = mapper; + } + + /** + * Add an error associated with a parent entity. + * + * @param error The error model to persist + * @param parentId The ID of the parent entity (processId, taskId, or experimentId) + * @return The ID of the saved error entity + * @throws RegistryException if the operation fails + */ + @Transactional + public String addError(ErrorModel error, String parentId) throws RegistryException { + TEntity entity = mapper.map(error, getEntityClass()); + getParentIdSetter().accept(entity, parentId); + TEntity saved = repository.save(entity); + return getErrorIdExtractor().apply(saved); + } + + /** + * Update an existing error associated with a parent entity. + * + * @param error The error model with updated information + * @param parentId The ID of the parent entity + * @throws RegistryException if the operation fails + */ + @Transactional + public void updateError(ErrorModel error, String parentId) throws RegistryException { + TEntity entity = mapper.map(error, getEntityClass()); + getParentIdSetter().accept(entity, parentId); + repository.save(entity); + } + + /** + * Retrieve all errors associated with a parent entity. + * + * @param parentId The ID of the parent entity + * @return List of error models + * @throws RegistryException if the operation fails + */ + @Transactional(readOnly = true) + public List<ErrorModel> getErrors(String parentId) throws RegistryException { + List<TEntity> entities = getFindByParentIdFunction().apply(parentId); + return entities.stream() + .map(e -> mapper.map(e, ErrorModel.class)) + .collect(Collectors.toList()); + } +} + diff --git a/airavata-api/src/main/java/org/apache/airavata/registry/services/ExperimentErrorService.java b/airavata-api/src/main/java/org/apache/airavata/registry/services/ExperimentErrorService.java index 6908b0e30e..1ec932e33c 100644 --- a/airavata-api/src/main/java/org/apache/airavata/registry/services/ExperimentErrorService.java +++ b/airavata-api/src/main/java/org/apache/airavata/registry/services/ExperimentErrorService.java @@ -20,10 +20,11 @@ package org.apache.airavata.registry.services; import com.github.dozermapper.core.Mapper; -import java.util.ArrayList; -import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; import org.apache.airavata.model.commons.ErrorModel; import org.apache.airavata.registry.entities.expcatalog.ExperimentErrorEntity; +import org.apache.airavata.registry.entities.expcatalog.ExperimentErrorPK; import org.apache.airavata.registry.exceptions.RegistryException; import org.apache.airavata.registry.repositories.expcatalog.ExperimentErrorRepository; import org.springframework.stereotype.Service; @@ -31,32 +32,63 @@ import org.springframework.transaction.annotation.Transactional; @Service @Transactional -public class ExperimentErrorService { - private final ExperimentErrorRepository experimentErrorRepository; - private final Mapper mapper; +public class ExperimentErrorService extends BaseErrorService<ExperimentErrorEntity, ExperimentErrorRepository, ExperimentErrorPK> { public ExperimentErrorService(ExperimentErrorRepository experimentErrorRepository, Mapper mapper) { - this.experimentErrorRepository = experimentErrorRepository; - this.mapper = mapper; + super(experimentErrorRepository, mapper); } + @Override + protected BiConsumer<ExperimentErrorEntity, String> getParentIdSetter() { + return ExperimentErrorEntity::setExperimentId; + } + + @Override + protected Function<String, java.util.List<ExperimentErrorEntity>> getFindByParentIdFunction() { + return repository::findByExperimentId; + } + + @Override + protected Class<ExperimentErrorEntity> getEntityClass() { + return ExperimentErrorEntity.class; + } + + @Override + protected Function<ExperimentErrorEntity, String> getErrorIdExtractor() { + return ExperimentErrorEntity::getErrorId; + } + + /** + * Add an experiment error. + * + * @param error The error model to persist + * @param experimentId The ID of the experiment + * @return The ID of the saved error entity + * @throws RegistryException if the operation fails + */ public String addExperimentError(ErrorModel error, String experimentId) throws RegistryException { - ExperimentErrorEntity entity = mapper.map(error, ExperimentErrorEntity.class); - entity.setExperimentId(experimentId); - ExperimentErrorEntity saved = experimentErrorRepository.save(entity); - return saved.getErrorId(); + return addError(error, experimentId); } + /** + * Update an experiment error. + * + * @param error The error model with updated information + * @param experimentId The ID of the experiment + * @throws RegistryException if the operation fails + */ public void updateExperimentError(ErrorModel error, String experimentId) throws RegistryException { - ExperimentErrorEntity entity = mapper.map(error, ExperimentErrorEntity.class); - entity.setExperimentId(experimentId); - experimentErrorRepository.save(entity); + updateError(error, experimentId); } - public List<ErrorModel> getExperimentErrors(String experimentId) throws RegistryException { - List<ExperimentErrorEntity> entities = experimentErrorRepository.findByExperimentId(experimentId); - List<ErrorModel> result = new ArrayList<>(); - entities.forEach(e -> result.add(mapper.map(e, ErrorModel.class))); - return result; + /** + * Retrieve all errors for an experiment. + * + * @param experimentId The ID of the experiment + * @return List of error models + * @throws RegistryException if the operation fails + */ + public java.util.List<ErrorModel> getExperimentErrors(String experimentId) throws RegistryException { + return getErrors(experimentId); } } diff --git a/airavata-api/src/main/java/org/apache/airavata/registry/services/ExperimentService.java b/airavata-api/src/main/java/org/apache/airavata/registry/services/ExperimentService.java index e3fcacf109..9242e0fe98 100644 --- a/airavata-api/src/main/java/org/apache/airavata/registry/services/ExperimentService.java +++ b/airavata-api/src/main/java/org/apache/airavata/registry/services/ExperimentService.java @@ -39,7 +39,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service -@Transactional public class ExperimentService { private static final Logger logger = LoggerFactory.getLogger(ExperimentService.class); @@ -53,6 +52,7 @@ public class ExperimentService { this.mapper = mapper; } + @Transactional public String addExperiment(ExperimentModel experimentModel) throws RegistryException { ExperimentStatus experimentStatus = new ExperimentStatus(); experimentStatus.setState(ExperimentState.CREATED); @@ -66,16 +66,19 @@ public class ExperimentService { return saveExperimentModelData(experimentModel); } + @Transactional public void updateExperiment(ExperimentModel updatedExperimentModel, String experimentId) throws RegistryException { saveExperimentModelData(updatedExperimentModel); } + @Transactional(readOnly = true) public ExperimentModel getExperiment(String experimentId) throws RegistryException { ExperimentEntity entity = experimentRepository.findById(experimentId).orElse(null); if (entity == null) return null; return mapper.map(entity, ExperimentModel.class); } + @Transactional public String addUserConfigurationData(UserConfigurationDataModel userConfigurationDataModel, String experimentId) throws RegistryException { ExperimentModel experimentModel = getExperiment(experimentId); @@ -84,17 +87,20 @@ public class ExperimentService { return experimentId; } + @Transactional public String updateUserConfigurationData( UserConfigurationDataModel updatedUserConfigurationDataModel, String experimentId) throws RegistryException { return addUserConfigurationData(updatedUserConfigurationDataModel, experimentId); } + @Transactional(readOnly = true) public UserConfigurationDataModel getUserConfigurationData(String experimentId) throws RegistryException { ExperimentModel experimentModel = getExperiment(experimentId); return experimentModel.getUserConfigurationData(); } + @Transactional(readOnly = true) public List<ExperimentModel> getExperimentList( String gatewayId, String fieldName, @@ -126,10 +132,12 @@ public class ExperimentService { return experimentModelList; } + @Transactional(readOnly = true) public boolean isExperimentExist(String experimentId) throws RegistryException { return experimentRepository.existsById(experimentId); } + @Transactional public void removeExperiment(String experimentId) throws RegistryException { experimentRepository.deleteById(experimentId); } diff --git a/airavata-api/src/main/java/org/apache/airavata/registry/services/GwyResourceProfileService.java b/airavata-api/src/main/java/org/apache/airavata/registry/services/GwyResourceProfileService.java index e5fae84b5e..97c629ab81 100644 --- a/airavata-api/src/main/java/org/apache/airavata/registry/services/GwyResourceProfileService.java +++ b/airavata-api/src/main/java/org/apache/airavata/registry/services/GwyResourceProfileService.java @@ -36,7 +36,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service -@Transactional public class GwyResourceProfileService { private final GwyResourceProfileRepository gwyResourceProfileRepository; private final ComputeResourcePrefRepository computeResourcePrefRepository; @@ -57,15 +56,18 @@ public class GwyResourceProfileService { this.sshAccountProvisionerConfigurationRepository = sshAccountProvisionerConfigurationRepository; } + @Transactional public String addGatewayResourceProfile(GatewayResourceProfile gatewayResourceProfile) { return updateGatewayResourceProfile(gatewayResourceProfile); } + @Transactional public void updateGatewayResourceProfile(String gatewayId, GatewayResourceProfile updatedProfile) throws AppCatalogException { updateGatewayResourceProfile(updatedProfile); } + @Transactional public String updateGatewayResourceProfile(GatewayResourceProfile gatewayResourceProfile) { String gatewayId = gatewayResourceProfile.getGatewayID(); GatewayProfileEntity gatewayProfileEntity = mapper.map(gatewayResourceProfile, GatewayProfileEntity.class); @@ -108,6 +110,7 @@ public class GwyResourceProfileService { return persistedCopy.getGatewayId(); } + @Transactional(readOnly = true) public GatewayResourceProfile getGatewayProfile(String gatewayId) { GatewayProfileEntity entity = gwyResourceProfileRepository.findById(gatewayId).orElse(null); @@ -125,6 +128,7 @@ public class GwyResourceProfileService { return gatewayResourceProfile; } + @Transactional public boolean removeGatewayResourceProfile(String gatewayId) throws AppCatalogException { if (!gwyResourceProfileRepository.existsById(gatewayId)) { return false; @@ -133,6 +137,7 @@ public class GwyResourceProfileService { return true; } + @Transactional(readOnly = true) public List<GatewayResourceProfile> getAllGatewayProfiles() { List<GatewayProfileEntity> entities = gwyResourceProfileRepository.findAll(); List<GatewayResourceProfile> gatewayResourceProfileList = new ArrayList<>(); @@ -151,6 +156,7 @@ public class GwyResourceProfileService { return gatewayResourceProfileList; } + @Transactional public boolean removeComputeResourcePreferenceFromGateway(String gatewayId, String preferenceId) { ComputeResourcePreferencePK computeResourcePreferencePK = new ComputeResourcePreferencePK(); computeResourcePreferencePK.setGatewayId(gatewayId); @@ -159,6 +165,7 @@ public class GwyResourceProfileService { return true; } + @Transactional public boolean removeDataStoragePreferenceFromGateway(String gatewayId, String preferenceId) { StoragePreferencePK storagePreferencePK = new StoragePreferencePK(); storagePreferencePK.setGatewayId(gatewayId); @@ -167,10 +174,12 @@ public class GwyResourceProfileService { return true; } + @Transactional(readOnly = true) public boolean isGatewayResourceProfileExists(String gatewayId) throws AppCatalogException { return gwyResourceProfileRepository.existsById(gatewayId); } + @Transactional(readOnly = true) public ComputeResourcePreference getComputeResourcePreference(String gatewayId, String hostId) { ComputeResourcePreferencePK computeResourcePreferencePK = new ComputeResourcePreferencePK(); computeResourcePreferencePK.setGatewayId(gatewayId); @@ -186,6 +195,7 @@ public class GwyResourceProfileService { return computeResourcePreference; } + @Transactional(readOnly = true) public StoragePreference getStoragePreference(String gatewayId, String storageId) { StoragePreferencePK storagePreferencePK = new StoragePreferencePK(); storagePreferencePK.setStorageResourceId(storageId); @@ -196,6 +206,7 @@ public class GwyResourceProfileService { .orElse(null); } + @Transactional(readOnly = true) public List<ComputeResourcePreference> getAllComputeResourcePreferences(String gatewayId) { List<ComputeResourcePreferenceEntity> entities = computeResourcePrefRepository.findByGatewayId(gatewayId); List<ComputeResourcePreference> preferences = entities.stream() @@ -211,6 +222,7 @@ public class GwyResourceProfileService { return preferences; } + @Transactional(readOnly = true) public List<StoragePreference> getAllStoragePreferences(String gatewayId) { List<StoragePreferenceEntity> entities = storagePrefRepository.findByGatewayId(gatewayId); return entities.stream() diff --git a/airavata-api/src/main/java/org/apache/airavata/registry/services/ProcessErrorService.java b/airavata-api/src/main/java/org/apache/airavata/registry/services/ProcessErrorService.java index 8d250bc557..5b52edc488 100644 --- a/airavata-api/src/main/java/org/apache/airavata/registry/services/ProcessErrorService.java +++ b/airavata-api/src/main/java/org/apache/airavata/registry/services/ProcessErrorService.java @@ -20,10 +20,11 @@ package org.apache.airavata.registry.services; import com.github.dozermapper.core.Mapper; -import java.util.ArrayList; -import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; import org.apache.airavata.model.commons.ErrorModel; import org.apache.airavata.registry.entities.expcatalog.ProcessErrorEntity; +import org.apache.airavata.registry.entities.expcatalog.ProcessErrorPK; import org.apache.airavata.registry.exceptions.RegistryException; import org.apache.airavata.registry.repositories.expcatalog.ProcessErrorRepository; import org.springframework.stereotype.Service; @@ -31,32 +32,63 @@ import org.springframework.transaction.annotation.Transactional; @Service @Transactional -public class ProcessErrorService { - private final ProcessErrorRepository processErrorRepository; - private final Mapper mapper; +public class ProcessErrorService extends BaseErrorService<ProcessErrorEntity, ProcessErrorRepository, ProcessErrorPK> { public ProcessErrorService(ProcessErrorRepository processErrorRepository, Mapper mapper) { - this.processErrorRepository = processErrorRepository; - this.mapper = mapper; + super(processErrorRepository, mapper); } + @Override + protected BiConsumer<ProcessErrorEntity, String> getParentIdSetter() { + return ProcessErrorEntity::setProcessId; + } + + @Override + protected Function<String, java.util.List<ProcessErrorEntity>> getFindByParentIdFunction() { + return repository::findByProcessId; + } + + @Override + protected Class<ProcessErrorEntity> getEntityClass() { + return ProcessErrorEntity.class; + } + + @Override + protected Function<ProcessErrorEntity, String> getErrorIdExtractor() { + return ProcessErrorEntity::getErrorId; + } + + /** + * Add a process error. + * + * @param error The error model to persist + * @param processId The ID of the process + * @return The ID of the saved error entity + * @throws RegistryException if the operation fails + */ public String addProcessError(ErrorModel error, String processId) throws RegistryException { - ProcessErrorEntity entity = mapper.map(error, ProcessErrorEntity.class); - entity.setProcessId(processId); - ProcessErrorEntity saved = processErrorRepository.save(entity); - return saved.getErrorId(); + return addError(error, processId); } + /** + * Update a process error. + * + * @param error The error model with updated information + * @param processId The ID of the process + * @throws RegistryException if the operation fails + */ public void updateProcessError(ErrorModel error, String processId) throws RegistryException { - ProcessErrorEntity entity = mapper.map(error, ProcessErrorEntity.class); - entity.setProcessId(processId); - processErrorRepository.save(entity); + updateError(error, processId); } - public List<ErrorModel> getProcessError(String processId) throws RegistryException { - List<ProcessErrorEntity> entities = processErrorRepository.findByProcessId(processId); - List<ErrorModel> result = new ArrayList<>(); - entities.forEach(e -> result.add(mapper.map(e, ErrorModel.class))); - return result; + /** + * Retrieve all errors for a process. + * + * @param processId The ID of the process + * @return List of error models + * @throws RegistryException if the operation fails + */ + public java.util.List<ErrorModel> getProcessError(String processId) throws RegistryException { + return getErrors(processId); } } diff --git a/airavata-api/src/main/java/org/apache/airavata/registry/services/ProcessService.java b/airavata-api/src/main/java/org/apache/airavata/registry/services/ProcessService.java index cb7d5ac389..04dfee119c 100644 --- a/airavata-api/src/main/java/org/apache/airavata/registry/services/ProcessService.java +++ b/airavata-api/src/main/java/org/apache/airavata/registry/services/ProcessService.java @@ -41,7 +41,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service -@Transactional public class ProcessService { private static final Logger logger = LoggerFactory.getLogger(ProcessService.class); @@ -97,6 +96,7 @@ public class ProcessService { } } + @Transactional public String addProcess(ProcessModel process, String experimentId) throws RegistryException { process.setExperimentId(experimentId); @@ -106,16 +106,19 @@ public class ProcessService { return processId; } + @Transactional public void updateProcess(ProcessModel updatedProcess, String processId) throws RegistryException { saveProcessModelData(updatedProcess); } + @Transactional(readOnly = true) public ProcessModel getProcess(String processId) throws RegistryException { ProcessEntity entity = processRepository.findById(processId).orElse(null); if (entity == null) return null; return mapper.map(entity, ProcessModel.class); } + @Transactional public String addProcessResourceSchedule( ComputationalResourceSchedulingModel computationalResourceSchedulingModel, String processId) throws RegistryException { @@ -125,17 +128,20 @@ public class ProcessService { return processId; } + @Transactional public String updateProcessResourceSchedule( ComputationalResourceSchedulingModel computationalResourceSchedulingModel, String processId) throws RegistryException { return addProcessResourceSchedule(computationalResourceSchedulingModel, processId); } + @Transactional(readOnly = true) public ComputationalResourceSchedulingModel getProcessResourceSchedule(String processId) throws RegistryException { ProcessModel processModel = getProcess(processId); return processModel.getProcessResourceSchedule(); } + @Transactional(readOnly = true) public List<ProcessModel> getProcessList(String fieldName, Object value) throws RegistryException { List<ProcessModel> processModelList; @@ -152,6 +158,7 @@ public class ProcessService { return processModelList; } + @Transactional(readOnly = true) public List<String> getProcessIds(String fieldName, Object value) throws RegistryException { List<String> processIds = new ArrayList<>(); List<ProcessModel> processModelList = getProcessList(fieldName, value); @@ -161,14 +168,17 @@ public class ProcessService { return processIds; } + @Transactional(readOnly = true) public boolean isProcessExist(String processId) throws RegistryException { return processRepository.existsById(processId); } + @Transactional public void removeProcess(String processId) throws RegistryException { processRepository.deleteById(processId); } + @Transactional(readOnly = true) public List<ProcessModel> getAllProcesses(int offset, int limit) { List<ProcessEntity> entities = processRepository.findAll(); List<ProcessModel> result = new ArrayList<>(); diff --git a/airavata-api/src/main/java/org/apache/airavata/registry/services/TaskErrorService.java b/airavata-api/src/main/java/org/apache/airavata/registry/services/TaskErrorService.java index cfa46e54d8..59530b00a9 100644 --- a/airavata-api/src/main/java/org/apache/airavata/registry/services/TaskErrorService.java +++ b/airavata-api/src/main/java/org/apache/airavata/registry/services/TaskErrorService.java @@ -20,10 +20,11 @@ package org.apache.airavata.registry.services; import com.github.dozermapper.core.Mapper; -import java.util.ArrayList; -import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; import org.apache.airavata.model.commons.ErrorModel; import org.apache.airavata.registry.entities.expcatalog.TaskErrorEntity; +import org.apache.airavata.registry.entities.expcatalog.TaskErrorPK; import org.apache.airavata.registry.exceptions.RegistryException; import org.apache.airavata.registry.repositories.expcatalog.TaskErrorRepository; import org.springframework.stereotype.Service; @@ -31,32 +32,63 @@ import org.springframework.transaction.annotation.Transactional; @Service @Transactional -public class TaskErrorService { - private final TaskErrorRepository taskErrorRepository; - private final Mapper mapper; +public class TaskErrorService extends BaseErrorService<TaskErrorEntity, TaskErrorRepository, TaskErrorPK> { public TaskErrorService(TaskErrorRepository taskErrorRepository, Mapper mapper) { - this.taskErrorRepository = taskErrorRepository; - this.mapper = mapper; + super(taskErrorRepository, mapper); } + @Override + protected BiConsumer<TaskErrorEntity, String> getParentIdSetter() { + return TaskErrorEntity::setTaskId; + } + + @Override + protected Function<String, java.util.List<TaskErrorEntity>> getFindByParentIdFunction() { + return repository::findByTaskId; + } + + @Override + protected Class<TaskErrorEntity> getEntityClass() { + return TaskErrorEntity.class; + } + + @Override + protected Function<TaskErrorEntity, String> getErrorIdExtractor() { + return TaskErrorEntity::getErrorId; + } + + /** + * Add a task error. + * + * @param error The error model to persist + * @param taskId The ID of the task + * @return The ID of the saved error entity + * @throws RegistryException if the operation fails + */ public String addTaskError(ErrorModel error, String taskId) throws RegistryException { - TaskErrorEntity entity = mapper.map(error, TaskErrorEntity.class); - entity.setTaskId(taskId); - TaskErrorEntity saved = taskErrorRepository.save(entity); - return saved.getErrorId(); + return addError(error, taskId); } + /** + * Update a task error. + * + * @param error The error model with updated information + * @param taskId The ID of the task + * @throws RegistryException if the operation fails + */ public void updateTaskError(ErrorModel error, String taskId) throws RegistryException { - TaskErrorEntity entity = mapper.map(error, TaskErrorEntity.class); - entity.setTaskId(taskId); - taskErrorRepository.save(entity); + updateError(error, taskId); } - public List<ErrorModel> getTaskError(String taskId) throws RegistryException { - List<TaskErrorEntity> entities = taskErrorRepository.findByTaskId(taskId); - List<ErrorModel> result = new ArrayList<>(); - entities.forEach(e -> result.add(mapper.map(e, ErrorModel.class))); - return result; + /** + * Retrieve all errors for a task. + * + * @param taskId The ID of the task + * @return List of error models + * @throws RegistryException if the operation fails + */ + public java.util.List<ErrorModel> getTaskError(String taskId) throws RegistryException { + return getErrors(taskId); } } diff --git a/airavata-api/src/main/java/org/apache/airavata/registry/services/UserResourceProfileService.java b/airavata-api/src/main/java/org/apache/airavata/registry/services/UserResourceProfileService.java index d4478675cd..ab19df294f 100644 --- a/airavata-api/src/main/java/org/apache/airavata/registry/services/UserResourceProfileService.java +++ b/airavata-api/src/main/java/org/apache/airavata/registry/services/UserResourceProfileService.java @@ -42,7 +42,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service -@Transactional public class UserResourceProfileService { private static final Logger logger = LoggerFactory.getLogger(UserResourceProfileService.class); @@ -62,10 +61,12 @@ public class UserResourceProfileService { this.mapper = mapper; } + @Transactional public String addUserResourceProfile(UserResourceProfile userResourceProfile) throws AppCatalogException { return saveUserResourceProfileData(userResourceProfile); } + @Transactional public void updateUserResourceProfile(String userId, String gatewayId, UserResourceProfile updatedProfile) throws AppCatalogException { saveUserResourceProfileData(updatedProfile); @@ -111,6 +112,7 @@ public class UserResourceProfileService { return userResourceProfileRepository.save(userResourceProfileEntity); } + @Transactional(readOnly = true) public UserResourceProfile getUserResourceProfile(String userId, String gatewayId) throws AppCatalogException { UserResourceProfilePK userResourceProfilePK = new UserResourceProfilePK(); userResourceProfilePK.setUserId(userId); @@ -121,6 +123,7 @@ public class UserResourceProfileService { return mapper.map(entity, UserResourceProfile.class); } + @Transactional(readOnly = true) public UserComputeResourcePreference getUserComputeResourcePreference( String userId, String gatewayId, String hostId) throws AppCatalogException { UserComputeResourcePreferencePK userComputeResourcePreferencePK = new UserComputeResourcePreferencePK(); @@ -133,6 +136,7 @@ public class UserResourceProfileService { .orElse(null); } + @Transactional(readOnly = true) public UserStoragePreference getUserStoragePreference(String userId, String gatewayId, String storageId) throws AppCatalogException { UserStoragePreferencePK userStoragePreferencePK = new UserStoragePreferencePK(); @@ -145,6 +149,7 @@ public class UserResourceProfileService { .orElse(null); } + @Transactional(readOnly = true) public List<UserResourceProfile> getAllUserResourceProfiles() throws AppCatalogException { List<UserResourceProfileEntity> entities = userResourceProfileRepository.findAll(); List<UserResourceProfile> result = new ArrayList<>(); @@ -152,6 +157,7 @@ public class UserResourceProfileService { return result; } + @Transactional(readOnly = true) public List<UserComputeResourcePreference> getAllUserComputeResourcePreferences(String userId, String gatewayId) throws AppCatalogException { List<UserComputeResourcePreferenceEntity> entities = @@ -161,6 +167,7 @@ public class UserResourceProfileService { .collect(java.util.stream.Collectors.toList()); } + @Transactional(readOnly = true) public List<UserStoragePreference> getAllUserStoragePreferences(String userId, String gatewayId) throws AppCatalogException { List<UserStoragePreferenceEntity> entities = @@ -170,6 +177,7 @@ public class UserResourceProfileService { .collect(java.util.stream.Collectors.toList()); } + @Transactional(readOnly = true) public List<String> getGatewayProfileIds(String gatewayName) throws AppCatalogException { List<UserResourceProfileEntity> entities = userResourceProfileRepository.findAll(); List<String> gatewayIdList = new ArrayList<>(); @@ -182,10 +190,12 @@ public class UserResourceProfileService { return gatewayIdList; } + @Transactional(readOnly = true) public String getUserNamefromID(String userId, String gatewayID) throws AppCatalogException { return userId; } + @Transactional public boolean removeUserResourceProfile(String userId, String gatewayId) throws AppCatalogException { UserResourceProfilePK userResourceProfilePK = new UserResourceProfilePK(); userResourceProfilePK.setUserId(userId); @@ -197,6 +207,7 @@ public class UserResourceProfileService { return true; } + @Transactional public boolean removeUserComputeResourcePreferenceFromGateway(String userId, String gatewayId, String preferenceId) throws AppCatalogException { UserComputeResourcePreferencePK userComputeResourcePreferencePK = new UserComputeResourcePreferencePK(); @@ -207,6 +218,7 @@ public class UserResourceProfileService { return true; } + @Transactional public boolean removeUserDataStoragePreferenceFromGateway(String userId, String gatewayId, String preferenceId) throws AppCatalogException { UserStoragePreferencePK userStoragePreferencePK = new UserStoragePreferencePK(); @@ -217,6 +229,7 @@ public class UserResourceProfileService { return true; } + @Transactional(readOnly = true) public boolean isUserResourceProfileExists(String userId, String gatewayId) throws AppCatalogException { UserResourceProfilePK userResourceProfilePK = new UserResourceProfilePK(); userResourceProfilePK.setUserId(userId); @@ -224,6 +237,7 @@ public class UserResourceProfileService { return userResourceProfileRepository.existsById(userResourceProfilePK); } + @Transactional(readOnly = true) public boolean isUserComputeResourcePreferenceExists(String userId, String gatewayId, String preferenceId) throws AppCatalogException { UserComputeResourcePreferencePK userComputeResourcePreferencePK = new UserComputeResourcePreferencePK(); diff --git a/airavata-api/src/main/java/org/apache/airavata/service/AiravataService.java b/airavata-api/src/main/java/org/apache/airavata/service/AiravataService.java index 665d969b4e..bde814372c 100644 --- a/airavata-api/src/main/java/org/apache/airavata/service/AiravataService.java +++ b/airavata-api/src/main/java/org/apache/airavata/service/AiravataService.java @@ -151,21 +151,42 @@ public class AiravataService { private record SharingEntity(Entity delegate) {} private boolean validateString(String name) { - boolean valid = true; - if (name == null || name.equals("") || name.trim().length() == 0) { - valid = false; - } - return valid; + return name != null && !name.trim().isEmpty(); } private AiravataSystemException airavataSystemException( AiravataErrorType errorType, String message, Throwable cause) { - var exception = new AiravataSystemException(errorType); - exception.setMessage(message); - if (cause != null) { - exception.initCause(cause); + return org.apache.airavata.common.exception.ExceptionHandlerUtil.wrapAsAiravataException( + errorType, message, cause); + } + + /** + * Functional interface for operations that may throw RegistryServiceException. + */ + @FunctionalInterface + private interface RegistryOperation<T> { + T execute() throws RegistryServiceException; + } + + /** + * Helper method to execute registry operations with standardized error handling. + * Reduces boilerplate code for try-catch blocks. + * + * @param operation The operation name for logging + * @param registryOp The operation to execute + * @param <T> The return type + * @return The result of the operation + * @throws AiravataSystemException if the operation fails + */ + private <T> T executeRegistryOperation(String operation, RegistryOperation<T> registryOp) + throws AiravataSystemException { + try { + return registryOp.execute(); + } catch (RegistryServiceException e) { + String msg = String.format("Error while %s: %s", operation, e.getMessage()); + logger.error(msg, e); + throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); } - return exception; } private boolean safeIsUserResourceProfileExists(AuthzToken authzToken, String userId, String gatewayId) @@ -1171,109 +1192,59 @@ public class AiravataService { } public ComputeResourceDescription getComputeResource(String computeResourceId) throws AiravataSystemException { - try { - return registryService.getComputeResource(computeResourceId); - } catch (RegistryServiceException e) { - String msg = "Error while retrieving compute resource: " + e.getMessage(); - logger.error(msg, e); - throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); - } + return executeRegistryOperation("retrieving compute resource", + () -> registryService.getComputeResource(computeResourceId)); } public String registerComputeResource(ComputeResourceDescription computeResourceDescription) throws AiravataSystemException { - try { - return registryService.registerComputeResource(computeResourceDescription); - } catch (RegistryServiceException e) { - String msg = "Error while saving compute resource: " + e.getMessage(); - logger.error(msg, e); - throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); - } + return executeRegistryOperation("saving compute resource", + () -> registryService.registerComputeResource(computeResourceDescription)); } public boolean updateComputeResource( String computeResourceId, ComputeResourceDescription computeResourceDescription) throws AiravataSystemException { - try { - return registryService.updateComputeResource(computeResourceId, computeResourceDescription); - } catch (RegistryServiceException e) { - String msg = "Error while updating compute resource: " + e.getMessage(); - logger.error(msg, e); - throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); - } + return executeRegistryOperation("updating compute resource", + () -> registryService.updateComputeResource(computeResourceId, computeResourceDescription)); } public boolean deleteComputeResource(String computeResourceId) throws AiravataSystemException { - try { - return registryService.deleteComputeResource(computeResourceId); - } catch (RegistryServiceException e) { - String msg = "Error while deleting compute resource: " + e.getMessage(); - logger.error(msg, e); - throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); - } + return executeRegistryOperation("deleting compute resource", + () -> registryService.deleteComputeResource(computeResourceId)); } public Map<String, String> getAllComputeResourceNames() throws AiravataSystemException { - try { - return registryService.getAllComputeResourceNames(); - } catch (RegistryServiceException e) { - String msg = "Error while retrieving compute resource names: " + e.getMessage(); - logger.error(msg, e); - throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); - } + return executeRegistryOperation("retrieving compute resource names", + () -> registryService.getAllComputeResourceNames()); } public String registerStorageResource(StorageResourceDescription storageResourceDescription) throws AiravataSystemException { - try { - return registryService.registerStorageResource(storageResourceDescription); - } catch (RegistryServiceException e) { - String msg = "Error while saving storage resource: " + e.getMessage(); - logger.error(msg, e); - throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); - } + return executeRegistryOperation("saving storage resource", + () -> registryService.registerStorageResource(storageResourceDescription)); } public StorageResourceDescription getStorageResource(String storageResourceId) throws AiravataSystemException { - try { - return registryService.getStorageResource(storageResourceId); - } catch (RegistryServiceException e) { - String msg = "Error while retrieving storage resource: " + e.getMessage(); - logger.error(msg, e); - throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); - } + return executeRegistryOperation("retrieving storage resource", + () -> registryService.getStorageResource(storageResourceId)); } public boolean updateStorageResource( String storageResourceId, StorageResourceDescription storageResourceDescription) throws AiravataSystemException { - try { - return registryService.updateStorageResource(storageResourceId, storageResourceDescription); - } catch (RegistryServiceException e) { - String msg = "Error while updating storage resource: " + e.getMessage(); - logger.error(msg, e); - throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); - } + return executeRegistryOperation("updating storage resource", + () -> registryService.updateStorageResource(storageResourceId, storageResourceDescription)); } public boolean deleteStorageResource(String storageResourceId) throws AiravataSystemException { - try { - return registryService.deleteStorageResource(storageResourceId); - } catch (RegistryServiceException e) { - String msg = "Error while deleting storage resource: " + e.getMessage(); - logger.error(msg, e); - throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); - } + return executeRegistryOperation("deleting storage resource", + () -> registryService.deleteStorageResource(storageResourceId)); } public Map<String, String> getAllStorageResourceNames() throws AiravataSystemException { - try { - return registryService.getAllStorageResourceNames(); - } catch (RegistryServiceException e) { - String msg = "Error while retrieving storage resource names: " + e.getMessage(); - logger.error(msg, e); - throw airavataSystemException(AiravataErrorType.INTERNAL_ERROR, msg, e); - } + return executeRegistryOperation("retrieving storage resource names", + () -> registryService.getAllStorageResourceNames()); } public String registerGatewayResourceProfile(GatewayResourceProfile gatewayResourceProfile) diff --git a/airavata-api/src/main/java/org/apache/airavata/service/OrchestratorService.java b/airavata-api/src/main/java/org/apache/airavata/service/OrchestratorService.java index 4d2da5d1f0..691ce21af9 100644 --- a/airavata-api/src/main/java/org/apache/airavata/service/OrchestratorService.java +++ b/airavata-api/src/main/java/org/apache/airavata/service/OrchestratorService.java @@ -27,7 +27,7 @@ import java.util.concurrent.ExecutorService; import org.apache.airavata.api.thrift.util.ThriftUtils; import org.apache.airavata.common.exception.AiravataException; import org.apache.airavata.common.logging.MDCConstants; -import org.apache.airavata.common.logging.MDCUtil; +import org.apache.airavata.common.logging.LoggingUtil; import org.apache.airavata.common.utils.AiravataUtils; import org.apache.airavata.common.utils.ZkConstants; import org.apache.airavata.config.AiravataServerProperties; @@ -894,7 +894,7 @@ public class OrchestratorService { logger.error("expId: " + experimentId + ", Error while launching single app experiment", e); } }; - executorService.execute(MDCUtil.wrapWithMDC(runner)); + executorService.execute(LoggingUtil.withMDC(runner)); } } return result; diff --git a/airavata-api/src/main/java/org/apache/airavata/sharing/messaging/SharingServiceDBEventHandler.java b/airavata-api/src/main/java/org/apache/airavata/sharing/messaging/SharingServiceDBEventHandler.java index eedb937839..262d1dbd62 100644 --- a/airavata-api/src/main/java/org/apache/airavata/sharing/messaging/SharingServiceDBEventHandler.java +++ b/airavata-api/src/main/java/org/apache/airavata/sharing/messaging/SharingServiceDBEventHandler.java @@ -108,7 +108,8 @@ public class SharingServiceDBEventHandler implements MessageHandler { break; case READ: - // FIXME: Remove if not required + // FIXME: Remove if not required - READ permission handling may be redundant + // Verify if this case is needed after reviewing permission model break; case DELETE: diff --git a/airavata-api/src/main/java/org/apache/airavata/sharing/repositories/AbstractRepository.java b/airavata-api/src/main/java/org/apache/airavata/sharing/repositories/AbstractRepository.java index 9ba6d5568b..22ae695eff 100644 --- a/airavata-api/src/main/java/org/apache/airavata/sharing/repositories/AbstractRepository.java +++ b/airavata-api/src/main/java/org/apache/airavata/sharing/repositories/AbstractRepository.java @@ -64,10 +64,40 @@ public abstract class AbstractRepository<T, E, Id> { return update(t); } - // FIXME do a bulk insert + /** + * Bulk create entities. Uses batch processing for better performance. + * + * @param tList List of Thrift models to persist + * @return List of persisted Thrift models + * @throws SharingRegistryException if the operation fails + */ @Transactional public List<T> create(List<T> tList) throws SharingRegistryException { - return update(tList); + if (tList == null || tList.isEmpty()) { + return new ArrayList<>(); + } + EntityManager em = getEntityManager(); + List<T> result = new ArrayList<>(); + int batchSize = 50; // Process in batches to avoid memory issues + + for (int i = 0; i < tList.size(); i++) { + E entity = getMapper().map(tList.get(i), dbEntityGenericClass); + em.persist(entity); + result.add(getMapper().map(entity, thriftGenericClass)); + + // Flush and clear every batchSize entities to manage memory + if ((i + 1) % batchSize == 0) { + em.flush(); + em.clear(); + } + } + + // Final flush for remaining entities + if (tList.size() % batchSize != 0) { + em.flush(); + } + + return result; } @Transactional @@ -78,12 +108,40 @@ public abstract class AbstractRepository<T, E, Id> { return getMapper().map(persistedCopy, thriftGenericClass); } - // FIXME do a bulk update + /** + * Bulk update entities. Uses batch processing for better performance. + * + * @param tList List of Thrift models to update + * @return List of updated Thrift models + * @throws SharingRegistryException if the operation fails + */ @Transactional public List<T> update(List<T> tList) throws SharingRegistryException { - List<T> returnList = new ArrayList<>(); - for (T temp : tList) returnList.add(update(temp)); - return returnList; + if (tList == null || tList.isEmpty()) { + return new ArrayList<>(); + } + EntityManager em = getEntityManager(); + List<T> result = new ArrayList<>(); + int batchSize = 50; // Process in batches to avoid memory issues + + for (int i = 0; i < tList.size(); i++) { + E entity = getMapper().map(tList.get(i), dbEntityGenericClass); + E merged = em.merge(entity); + result.add(getMapper().map(merged, thriftGenericClass)); + + // Flush and clear every batchSize entities to manage memory + if ((i + 1) % batchSize == 0) { + em.flush(); + em.clear(); + } + } + + // Final flush for remaining entities + if (tList.size() % batchSize != 0) { + em.flush(); + } + + return result; } @Transactional
