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

Reply via email to