diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
deleted file mode 100644
index dd55441296a..00000000000
--- a/.devcontainer/Dockerfile
+++ /dev/null
@@ -1,70 +0,0 @@
-FROM ubuntu:22.04 as deps
-
-ARG TARGETARCH
-
-# Avoid interactive prompts during package installation
-ENV DEBIAN_FRONTEND=noninteractive
-
-# Install system dependencies, python 3.10, and java 17
-RUN apt-get update && \
- apt-get install -y \
- curl wget git build-essential pkg-config libssl-dev libffi-dev software-properties-common \
- python3.10 python3.10-dev \
- openjdk-17-jdk \
- && rm -rf /var/lib/apt/lists/*
-
-# Install maven 3.9.10
-RUN wget https://dlcdn.apache.org/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.tar.gz \
- && tar -xzf apache-maven-3.9.10-bin.tar.gz \
- && mv apache-maven-3.9.10 /opt/maven \
- && rm apache-maven-3.9.10-bin.tar.gz
-
-# Install go 1.23.0
-RUN wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz \
- && tar -C /usr/local -xzf go1.23.0.linux-amd64.tar.gz \
- && rm go1.23.0.linux-amd64.tar.gz
-
-# Install Thrift 0.22
-RUN wget https://dlcdn.apache.org/thrift/0.22.0/thrift-0.22.0.tar.gz \
- && tar -xzf thrift-0.22.0.tar.gz \
- && cd thrift-0.22.0 \
- && ./configure --without-rs --enable-libs=no --enable-tests=no \
- && make -j$(nproc) \
- && make install \
- && cd .. \
- && rm -rf thrift-0.22.0 thrift-0.22.0.tar.gz
-
-# Create non-root user (developer)
-RUN useradd -m -s /bin/bash developer
-USER developer
-WORKDIR /home/developer
-
-# Set environment variables
-ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-$TARGETARCH
-ENV MAVEN_HOME=/opt/maven
-ENV GOROOT=/usr/local/go
-ENV GOPATH=/home/developer/go
-ENV PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin:$GOROOT/bin:$GOPATH/bin
-
-EXPOSE 7878 8000 17000 8050 8930 8960 8962 8970 18800 19900 18889 19908
-
-FROM deps AS compiler
-
-COPY --chown=developer:developer .. /home/developer/workspace
-WORKDIR /home/developer/workspace
-RUN mvn clean install -DskipTests
-
-
-FROM eclipse-temurin:17-jdk AS runner
-WORKDIR /app
-
-COPY --from=compiler /home/developer/workspace/distribution/*.tar.gz ./
-
-COPY ../dev-tools/deployment-scripts/*.sh ./
-COPY ../vault ./vault
-
-RUN chmod +x ./distribution_update.sh ./services_up.sh
-
-EXPOSE 7878 8000 17000 8050 8930 8960 8962 8970 18800 19900 18889 19908
-
-CMD ["sh", "-c", "./distribution_update.sh && ./services_up.sh && sleep infinity"]
diff --git a/.devcontainer/compose.yml b/.devcontainer/compose.yml
new file mode 100644
index 00000000000..0296c30fadd
--- /dev/null
+++ b/.devcontainer/compose.yml
@@ -0,0 +1,214 @@
+# Airavata development: core services + devcontainer
+# Credentials: .devcontainer/dev.env.defaults (single source of truth)
+# Shared config: conf/ at repo root (init-db, keycloak)
+#
+# ./scripts/run or init.sh [--clean] [--run] then dev.sh serve
+# Profiles: (none) = core | test = + slurm, sftp
+#
+# Local hostnames (add to /etc/hosts if desired):
+# 127.0.0.1 airavata.localhost api.airavata.localhost auth.airavata.localhost db.airavata.localhost temporal.airavata.localhost
+#
+# Service ports on host:
+# auth.airavata.localhost:18080 — Keycloak
+# api.airavata.localhost:8090 — API Server (run via mvn spring-boot:run)
+# db.airavata.localhost:13306 — MariaDB
+# temporal.airavata.localhost:7233/8233 — Temporal gRPC/UI
+# airavata.localhost:3000 — Portal (run via npm run dev)
+
+networks:
+ airavata-network:
+ driver: bridge
+
+volumes:
+ db_data:
+ slurm-data:
+ slurm-config:
+ slurm-logs:
+ sftp-data:
+ sftp-keys:
+
+services:
+ # ===========================================
+ # CORE SERVICES (always started)
+ # ===========================================
+
+ keycloak:
+ image: keycloak/keycloak:26.5
+ container_name: keycloak
+ restart: always
+ environment:
+ KEYCLOAK_ADMIN: admin
+ KEYCLOAK_ADMIN_PASSWORD: admin
+ volumes:
+ - ../conf/keycloak/keycloak.conf:/opt/keycloak/conf/keycloak.conf
+ command: ["start"]
+ networks:
+ - airavata-network
+ ports:
+ - "18080:18080"
+ healthcheck:
+ test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/localhost/18080'"]
+ interval: 10s
+ timeout: 5s
+ retries: 10
+ start_period: 30s
+
+ keycloak-setup:
+ image: alpine:3.19
+ container_name: keycloak-setup
+ env_file:
+ - ./dev.env.defaults
+ depends_on:
+ keycloak:
+ condition: service_healthy
+ environment:
+ KEYCLOAK_URL: http://keycloak:18080
+ KEYCLOAK_INIT_WIPE: "true"
+ # INCLUDE_CILOGON: "true"
+ # CILOGON_CLIENT_ID: "cilogon:/client_id/..."
+ # CILOGON_CLIENT_SECRET: "..."
+ # INCLUDE_SIDECAR_AGENT: "true"
+ # INCLUDE_JUPYTERLAB: "true"
+ # JUPYTERLAB_CLIENT_SECRET: "..."
+ volumes:
+ - ../conf/keycloak/setup-keycloak.sh:/setup-keycloak.sh:ro
+ entrypoint: ["/bin/sh", "-c"]
+ command: ["apk add --no-cache bash curl jq >/dev/null 2>&1 && bash /setup-keycloak.sh"]
+ networks:
+ - airavata-network
+ restart: "no"
+
+ db:
+ image: mariadb:11.7
+ container_name: db
+ restart: always
+ environment:
+ MYSQL_ROOT_PASSWORD: 123456
+ MYSQL_DATABASE: airavata
+ MYSQL_USER: airavata
+ MYSQL_PASSWORD: 123456
+ volumes:
+ - db_data:/var/lib/mysql
+ - ../conf/init-db:/docker-entrypoint-initdb.d:ro
+ command:
+ - mariadbd
+ - --character-set-server=utf8mb4
+ - --collation-server=utf8mb4_unicode_ci
+ - --sql-mode=NO_ENGINE_SUBSTITUTION
+ networks:
+ - airavata-network
+ ports:
+ - "13306:3306"
+ healthcheck:
+ test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "-u", "airavata", "-p123456"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 30s
+
+ temporal:
+ image: temporalio/admin-tools:latest
+ container_name: temporal
+ restart: always
+ entrypoint: ["temporal"]
+ command: ["server", "start-dev", "--ip", "0.0.0.0", "--db-filename", "/tmp/temporal.db"]
+ networks:
+ - airavata-network
+ ports:
+ - "7233:7233"
+ - "8233:8233"
+ healthcheck:
+ test: ["CMD", "temporal", "operator", "cluster", "health"]
+ interval: 10s
+ timeout: 5s
+ retries: 10
+ start_period: 15s
+
+ adminer:
+ image: adminer:latest
+ container_name: adminer
+ restart: always
+ depends_on:
+ db:
+ condition: service_healthy
+ environment:
+ ADMINER_DEFAULT_SERVER: db
+ networks:
+ - airavata-network
+ ports:
+ - "8081:8080"
+
+ devcontainer:
+ image: mcr.microsoft.com/devcontainers/base:ubuntu
+ container_name: airavata-devcontainer
+ depends_on:
+ db:
+ condition: service_healthy
+ keycloak:
+ condition: service_healthy
+ temporal:
+ condition: service_healthy
+ volumes:
+ - ..:/home/developer/workspace:cached
+ working_dir: /home/developer/workspace
+ network_mode: host
+ command: sleep infinity
+
+ # ===========================================
+ # TEST PROFILE SERVICES
+ # ===========================================
+ # Start with: docker compose --profile test up
+
+ slurm-test-cluster:
+ build:
+ context: ./test-infra/slurm
+ dockerfile: Dockerfile
+ image: airavata-test-slurm:latest
+ container_name: airavata-test-slurm
+ hostname: slurm-test-cluster
+ profiles:
+ - test
+ ports:
+ - "10022:22"
+ - "6817:6817"
+ - "6818:6818"
+ environment:
+ SLURM_CONF: /etc/slurm/slurm.conf
+ volumes:
+ - slurm-data:/var/lib/slurm
+ - slurm-config:/etc/slurm
+ - slurm-logs:/var/log/slurm
+ networks:
+ - airavata-network
+ healthcheck:
+ test: ["CMD", "sinfo", "--version"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ command: /usr/sbin/slurmd -D
+
+ sftp-test-server:
+ build:
+ context: ./test-infra/sftp
+ dockerfile: Dockerfile
+ image: airavata-test-sftp:latest
+ container_name: airavata-test-sftp
+ hostname: sftp-test-server
+ profiles:
+ - test
+ ports:
+ - "10023:22"
+ environment:
+ SFTP_USER: testuser
+ SFTP_PASSWORD: testpass
+ SFTP_ROOT: /home/testuser/data
+ volumes:
+ - sftp-data:/home/testuser/data
+ - sftp-keys:/home/testuser/.ssh
+ networks:
+ - airavata-network
+ healthcheck:
+ test: ["CMD", "nc", "-z", "localhost", "22"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
diff --git a/.devcontainer/database_scripts/init/00-accounts.sql b/.devcontainer/database_scripts/init/00-accounts.sql
deleted file mode 100644
index 0d57e5e080b..00000000000
--- a/.devcontainer/database_scripts/init/00-accounts.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-CREATE USER IF NOT EXISTS 'airavata'@'%' IDENTIFIED BY '123456';
-GRANT ALL PRIVILEGES ON *.* TO 'airavata'@'%';
-FLUSH PRIVILEGES;
\ No newline at end of file
diff --git a/.devcontainer/database_scripts/init/01-databases.sql b/.devcontainer/database_scripts/init/01-databases.sql
deleted file mode 100644
index 0f253c84308..00000000000
--- a/.devcontainer/database_scripts/init/01-databases.sql
+++ /dev/null
@@ -1,3617 +0,0 @@
--- MySQL dump 10.13 Distrib 5.7.21, for Linux (x86_64)
---
--- Host: localhost Database: app_catalog
--- ------------------------------------------------------
--- Server version 5.7.21
-
-/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
-/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
-/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
-/*!40101 SET NAMES utf8 */;
-/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
-/*!40103 SET TIME_ZONE='+00:00' */;
-/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
-/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
-/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
-/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-
---
--- Current Database: `app_catalog`
---
-
-CREATE DATABASE /*!32312 IF NOT EXISTS*/ `app_catalog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
-
-USE `app_catalog`;
-
---
--- Table structure for table `APPLICATION_DEPLOYMENT`
---
-
-DROP TABLE IF EXISTS `APPLICATION_DEPLOYMENT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `APPLICATION_DEPLOYMENT` (
- `DEPLOYMENT_ID` varchar(255) NOT NULL,
- `APP_MODULE_ID` varchar(255) NOT NULL,
- `COMPUTE_HOSTID` varchar(255) NOT NULL,
- `EXECUTABLE_PATH` varchar(255) DEFAULT NULL,
- `PARALLELISM` varchar(255) DEFAULT NULL,
- `APPLICATION_DESC` varchar(255) DEFAULT NULL,
- `ENV_MODULE_LOAD_CMD` varchar(255) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `GATEWAY_ID` varchar(255) NOT NULL,
- `DEFAULT_QUEUE_NAME` varchar(255) DEFAULT NULL,
- `DEFAULT_NODE_COUNT` int(11) DEFAULT NULL,
- `DEFAULT_CPU_COUNT` int(11) DEFAULT NULL,
- `DEFAULT_WALLTIME` int(11) DEFAULT NULL,
- `EDITABLE_BY_USER` tinyint(1) DEFAULT NULL,
- PRIMARY KEY (`DEPLOYMENT_ID`),
- KEY `COMPUTE_HOSTID` (`COMPUTE_HOSTID`),
- KEY `APP_MODULE_ID` (`APP_MODULE_ID`),
- CONSTRAINT `application_deployment_ibfk_1` FOREIGN KEY (`COMPUTE_HOSTID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE,
- CONSTRAINT `application_deployment_ibfk_2` FOREIGN KEY (`APP_MODULE_ID`) REFERENCES `APPLICATION_MODULE` (`MODULE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `APPLICATION_DEPLOYMENT`
---
-
-LOCK TABLES `APPLICATION_DEPLOYMENT` WRITE;
-/*!40000 ALTER TABLE `APPLICATION_DEPLOYMENT` DISABLE KEYS */;
-INSERT INTO `APPLICATION_DEPLOYMENT` VALUES ('js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84','Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84','js-156-93.jetstream-cloud.org_f0813875-de5b-4abb-b56f-eb9237809c61','/home/grid_user/apps/echo_wrapper.sh','SERIAL','',NULL,'2019-02-26 10:42:01','2019-02-26 10:42:01','default','cloud',1,2,0,0);
-/*!40000 ALTER TABLE `APPLICATION_DEPLOYMENT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `APPLICATION_INPUT`
---
-
-DROP TABLE IF EXISTS `APPLICATION_INPUT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `APPLICATION_INPUT` (
- `INTERFACE_ID` varchar(255) NOT NULL,
- `INPUT_KEY` varchar(255) NOT NULL,
- `INPUT_VALUE` varchar(255) DEFAULT NULL,
- `DATA_TYPE` varchar(255) DEFAULT NULL,
- `METADATA` varchar(4096) DEFAULT NULL,
- `APP_ARGUMENT` varchar(255) DEFAULT NULL,
- `STANDARD_INPUT` smallint(6) DEFAULT NULL,
- `USER_FRIENDLY_DESC` varchar(255) DEFAULT NULL,
- `INPUT_ORDER` int(11) DEFAULT NULL,
- `IS_REQUIRED` smallint(6) DEFAULT NULL,
- `REQUIRED_TO_COMMANDLINE` smallint(6) DEFAULT NULL,
- `DATA_STAGED` smallint(6) DEFAULT NULL,
- `IS_READ_ONLY` smallint(6) DEFAULT NULL,
- PRIMARY KEY (`INTERFACE_ID`,`INPUT_KEY`),
- CONSTRAINT `application_input_ibfk_1` FOREIGN KEY (`INTERFACE_ID`) REFERENCES `APPLICATION_INTERFACE` (`INTERFACE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `APPLICATION_INPUT`
---
-
-LOCK TABLES `APPLICATION_INPUT` WRITE;
-/*!40000 ALTER TABLE `APPLICATION_INPUT` DISABLE KEYS */;
-INSERT INTO `APPLICATION_INPUT` VALUES ('Echo_661f23c7-eca7-49ba-987b-55f4202b60bb','Input-To-Echo','','STRING',NULL,'',0,'',0,1,1,0,0);
-/*!40000 ALTER TABLE `APPLICATION_INPUT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `APPLICATION_INTERFACE`
---
-
-DROP TABLE IF EXISTS `APPLICATION_INTERFACE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `APPLICATION_INTERFACE` (
- `INTERFACE_ID` varchar(255) NOT NULL,
- `APPLICATION_NAME` varchar(255) DEFAULT NULL,
- `APPLICATION_DESCRIPTION` varchar(500) DEFAULT NULL,
- `GATEWAY_ID` varchar(255) NOT NULL,
- `ARCHIVE_WORKING_DIRECTORY` smallint(6) DEFAULT NULL,
- `HAS_OPTIONAL_FILE_INPUTS` tinyint(1) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`INTERFACE_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `APPLICATION_INTERFACE`
---
-
-LOCK TABLES `APPLICATION_INTERFACE` WRITE;
-/*!40000 ALTER TABLE `APPLICATION_INTERFACE` DISABLE KEYS */;
-INSERT INTO `APPLICATION_INTERFACE` VALUES ('Echo_661f23c7-eca7-49ba-987b-55f4202b60bb','Echo','A Simple Echo Application','default',1,0,'2019-02-25 18:57:38','2019-02-25 18:57:38');
-/*!40000 ALTER TABLE `APPLICATION_INTERFACE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `APPLICATION_MODULE`
---
-
-DROP TABLE IF EXISTS `APPLICATION_MODULE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `APPLICATION_MODULE` (
- `MODULE_ID` varchar(255) NOT NULL,
- `MODULE_NAME` varchar(255) DEFAULT NULL,
- `MODULE_VERSION` varchar(255) DEFAULT NULL,
- `MODULE_DESC` varchar(500) DEFAULT NULL,
- `GATEWAY_ID` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`MODULE_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `APPLICATION_MODULE`
---
-
-LOCK TABLES `APPLICATION_MODULE` WRITE;
-/*!40000 ALTER TABLE `APPLICATION_MODULE` DISABLE KEYS */;
-INSERT INTO `APPLICATION_MODULE` VALUES ('Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84','Echo','','','default','2019-02-25 18:53:41','2019-02-25 18:53:41');
-/*!40000 ALTER TABLE `APPLICATION_MODULE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `APPLICATION_OUTPUT`
---
-
-DROP TABLE IF EXISTS `APPLICATION_OUTPUT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `APPLICATION_OUTPUT` (
- `INTERFACE_ID` varchar(255) NOT NULL,
- `OUTPUT_KEY` varchar(255) NOT NULL,
- `OUTPUT_VALUE` varchar(255) DEFAULT NULL,
- `DATA_TYPE` varchar(255) DEFAULT NULL,
- `IS_REQUIRED` smallint(6) DEFAULT NULL,
- `REQUIRED_TO_COMMANDLINE` smallint(6) DEFAULT NULL,
- `DATA_MOVEMENT` smallint(6) DEFAULT NULL,
- `DATA_NAME_LOCATION` varchar(255) DEFAULT NULL,
- `SEARCH_QUERY` varchar(255) DEFAULT NULL,
- `APP_ARGUMENT` varchar(255) DEFAULT NULL,
- `OUTPUT_STREAMING` smallint(6) DEFAULT NULL,
- `METADATA` varchar(4096) DEFAULT NULL,
- PRIMARY KEY (`INTERFACE_ID`,`OUTPUT_KEY`),
- CONSTRAINT `application_output_ibfk_1` FOREIGN KEY (`INTERFACE_ID`) REFERENCES `APPLICATION_INTERFACE` (`INTERFACE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `APPLICATION_OUTPUT`
---
-
-LOCK TABLES `APPLICATION_OUTPUT` WRITE;
-/*!40000 ALTER TABLE `APPLICATION_OUTPUT` DISABLE KEYS */;
-INSERT INTO `APPLICATION_OUTPUT` VALUES ('Echo_661f23c7-eca7-49ba-987b-55f4202b60bb','Echo-Standard-Error','','STDERR',1,0,0,NULL,'','',0, NULL),('Echo_661f23c7-eca7-49ba-987b-55f4202b60bb','Echo-Standard-Out','','STDOUT',1,0,0,NULL,'','',0, NULL);
-/*!40000 ALTER TABLE `APPLICATION_OUTPUT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `APP_ENVIRONMENT`
---
-
-DROP TABLE IF EXISTS `APP_ENVIRONMENT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `APP_ENVIRONMENT` (
- `DEPLOYMENT_ID` varchar(255) NOT NULL,
- `NAME` varchar(255) NOT NULL,
- `VALUE` varchar(255) DEFAULT NULL,
- `ENV_ORDER` int(11) DEFAULT NULL,
- PRIMARY KEY (`DEPLOYMENT_ID`,`NAME`),
- CONSTRAINT `app_environment_ibfk_1` FOREIGN KEY (`DEPLOYMENT_ID`) REFERENCES `APPLICATION_DEPLOYMENT` (`DEPLOYMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `APP_ENVIRONMENT`
---
-
-LOCK TABLES `APP_ENVIRONMENT` WRITE;
-/*!40000 ALTER TABLE `APP_ENVIRONMENT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `APP_ENVIRONMENT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `APP_MODULE_MAPPING`
---
-
-DROP TABLE IF EXISTS `APP_MODULE_MAPPING`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `APP_MODULE_MAPPING` (
- `INTERFACE_ID` varchar(255) NOT NULL,
- `MODULE_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`INTERFACE_ID`,`MODULE_ID`),
- KEY `MODULE_ID` (`MODULE_ID`),
- CONSTRAINT `app_module_mapping_ibfk_1` FOREIGN KEY (`INTERFACE_ID`) REFERENCES `APPLICATION_INTERFACE` (`INTERFACE_ID`) ON DELETE CASCADE,
- CONSTRAINT `app_module_mapping_ibfk_2` FOREIGN KEY (`MODULE_ID`) REFERENCES `APPLICATION_MODULE` (`MODULE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `APP_MODULE_MAPPING`
---
-
-LOCK TABLES `APP_MODULE_MAPPING` WRITE;
-/*!40000 ALTER TABLE `APP_MODULE_MAPPING` DISABLE KEYS */;
-INSERT INTO `APP_MODULE_MAPPING` VALUES ('Echo_661f23c7-eca7-49ba-987b-55f4202b60bb','Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84');
-/*!40000 ALTER TABLE `APP_MODULE_MAPPING` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `BATCH_QUEUE`
---
-
-DROP TABLE IF EXISTS `BATCH_QUEUE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `BATCH_QUEUE` (
- `COMPUTE_RESOURCE_ID` varchar(255) NOT NULL,
- `MAX_RUNTIME` int(11) DEFAULT NULL,
- `MAX_JOB_IN_QUEUE` int(11) DEFAULT NULL,
- `QUEUE_DESCRIPTION` varchar(255) DEFAULT NULL,
- `QUEUE_NAME` varchar(255) NOT NULL,
- `MAX_PROCESSORS` int(11) DEFAULT NULL,
- `MAX_NODES` int(11) DEFAULT NULL,
- `MAX_MEMORY` int(11) DEFAULT NULL,
- `CPU_PER_NODE` int(11) DEFAULT NULL,
- `DEFAULT_NODE_COUNT` int(11) DEFAULT NULL,
- `DEFAULT_CPU_COUNT` int(11) DEFAULT NULL,
- `DEFAULT_WALLTIME` int(11) DEFAULT NULL,
- `QUEUE_SPECIFIC_MACROS` varchar(255) DEFAULT NULL,
- `IS_DEFAULT_QUEUE` tinyint(1) DEFAULT NULL,
- PRIMARY KEY (`COMPUTE_RESOURCE_ID`,`QUEUE_NAME`),
- CONSTRAINT `batch_queue_ibfk_1` FOREIGN KEY (`COMPUTE_RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `BATCH_QUEUE`
---
-
-LOCK TABLES `BATCH_QUEUE` WRITE;
-/*!40000 ALTER TABLE `BATCH_QUEUE` DISABLE KEYS */;
-INSERT INTO `BATCH_QUEUE` VALUES ('js-156-93.jetstream-cloud.org_f0813875-de5b-4abb-b56f-eb9237809c61',180,300,'cloud partition','cloud',4,2,0,2,1,2,30,'',0);
-/*!40000 ALTER TABLE `BATCH_QUEUE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `BATCH_QUEUE_RESOURCE_POLICY`
---
-
-DROP TABLE IF EXISTS `BATCH_QUEUE_RESOURCE_POLICY`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `BATCH_QUEUE_RESOURCE_POLICY` (
- `RESOURCE_POLICY_ID` varchar(255) NOT NULL,
- `COMPUTE_RESOURCE_ID` varchar(255) NOT NULL,
- `GROUP_RESOURCE_PROFILE_ID` varchar(255) NOT NULL,
- `QUEUE_NAME` varchar(255) NOT NULL,
- `MAX_ALLOWED_NODES` int(11) DEFAULT NULL,
- `MAX_ALLOWED_CORES` int(11) DEFAULT NULL,
- `MAX_ALLOWED_WALLTIME` int(11) DEFAULT NULL,
- PRIMARY KEY (`RESOURCE_POLICY_ID`),
- KEY `COMPUTE_RESOURCE_ID` (`COMPUTE_RESOURCE_ID`),
- KEY `GROUP_RESOURCE_PROFILE_ID` (`GROUP_RESOURCE_PROFILE_ID`),
- CONSTRAINT `batch_queue_resource_policy_ibfk_1` FOREIGN KEY (`COMPUTE_RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE,
- CONSTRAINT `batch_queue_resource_policy_ibfk_2` FOREIGN KEY (`GROUP_RESOURCE_PROFILE_ID`) REFERENCES `GROUP_RESOURCE_PROFILE` (`GROUP_RESOURCE_PROFILE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `BATCH_QUEUE_RESOURCE_POLICY`
---
-
-LOCK TABLES `BATCH_QUEUE_RESOURCE_POLICY` WRITE;
-/*!40000 ALTER TABLE `BATCH_QUEUE_RESOURCE_POLICY` DISABLE KEYS */;
-/*!40000 ALTER TABLE `BATCH_QUEUE_RESOURCE_POLICY` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `CLOUD_JOB_SUBMISSION`
---
-
-DROP TABLE IF EXISTS `CLOUD_JOB_SUBMISSION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `CLOUD_JOB_SUBMISSION` (
- `JOB_SUBMISSION_INTERFACE_ID` varchar(255) NOT NULL,
- `EXECUTABLE_TYPE` varchar(255) DEFAULT NULL,
- `NODE_ID` varchar(255) DEFAULT NULL,
- `PROVIDER_NAME` varchar(255) DEFAULT NULL,
- `SECURITY_PROTOCOL` varchar(255) DEFAULT NULL,
- `USER_ACCOUNT_NAME` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`JOB_SUBMISSION_INTERFACE_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `CLOUD_JOB_SUBMISSION`
---
-
-LOCK TABLES `CLOUD_JOB_SUBMISSION` WRITE;
-/*!40000 ALTER TABLE `CLOUD_JOB_SUBMISSION` DISABLE KEYS */;
-/*!40000 ALTER TABLE `CLOUD_JOB_SUBMISSION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `COMPUTE_RESOURCE`
---
-
-DROP TABLE IF EXISTS `COMPUTE_RESOURCE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `COMPUTE_RESOURCE` (
- `RESOURCE_ID` varchar(255) NOT NULL,
- `HOST_NAME` varchar(255) NOT NULL,
- `RESOURCE_DESCRIPTION` varchar(255) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `MAX_MEMORY_NODE` int(11) DEFAULT NULL,
- `CPUS_PER_NODE` int(11) DEFAULT NULL,
- `DEFAULT_NODE_COUNT` int(11) DEFAULT NULL,
- `DEFAULT_CPU_COUNT` int(11) DEFAULT NULL,
- `DEFAULT_WALLTIME` int(11) DEFAULT NULL,
- `ENABLED` smallint(6) DEFAULT NULL,
- `GATEWAY_USAGE_REPORTING` tinyint(1) DEFAULT NULL,
- `GATEWAY_USAGE_MODULE_LOAD_CMD` varchar(500) DEFAULT NULL,
- `GATEWAY_USAGE_EXECUTABLE` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`RESOURCE_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `COMPUTE_RESOURCE`
---
-
-LOCK TABLES `COMPUTE_RESOURCE` WRITE;
-/*!40000 ALTER TABLE `COMPUTE_RESOURCE` DISABLE KEYS */;
-INSERT INTO `COMPUTE_RESOURCE` VALUES ('js-156-93.jetstream-cloud.org_f0813875-de5b-4abb-b56f-eb9237809c61','js-156-93.jetstream-cloud.org','','2019-02-25 23:50:45','2019-02-25 23:50:45',0,NULL,NULL,NULL,NULL,1,0,NULL,NULL);
-/*!40000 ALTER TABLE `COMPUTE_RESOURCE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `COMPUTE_RESOURCE_FILE_SYSTEM`
---
-
-DROP TABLE IF EXISTS `COMPUTE_RESOURCE_FILE_SYSTEM`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `COMPUTE_RESOURCE_FILE_SYSTEM` (
- `COMPUTE_RESOURCE_ID` varchar(255) NOT NULL,
- `PATH` varchar(255) DEFAULT NULL,
- `FILE_SYSTEM` varchar(255) NOT NULL,
- PRIMARY KEY (`COMPUTE_RESOURCE_ID`,`FILE_SYSTEM`),
- CONSTRAINT `compute_resource_file_system_ibfk_1` FOREIGN KEY (`COMPUTE_RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `COMPUTE_RESOURCE_FILE_SYSTEM`
---
-
-LOCK TABLES `COMPUTE_RESOURCE_FILE_SYSTEM` WRITE;
-/*!40000 ALTER TABLE `COMPUTE_RESOURCE_FILE_SYSTEM` DISABLE KEYS */;
-/*!40000 ALTER TABLE `COMPUTE_RESOURCE_FILE_SYSTEM` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `COMPUTE_RESOURCE_POLICY`
---
-
-DROP TABLE IF EXISTS `COMPUTE_RESOURCE_POLICY`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `COMPUTE_RESOURCE_POLICY` (
- `RESOURCE_POLICY_ID` varchar(255) NOT NULL,
- `COMPUTE_RESOURCE_ID` varchar(255) NOT NULL,
- `GROUP_RESOURCE_PROFILE_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`RESOURCE_POLICY_ID`),
- KEY `COMPUTE_RESOURCE_ID` (`COMPUTE_RESOURCE_ID`),
- KEY `GROUP_RESOURCE_PROFILE_ID` (`GROUP_RESOURCE_PROFILE_ID`),
- CONSTRAINT `compute_resource_policy_ibfk_1` FOREIGN KEY (`COMPUTE_RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE,
- CONSTRAINT `compute_resource_policy_ibfk_2` FOREIGN KEY (`GROUP_RESOURCE_PROFILE_ID`) REFERENCES `GROUP_RESOURCE_PROFILE` (`GROUP_RESOURCE_PROFILE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `COMPUTE_RESOURCE_POLICY`
---
-
-LOCK TABLES `COMPUTE_RESOURCE_POLICY` WRITE;
-/*!40000 ALTER TABLE `COMPUTE_RESOURCE_POLICY` DISABLE KEYS */;
-INSERT INTO `COMPUTE_RESOURCE_POLICY` VALUES ('ae1b950e-b714-4d59-949b-338c6d6cf640','js-156-93.jetstream-cloud.org_f0813875-de5b-4abb-b56f-eb9237809c61','af03c63d-a40e-4ed1-aee0-759a6ed0202c');
-/*!40000 ALTER TABLE `COMPUTE_RESOURCE_POLICY` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `COMPUTE_RESOURCE_POLICY_QUEUES`
---
-
-DROP TABLE IF EXISTS `COMPUTE_RESOURCE_POLICY_QUEUES`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `COMPUTE_RESOURCE_POLICY_QUEUES` (
- `RESOURCE_POLICY_ID` varchar(255) NOT NULL,
- `QUEUE_NAME` varchar(255) NOT NULL,
- PRIMARY KEY (`RESOURCE_POLICY_ID`,`QUEUE_NAME`),
- CONSTRAINT `compute_resource_policy_queues_ibfk_1` FOREIGN KEY (`RESOURCE_POLICY_ID`) REFERENCES `COMPUTE_RESOURCE_POLICY` (`RESOURCE_POLICY_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `COMPUTE_RESOURCE_POLICY_QUEUES`
---
-
-LOCK TABLES `COMPUTE_RESOURCE_POLICY_QUEUES` WRITE;
-/*!40000 ALTER TABLE `COMPUTE_RESOURCE_POLICY_QUEUES` DISABLE KEYS */;
-INSERT INTO `COMPUTE_RESOURCE_POLICY_QUEUES` VALUES ('ae1b950e-b714-4d59-949b-338c6d6cf640','cloud');
-/*!40000 ALTER TABLE `COMPUTE_RESOURCE_POLICY_QUEUES` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `COMPUTE_RESOURCE_PREFERENCE`
---
-
-DROP TABLE IF EXISTS `COMPUTE_RESOURCE_PREFERENCE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `COMPUTE_RESOURCE_PREFERENCE` (
- `GATEWAY_ID` varchar(255) NOT NULL,
- `RESOURCE_ID` varchar(255) NOT NULL,
- `OVERRIDE_BY_AIRAVATA` smallint(6) DEFAULT NULL,
- `PREFERED_JOB_SUB_PROTOCOL` varchar(255) DEFAULT NULL,
- `PREFERED_DATA_MOVE_PROTOCOL` varchar(255) DEFAULT NULL,
- `PREFERED_BATCH_QUEUE` varchar(255) DEFAULT NULL,
- `SCRATCH_LOCATION` varchar(255) DEFAULT NULL,
- `ALLOCATION_PROJECT_NUMBER` varchar(255) DEFAULT NULL,
- `LOGIN_USERNAME` varchar(255) DEFAULT NULL,
- `RESOURCE_CS_TOKEN` varchar(255) DEFAULT NULL,
- `USAGE_REPORTING_GATEWAY_ID` varchar(255) DEFAULT NULL,
- `QUALITY_OF_SERVICE` varchar(255) DEFAULT NULL,
- `RESERVATION` varchar(255) DEFAULT NULL,
- `RESERVATION_START_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `RESERVATION_END_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `SSH_ACCOUNT_PROVISIONER` varchar(255) DEFAULT NULL,
- `SSH_ACCOUNT_PROVISIONER_ADDITIONAL_INFO` varchar(1000) DEFAULT NULL,
- PRIMARY KEY (`GATEWAY_ID`,`RESOURCE_ID`),
- KEY `RESOURCE_ID` (`RESOURCE_ID`),
- CONSTRAINT `compute_resource_preference_ibfk_1` FOREIGN KEY (`RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE,
- CONSTRAINT `compute_resource_preference_ibfk_2` FOREIGN KEY (`GATEWAY_ID`) REFERENCES `GATEWAY_PROFILE` (`GATEWAY_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `COMPUTE_RESOURCE_PREFERENCE`
---
-
-LOCK TABLES `COMPUTE_RESOURCE_PREFERENCE` WRITE;
-/*!40000 ALTER TABLE `COMPUTE_RESOURCE_PREFERENCE` DISABLE KEYS */;
-INSERT INTO `COMPUTE_RESOURCE_PREFERENCE` VALUES ('default','js-156-93.jetstream-cloud.org_f0813875-de5b-4abb-b56f-eb9237809c61',1,'SSH','SCP','cloud','/home/grid_user/airavata-workdirs','','grid_user','46a99a5a-8b55-4982-bfd7-90fe72b00d46',NULL,'','','2010-12-31 19:00:00','2030-12-31 19:00:00',NULL,NULL);
-/*!40000 ALTER TABLE `COMPUTE_RESOURCE_PREFERENCE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `CONFIGURATION`
---
-
-DROP TABLE IF EXISTS `CONFIGURATION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `CONFIGURATION` (
- `CONFIG_KEY` varchar(255) NOT NULL,
- `CONFIG_VAL` varchar(255) NOT NULL,
- PRIMARY KEY (`CONFIG_KEY`,`CONFIG_VAL`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `CONFIGURATION`
---
-
-LOCK TABLES `CONFIGURATION` WRITE;
-/*!40000 ALTER TABLE `CONFIGURATION` DISABLE KEYS */;
-INSERT INTO `CONFIGURATION` VALUES ('app_catalog_version','0.16');
-/*!40000 ALTER TABLE `CONFIGURATION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `DATA_MOVEMENT_INTERFACE`
---
-
-DROP TABLE IF EXISTS `DATA_MOVEMENT_INTERFACE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `DATA_MOVEMENT_INTERFACE` (
- `COMPUTE_RESOURCE_ID` varchar(255) NOT NULL,
- `DATA_MOVEMENT_PROTOCOL` varchar(255) NOT NULL,
- `DATA_MOVEMENT_INTERFACE_ID` varchar(255) NOT NULL,
- `PRIORITY_ORDER` int(11) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`COMPUTE_RESOURCE_ID`,`DATA_MOVEMENT_INTERFACE_ID`),
- CONSTRAINT `data_movement_interface_ibfk_1` FOREIGN KEY (`COMPUTE_RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `DATA_MOVEMENT_INTERFACE`
---
-
-LOCK TABLES `DATA_MOVEMENT_INTERFACE` WRITE;
-/*!40000 ALTER TABLE `DATA_MOVEMENT_INTERFACE` DISABLE KEYS */;
-INSERT INTO `DATA_MOVEMENT_INTERFACE` VALUES ('js-156-93.jetstream-cloud.org_f0813875-de5b-4abb-b56f-eb9237809c61','SCP','SCP_1e775974-c405-4793-9e6c-a88d98013a28',0,'2019-02-25 23:53:15','2019-02-25 23:53:15');
-/*!40000 ALTER TABLE `DATA_MOVEMENT_INTERFACE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GATEWAY_GROUPS`
---
-
-DROP TABLE IF EXISTS `GATEWAY_GROUPS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GATEWAY_GROUPS` (
- `GATEWAY_ID` varchar(255) NOT NULL,
- `ADMINS_GROUP_ID` varchar(255) DEFAULT NULL,
- `READ_ONLY_ADMINS_GROUP_ID` varchar(255) DEFAULT NULL,
- `DEFAULT_GATEWAY_USERS_GROUP_ID` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`GATEWAY_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GATEWAY_GROUPS`
---
-
-LOCK TABLES `GATEWAY_GROUPS` WRITE;
-/*!40000 ALTER TABLE `GATEWAY_GROUPS` DISABLE KEYS */;
-INSERT INTO `GATEWAY_GROUPS` VALUES ('default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','Gateway_Users_2e4e6eb4-05ad-4a0f-92d1-02aa26c56004');
-/*!40000 ALTER TABLE `GATEWAY_GROUPS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GATEWAY_PROFILE`
---
-
-DROP TABLE IF EXISTS `GATEWAY_PROFILE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GATEWAY_PROFILE` (
- `GATEWAY_ID` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `CS_TOKEN` varchar(255) DEFAULT NULL,
- `IDENTITY_SERVER_TENANT` varchar(255) DEFAULT NULL,
- `IDENTITY_SERVER_PWD_CRED_TOKEN` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`GATEWAY_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GATEWAY_PROFILE`
---
-
-LOCK TABLES `GATEWAY_PROFILE` WRITE;
-/*!40000 ALTER TABLE `GATEWAY_PROFILE` DISABLE KEYS */;
-INSERT INTO `GATEWAY_PROFILE` VALUES ('default','2019-02-26 15:15:34','2019-02-26 10:15:34',NULL,'default','daf89639-bf92-4161-9c54-34571372d092');
-/*!40000 ALTER TABLE `GATEWAY_PROFILE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GLOBUS_GK_ENDPOINT`
---
-
-DROP TABLE IF EXISTS `GLOBUS_GK_ENDPOINT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GLOBUS_GK_ENDPOINT` (
- `SUBMISSION_ID` varchar(255) NOT NULL,
- `ENDPOINT` varchar(255) NOT NULL,
- PRIMARY KEY (`SUBMISSION_ID`,`ENDPOINT`),
- CONSTRAINT `globus_gk_endpoint_ibfk_1` FOREIGN KEY (`SUBMISSION_ID`) REFERENCES `GLOBUS_SUBMISSION` (`SUBMISSION_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GLOBUS_GK_ENDPOINT`
---
-
-LOCK TABLES `GLOBUS_GK_ENDPOINT` WRITE;
-/*!40000 ALTER TABLE `GLOBUS_GK_ENDPOINT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GLOBUS_GK_ENDPOINT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GLOBUS_SUBMISSION`
---
-
-DROP TABLE IF EXISTS `GLOBUS_SUBMISSION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GLOBUS_SUBMISSION` (
- `SUBMISSION_ID` varchar(255) NOT NULL,
- `RESOURCE_JOB_MANAGER` varchar(255) DEFAULT NULL,
- `SECURITY_PROTOCAL` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`SUBMISSION_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GLOBUS_SUBMISSION`
---
-
-LOCK TABLES `GLOBUS_SUBMISSION` WRITE;
-/*!40000 ALTER TABLE `GLOBUS_SUBMISSION` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GLOBUS_SUBMISSION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GRIDFTP_DATA_MOVEMENT`
---
-
-DROP TABLE IF EXISTS `GRIDFTP_DATA_MOVEMENT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GRIDFTP_DATA_MOVEMENT` (
- `DATA_MOVEMENT_INTERFACE_ID` varchar(255) NOT NULL,
- `SECURITY_PROTOCOL` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`DATA_MOVEMENT_INTERFACE_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GRIDFTP_DATA_MOVEMENT`
---
-
-LOCK TABLES `GRIDFTP_DATA_MOVEMENT` WRITE;
-/*!40000 ALTER TABLE `GRIDFTP_DATA_MOVEMENT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GRIDFTP_DATA_MOVEMENT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GRIDFTP_ENDPOINT`
---
-
-DROP TABLE IF EXISTS `GRIDFTP_ENDPOINT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GRIDFTP_ENDPOINT` (
- `ENDPOINT` varchar(255) NOT NULL,
- `DATA_MOVEMENT_INTERFACE_ID` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`DATA_MOVEMENT_INTERFACE_ID`,`ENDPOINT`),
- CONSTRAINT `gridftp_endpoint_ibfk_1` FOREIGN KEY (`DATA_MOVEMENT_INTERFACE_ID`) REFERENCES `GRIDFTP_DATA_MOVEMENT` (`DATA_MOVEMENT_INTERFACE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GRIDFTP_ENDPOINT`
---
-
-LOCK TABLES `GRIDFTP_ENDPOINT` WRITE;
-/*!40000 ALTER TABLE `GRIDFTP_ENDPOINT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GRIDFTP_ENDPOINT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GROUP_COMPUTE_RESOURCE_PREFERENCE`
---
-
-DROP TABLE IF EXISTS `GROUP_COMPUTE_RESOURCE_PREFERENCE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GROUP_COMPUTE_RESOURCE_PREFERENCE` (
- `RESOURCE_ID` varchar(255) NOT NULL,
- `GROUP_RESOURCE_PROFILE_ID` varchar(255) NOT NULL,
- `OVERRIDE_BY_AIRAVATA` smallint(6) DEFAULT NULL,
- `PREFERED_JOB_SUB_PROTOCOL` varchar(255) DEFAULT NULL,
- `PREFERED_DATA_MOVE_PROTOCOL` varchar(255) DEFAULT NULL,
- `PREFERED_BATCH_QUEUE` varchar(255) DEFAULT NULL,
- `SCRATCH_LOCATION` varchar(255) DEFAULT NULL,
- `ALLOCATION_PROJECT_NUMBER` varchar(255) DEFAULT NULL,
- `LOGIN_USERNAME` varchar(255) DEFAULT NULL,
- `RESOURCE_CS_TOKEN` varchar(255) DEFAULT NULL,
- `USAGE_REPORTING_GATEWAY_ID` varchar(255) DEFAULT NULL,
- `QUALITY_OF_SERVICE` varchar(255) DEFAULT NULL,
- `RESERVATION` varchar(255) DEFAULT NULL,
- `RESERVATION_START_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `RESERVATION_END_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `SSH_ACCOUNT_PROVISIONER` varchar(255) DEFAULT NULL,
- `SSH_ACCOUNT_PROVISIONER_ADDITIONAL_INFO` varchar(1000) DEFAULT NULL,
- PRIMARY KEY (`RESOURCE_ID`,`GROUP_RESOURCE_PROFILE_ID`),
- KEY `GROUP_RESOURCE_PROFILE_ID` (`GROUP_RESOURCE_PROFILE_ID`),
- CONSTRAINT `group_compute_resource_preference_ibfk_1` FOREIGN KEY (`RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE,
- CONSTRAINT `group_compute_resource_preference_ibfk_2` FOREIGN KEY (`GROUP_RESOURCE_PROFILE_ID`) REFERENCES `GROUP_RESOURCE_PROFILE` (`GROUP_RESOURCE_PROFILE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GROUP_COMPUTE_RESOURCE_PREFERENCE`
---
-
-LOCK TABLES `GROUP_COMPUTE_RESOURCE_PREFERENCE` WRITE;
-/*!40000 ALTER TABLE `GROUP_COMPUTE_RESOURCE_PREFERENCE` DISABLE KEYS */;
-INSERT INTO `GROUP_COMPUTE_RESOURCE_PREFERENCE` VALUES ('js-156-93.jetstream-cloud.org_f0813875-de5b-4abb-b56f-eb9237809c61','af03c63d-a40e-4ed1-aee0-759a6ed0202c',1,NULL,NULL,NULL,'/home/grid_user/airavata-workdirs',NULL,'grid_user',NULL,NULL,NULL,NULL,'2019-03-11 15:41:14','2019-03-11 15:41:14',NULL,NULL);
-/*!40000 ALTER TABLE `GROUP_COMPUTE_RESOURCE_PREFERENCE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GROUP_RESOURCE_PROFILE`
---
-
-DROP TABLE IF EXISTS `GROUP_RESOURCE_PROFILE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GROUP_RESOURCE_PROFILE` (
- `GATEWAY_ID` varchar(255) NOT NULL,
- `GROUP_RESOURCE_PROFILE_ID` varchar(255) NOT NULL,
- `GROUP_RESOURCE_PROFILE_NAME` varchar(255) DEFAULT NULL,
- `CREATION_TIME` bigint(20) NOT NULL,
- `UPDATE_TIME` bigint(20) NOT NULL,
- `DEFAULT_CREDENTIAL_STORE_TOKEN` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`GROUP_RESOURCE_PROFILE_ID`),
- UNIQUE KEY `GATEWAY_ID` (`GATEWAY_ID`,`GROUP_RESOURCE_PROFILE_NAME`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GROUP_RESOURCE_PROFILE`
---
-
-LOCK TABLES `GROUP_RESOURCE_PROFILE` WRITE;
-/*!40000 ALTER TABLE `GROUP_RESOURCE_PROFILE` DISABLE KEYS */;
-INSERT INTO `GROUP_RESOURCE_PROFILE` VALUES ('default','af03c63d-a40e-4ed1-aee0-759a6ed0202c','Default-Group-Resource-Profile',1552318871758,1552318871760,'46a99a5a-8b55-4982-bfd7-90fe72b00d46');
-/*!40000 ALTER TABLE `GROUP_RESOURCE_PROFILE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GRP_SSH_ACC_PROV_CONFIG`
---
-
-DROP TABLE IF EXISTS `GRP_SSH_ACC_PROV_CONFIG`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GRP_SSH_ACC_PROV_CONFIG` (
- `RESOURCE_ID` varchar(255) NOT NULL,
- `GROUP_RESOURCE_PROFILE_ID` varchar(255) NOT NULL,
- `CONFIG_NAME` varchar(255) NOT NULL,
- `CONFIG_VALUE` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`RESOURCE_ID`,`CONFIG_NAME`,`GROUP_RESOURCE_PROFILE_ID`),
- KEY `RESOURCE_ID` (`RESOURCE_ID`,`GROUP_RESOURCE_PROFILE_ID`),
- CONSTRAINT `grp_ssh_acc_prov_config_ibfk_1` FOREIGN KEY (`RESOURCE_ID`, `GROUP_RESOURCE_PROFILE_ID`) REFERENCES `GROUP_COMPUTE_RESOURCE_PREFERENCE` (`RESOURCE_ID`, `GROUP_RESOURCE_PROFILE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GRP_SSH_ACC_PROV_CONFIG`
---
-
-LOCK TABLES `GRP_SSH_ACC_PROV_CONFIG` WRITE;
-/*!40000 ALTER TABLE `GRP_SSH_ACC_PROV_CONFIG` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GRP_SSH_ACC_PROV_CONFIG` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GSISSH_EXPORT`
---
-
-DROP TABLE IF EXISTS `GSISSH_EXPORT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GSISSH_EXPORT` (
- `SUBMISSION_ID` varchar(255) NOT NULL,
- `EXPORT` varchar(255) NOT NULL,
- PRIMARY KEY (`SUBMISSION_ID`,`EXPORT`),
- CONSTRAINT `gsissh_export_ibfk_1` FOREIGN KEY (`SUBMISSION_ID`) REFERENCES `GSISSH_SUBMISSION` (`SUBMISSION_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GSISSH_EXPORT`
---
-
-LOCK TABLES `GSISSH_EXPORT` WRITE;
-/*!40000 ALTER TABLE `GSISSH_EXPORT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GSISSH_EXPORT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GSISSH_POSTJOBCOMMAND`
---
-
-DROP TABLE IF EXISTS `GSISSH_POSTJOBCOMMAND`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GSISSH_POSTJOBCOMMAND` (
- `SUBMISSION_ID` varchar(255) NOT NULL,
- `COMMAND` varchar(255) NOT NULL,
- PRIMARY KEY (`SUBMISSION_ID`,`COMMAND`),
- CONSTRAINT `gsissh_postjobcommand_ibfk_1` FOREIGN KEY (`SUBMISSION_ID`) REFERENCES `GSISSH_SUBMISSION` (`SUBMISSION_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GSISSH_POSTJOBCOMMAND`
---
-
-LOCK TABLES `GSISSH_POSTJOBCOMMAND` WRITE;
-/*!40000 ALTER TABLE `GSISSH_POSTJOBCOMMAND` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GSISSH_POSTJOBCOMMAND` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GSISSH_PREJOBCOMMAND`
---
-
-DROP TABLE IF EXISTS `GSISSH_PREJOBCOMMAND`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GSISSH_PREJOBCOMMAND` (
- `SUBMISSION_ID` varchar(255) NOT NULL,
- `COMMAND` varchar(255) NOT NULL,
- PRIMARY KEY (`SUBMISSION_ID`,`COMMAND`),
- CONSTRAINT `gsissh_prejobcommand_ibfk_1` FOREIGN KEY (`SUBMISSION_ID`) REFERENCES `GSISSH_SUBMISSION` (`SUBMISSION_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GSISSH_PREJOBCOMMAND`
---
-
-LOCK TABLES `GSISSH_PREJOBCOMMAND` WRITE;
-/*!40000 ALTER TABLE `GSISSH_PREJOBCOMMAND` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GSISSH_PREJOBCOMMAND` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GSISSH_SUBMISSION`
---
-
-DROP TABLE IF EXISTS `GSISSH_SUBMISSION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GSISSH_SUBMISSION` (
- `SUBMISSION_ID` varchar(255) NOT NULL,
- `RESOURCE_JOB_MANAGER` varchar(255) DEFAULT NULL,
- `SSH_PORT` int(11) DEFAULT NULL,
- `INSTALLED_PATH` varchar(255) DEFAULT NULL,
- `MONITOR_MODE` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`SUBMISSION_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GSISSH_SUBMISSION`
---
-
-LOCK TABLES `GSISSH_SUBMISSION` WRITE;
-/*!40000 ALTER TABLE `GSISSH_SUBMISSION` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GSISSH_SUBMISSION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `HOST_ALIAS`
---
-
-DROP TABLE IF EXISTS `HOST_ALIAS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `HOST_ALIAS` (
- `RESOURCE_ID` varchar(255) NOT NULL,
- `ALIAS` varchar(255) NOT NULL,
- PRIMARY KEY (`RESOURCE_ID`,`ALIAS`),
- CONSTRAINT `host_alias_ibfk_1` FOREIGN KEY (`RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `HOST_ALIAS`
---
-
-LOCK TABLES `HOST_ALIAS` WRITE;
-/*!40000 ALTER TABLE `HOST_ALIAS` DISABLE KEYS */;
-/*!40000 ALTER TABLE `HOST_ALIAS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `HOST_IPADDRESS`
---
-
-DROP TABLE IF EXISTS `HOST_IPADDRESS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `HOST_IPADDRESS` (
- `RESOURCE_ID` varchar(255) NOT NULL,
- `IP_ADDRESS` varchar(255) NOT NULL,
- PRIMARY KEY (`RESOURCE_ID`,`IP_ADDRESS`),
- CONSTRAINT `host_ipaddress_ibfk_1` FOREIGN KEY (`RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `HOST_IPADDRESS`
---
-
-LOCK TABLES `HOST_IPADDRESS` WRITE;
-/*!40000 ALTER TABLE `HOST_IPADDRESS` DISABLE KEYS */;
-/*!40000 ALTER TABLE `HOST_IPADDRESS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `JOB_MANAGER_COMMAND`
---
-
-DROP TABLE IF EXISTS `JOB_MANAGER_COMMAND`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `JOB_MANAGER_COMMAND` (
- `RESOURCE_JOB_MANAGER_ID` varchar(255) NOT NULL,
- `COMMAND_TYPE` varchar(255) NOT NULL,
- `COMMAND` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`RESOURCE_JOB_MANAGER_ID`,`COMMAND_TYPE`),
- CONSTRAINT `job_manager_command_ibfk_1` FOREIGN KEY (`RESOURCE_JOB_MANAGER_ID`) REFERENCES `RESOURCE_JOB_MANAGER` (`RESOURCE_JOB_MANAGER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `JOB_MANAGER_COMMAND`
---
-
-LOCK TABLES `JOB_MANAGER_COMMAND` WRITE;
-/*!40000 ALTER TABLE `JOB_MANAGER_COMMAND` DISABLE KEYS */;
-INSERT INTO `JOB_MANAGER_COMMAND` VALUES ('RJM_11132688-7ff1-48fb-802a-0203f8d8b14b','CHECK_JOB','squeue'),('RJM_11132688-7ff1-48fb-802a-0203f8d8b14b','DELETION','scancel'),('RJM_11132688-7ff1-48fb-802a-0203f8d8b14b','JOB_MONITORING','squeue -t all'),('RJM_11132688-7ff1-48fb-802a-0203f8d8b14b','SUBMISSION','sbatch');
-/*!40000 ALTER TABLE `JOB_MANAGER_COMMAND` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `JOB_SUBMISSION_INTERFACE`
---
-
-DROP TABLE IF EXISTS `JOB_SUBMISSION_INTERFACE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `JOB_SUBMISSION_INTERFACE` (
- `JOB_SUBMISSION_INTERFACE_ID` varchar(255) NOT NULL,
- `COMPUTE_RESOURCE_ID` varchar(255) NOT NULL,
- `JOB_SUBMISSION_PROTOCOL` varchar(255) NOT NULL,
- `PRIORITY_ORDER` int(11) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`COMPUTE_RESOURCE_ID`,`JOB_SUBMISSION_INTERFACE_ID`),
- CONSTRAINT `job_submission_interface_ibfk_1` FOREIGN KEY (`COMPUTE_RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE` (`RESOURCE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `JOB_SUBMISSION_INTERFACE`
---
-
-LOCK TABLES `JOB_SUBMISSION_INTERFACE` WRITE;
-/*!40000 ALTER TABLE `JOB_SUBMISSION_INTERFACE` DISABLE KEYS */;
-INSERT INTO `JOB_SUBMISSION_INTERFACE` VALUES ('SSH_8cdb13a9-130b-4527-96fe-dcd0c5ffd185','js-156-93.jetstream-cloud.org_f0813875-de5b-4abb-b56f-eb9237809c61','SSH',0,'2019-02-25 23:52:55','2019-02-25 23:52:55');
-/*!40000 ALTER TABLE `JOB_SUBMISSION_INTERFACE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `LIBRARY_APEND_PATH`
---
-
-DROP TABLE IF EXISTS `LIBRARY_APEND_PATH`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `LIBRARY_APEND_PATH` (
- `DEPLOYMENT_ID` varchar(255) NOT NULL,
- `NAME` varchar(255) NOT NULL,
- `VALUE` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`DEPLOYMENT_ID`,`NAME`),
- CONSTRAINT `library_apend_path_ibfk_1` FOREIGN KEY (`DEPLOYMENT_ID`) REFERENCES `APPLICATION_DEPLOYMENT` (`DEPLOYMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `LIBRARY_APEND_PATH`
---
-
-LOCK TABLES `LIBRARY_APEND_PATH` WRITE;
-/*!40000 ALTER TABLE `LIBRARY_APEND_PATH` DISABLE KEYS */;
-/*!40000 ALTER TABLE `LIBRARY_APEND_PATH` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `LIBRARY_PREPAND_PATH`
---
-
-DROP TABLE IF EXISTS `LIBRARY_PREPAND_PATH`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `LIBRARY_PREPAND_PATH` (
- `DEPLOYMENT_ID` varchar(255) NOT NULL,
- `NAME` varchar(255) NOT NULL,
- `VALUE` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`DEPLOYMENT_ID`,`NAME`),
- CONSTRAINT `library_prepand_path_ibfk_1` FOREIGN KEY (`DEPLOYMENT_ID`) REFERENCES `APPLICATION_DEPLOYMENT` (`DEPLOYMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `LIBRARY_PREPAND_PATH`
---
-
-LOCK TABLES `LIBRARY_PREPAND_PATH` WRITE;
-/*!40000 ALTER TABLE `LIBRARY_PREPAND_PATH` DISABLE KEYS */;
-/*!40000 ALTER TABLE `LIBRARY_PREPAND_PATH` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `LOCAL_DATA_MOVEMENT`
---
-
-DROP TABLE IF EXISTS `LOCAL_DATA_MOVEMENT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `LOCAL_DATA_MOVEMENT` (
- `DATA_MOVEMENT_INTERFACE_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`DATA_MOVEMENT_INTERFACE_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `LOCAL_DATA_MOVEMENT`
---
-
-LOCK TABLES `LOCAL_DATA_MOVEMENT` WRITE;
-/*!40000 ALTER TABLE `LOCAL_DATA_MOVEMENT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `LOCAL_DATA_MOVEMENT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `LOCAL_SUBMISSION`
---
-
-DROP TABLE IF EXISTS `LOCAL_SUBMISSION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `LOCAL_SUBMISSION` (
- `RESOURCE_JOB_MANAGER_ID` varchar(255) NOT NULL,
- `JOB_SUBMISSION_INTERFACE_ID` varchar(255) NOT NULL,
- `SECURITY_PROTOCOL` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`JOB_SUBMISSION_INTERFACE_ID`),
- KEY `RESOURCE_JOB_MANAGER_ID` (`RESOURCE_JOB_MANAGER_ID`),
- CONSTRAINT `local_submission_ibfk_1` FOREIGN KEY (`RESOURCE_JOB_MANAGER_ID`) REFERENCES `RESOURCE_JOB_MANAGER` (`RESOURCE_JOB_MANAGER_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `LOCAL_SUBMISSION`
---
-
-LOCK TABLES `LOCAL_SUBMISSION` WRITE;
-/*!40000 ALTER TABLE `LOCAL_SUBMISSION` DISABLE KEYS */;
-/*!40000 ALTER TABLE `LOCAL_SUBMISSION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `MODULE_LOAD_CMD`
---
-
-DROP TABLE IF EXISTS `MODULE_LOAD_CMD`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `MODULE_LOAD_CMD` (
- `CMD` varchar(255) NOT NULL,
- `APP_DEPLOYMENT_ID` varchar(255) NOT NULL,
- `COMMAND_ORDER` int(11) DEFAULT NULL,
- PRIMARY KEY (`APP_DEPLOYMENT_ID`,`CMD`),
- CONSTRAINT `module_load_cmd_ibfk_1` FOREIGN KEY (`APP_DEPLOYMENT_ID`) REFERENCES `APPLICATION_DEPLOYMENT` (`DEPLOYMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `MODULE_LOAD_CMD`
---
-
-LOCK TABLES `MODULE_LOAD_CMD` WRITE;
-/*!40000 ALTER TABLE `MODULE_LOAD_CMD` DISABLE KEYS */;
-/*!40000 ALTER TABLE `MODULE_LOAD_CMD` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PARALLELISM_COMMAND`
---
-
-DROP TABLE IF EXISTS `PARALLELISM_COMMAND`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PARALLELISM_COMMAND` (
- `RESOURCE_JOB_MANAGER_ID` varchar(255) NOT NULL,
- `COMMAND_TYPE` varchar(255) NOT NULL,
- `COMMAND` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`RESOURCE_JOB_MANAGER_ID`,`COMMAND_TYPE`),
- CONSTRAINT `parallelism_command_ibfk_1` FOREIGN KEY (`RESOURCE_JOB_MANAGER_ID`) REFERENCES `RESOURCE_JOB_MANAGER` (`RESOURCE_JOB_MANAGER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PARALLELISM_COMMAND`
---
-
-LOCK TABLES `PARALLELISM_COMMAND` WRITE;
-/*!40000 ALTER TABLE `PARALLELISM_COMMAND` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PARALLELISM_COMMAND` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PARSER`
---
-
-DROP TABLE IF EXISTS `PARSER`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PARSER` (
- `PARSER_ID` varchar(255) NOT NULL,
- `IMAGE_NAME` varchar(255) NOT NULL,
- `OUTPUT_DIR_PATH` varchar(255) NOT NULL,
- `INPUT_DIR_PATH` varchar(255) NOT NULL,
- `EXECUTION_COMMAND` varchar(255) NOT NULL,
- `GATEWAY_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`PARSER_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PARSER`
---
-
-LOCK TABLES `PARSER` WRITE;
-/*!40000 ALTER TABLE `PARSER` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PARSER` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PARSER_CONNECTOR`
---
-
-DROP TABLE IF EXISTS `PARSER_CONNECTOR`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PARSER_CONNECTOR` (
- `PARSER_CONNECTOR_ID` varchar(255) NOT NULL,
- `PARENT_PARSER_ID` varchar(255) NOT NULL,
- `CHILD_PARSER_ID` varchar(255) NOT NULL,
- `PARSING_TEMPLATE_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`PARSER_CONNECTOR_ID`),
- KEY `PARENT_PARSER_ID` (`PARENT_PARSER_ID`),
- KEY `CHILD_PARSER_ID` (`CHILD_PARSER_ID`),
- KEY `PARSING_TEMPLATE_ID` (`PARSING_TEMPLATE_ID`),
- CONSTRAINT `parser_connector_ibfk_1` FOREIGN KEY (`PARENT_PARSER_ID`) REFERENCES `PARSER` (`PARSER_ID`) ON DELETE CASCADE,
- CONSTRAINT `parser_connector_ibfk_2` FOREIGN KEY (`CHILD_PARSER_ID`) REFERENCES `PARSER` (`PARSER_ID`) ON DELETE CASCADE,
- CONSTRAINT `parser_connector_ibfk_3` FOREIGN KEY (`PARSING_TEMPLATE_ID`) REFERENCES `PARSING_TEMPLATE` (`PARSING_TEMPLATE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PARSER_CONNECTOR`
---
-
-LOCK TABLES `PARSER_CONNECTOR` WRITE;
-/*!40000 ALTER TABLE `PARSER_CONNECTOR` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PARSER_CONNECTOR` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PARSER_CONNECTOR_INPUT`
---
-
-DROP TABLE IF EXISTS `PARSER_CONNECTOR_INPUT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PARSER_CONNECTOR_INPUT` (
- `PARSER_CONNECTOR_INPUT_ID` varchar(255) NOT NULL,
- `PARSER_INPUT_ID` varchar(255) NOT NULL,
- `PARSER_OUTPUT_ID` varchar(255) DEFAULT NULL,
- `VALUE` varchar(255) DEFAULT NULL,
- `PARSER_CONNECTOR_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`PARSER_CONNECTOR_INPUT_ID`),
- KEY `PARSER_INPUT_ID` (`PARSER_INPUT_ID`),
- KEY `PARSER_OUTPUT_ID` (`PARSER_OUTPUT_ID`),
- KEY `PARSER_CONNECTOR_ID` (`PARSER_CONNECTOR_ID`),
- CONSTRAINT `parser_connector_input_ibfk_1` FOREIGN KEY (`PARSER_INPUT_ID`) REFERENCES `PARSER_INPUT` (`PARSER_INPUT_ID`) ON DELETE CASCADE,
- CONSTRAINT `parser_connector_input_ibfk_2` FOREIGN KEY (`PARSER_OUTPUT_ID`) REFERENCES `PARSER_OUTPUT` (`PARSER_OUTPUT_ID`) ON DELETE CASCADE,
- CONSTRAINT `parser_connector_input_ibfk_3` FOREIGN KEY (`PARSER_CONNECTOR_ID`) REFERENCES `PARSER_CONNECTOR` (`PARSER_CONNECTOR_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PARSER_CONNECTOR_INPUT`
---
-
-LOCK TABLES `PARSER_CONNECTOR_INPUT` WRITE;
-/*!40000 ALTER TABLE `PARSER_CONNECTOR_INPUT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PARSER_CONNECTOR_INPUT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PARSER_INPUT`
---
-
-DROP TABLE IF EXISTS `PARSER_INPUT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PARSER_INPUT` (
- `PARSER_INPUT_ID` varchar(255) NOT NULL,
- `PARSER_INPUT_NAME` varchar(255) NOT NULL,
- `PARSER_INPUT_REQUIRED` tinyint(1) NOT NULL,
- `PARSER_ID` varchar(255) NOT NULL,
- `INPUT_TYPE` varchar(255) NOT NULL,
- PRIMARY KEY (`PARSER_INPUT_ID`),
- KEY `PARSER_ID` (`PARSER_ID`),
- CONSTRAINT `parser_input_ibfk_1` FOREIGN KEY (`PARSER_ID`) REFERENCES `PARSER` (`PARSER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PARSER_INPUT`
---
-
-LOCK TABLES `PARSER_INPUT` WRITE;
-/*!40000 ALTER TABLE `PARSER_INPUT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PARSER_INPUT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PARSER_OUTPUT`
---
-
-DROP TABLE IF EXISTS `PARSER_OUTPUT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PARSER_OUTPUT` (
- `PARSER_OUTPUT_ID` varchar(255) NOT NULL,
- `PARSER_OUTPUT_NAME` varchar(255) NOT NULL,
- `PARSER_OUTPUT_REQUIRED` tinyint(1) NOT NULL,
- `PARSER_ID` varchar(255) NOT NULL,
- `OUTPUT_TYPE` varchar(255) NOT NULL,
- PRIMARY KEY (`PARSER_OUTPUT_ID`),
- KEY `PARSER_ID` (`PARSER_ID`),
- CONSTRAINT `parser_output_ibfk_1` FOREIGN KEY (`PARSER_ID`) REFERENCES `PARSER` (`PARSER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PARSER_OUTPUT`
---
-
-LOCK TABLES `PARSER_OUTPUT` WRITE;
-/*!40000 ALTER TABLE `PARSER_OUTPUT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PARSER_OUTPUT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PARSING_TEMPLATE`
---
-
-DROP TABLE IF EXISTS `PARSING_TEMPLATE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PARSING_TEMPLATE` (
- `PARSING_TEMPLATE_ID` varchar(255) NOT NULL,
- `APP_INTERFACE_ID` varchar(255) NOT NULL,
- `GATEWAY_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`PARSING_TEMPLATE_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PARSING_TEMPLATE`
---
-
-LOCK TABLES `PARSING_TEMPLATE` WRITE;
-/*!40000 ALTER TABLE `PARSING_TEMPLATE` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PARSING_TEMPLATE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PARSING_TEMPLATE_INPUT`
---
-
-DROP TABLE IF EXISTS `PARSING_TEMPLATE_INPUT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PARSING_TEMPLATE_INPUT` (
- `PARSING_TEMPLATE_INPUT_ID` varchar(255) NOT NULL,
- `TARGET_PARSER_INPUT_ID` varchar(255) NOT NULL,
- `APPLICATION_OUTPUT_NAME` varchar(255) DEFAULT NULL,
- `VALUE` varchar(255) DEFAULT NULL,
- `PARSING_TEMPLATE_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`PARSING_TEMPLATE_INPUT_ID`),
- KEY `TARGET_PARSER_INPUT_ID` (`TARGET_PARSER_INPUT_ID`),
- KEY `PARSING_TEMPLATE_ID` (`PARSING_TEMPLATE_ID`),
- CONSTRAINT `parsing_template_input_ibfk_1` FOREIGN KEY (`TARGET_PARSER_INPUT_ID`) REFERENCES `PARSER_INPUT` (`PARSER_INPUT_ID`) ON DELETE CASCADE,
- CONSTRAINT `parsing_template_input_ibfk_2` FOREIGN KEY (`PARSING_TEMPLATE_ID`) REFERENCES `PARSING_TEMPLATE` (`PARSING_TEMPLATE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PARSING_TEMPLATE_INPUT`
---
-
-LOCK TABLES `PARSING_TEMPLATE_INPUT` WRITE;
-/*!40000 ALTER TABLE `PARSING_TEMPLATE_INPUT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PARSING_TEMPLATE_INPUT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `POSTJOB_COMMAND`
---
-
-DROP TABLE IF EXISTS `POSTJOB_COMMAND`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `POSTJOB_COMMAND` (
- `APPDEPLOYMENT_ID` varchar(255) NOT NULL,
- `COMMAND` varchar(255) NOT NULL,
- `COMMAND_ORDER` int(11) DEFAULT NULL,
- PRIMARY KEY (`APPDEPLOYMENT_ID`,`COMMAND`),
- CONSTRAINT `postjob_command_ibfk_1` FOREIGN KEY (`APPDEPLOYMENT_ID`) REFERENCES `APPLICATION_DEPLOYMENT` (`DEPLOYMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `POSTJOB_COMMAND`
---
-
-LOCK TABLES `POSTJOB_COMMAND` WRITE;
-/*!40000 ALTER TABLE `POSTJOB_COMMAND` DISABLE KEYS */;
-/*!40000 ALTER TABLE `POSTJOB_COMMAND` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PREJOB_COMMAND`
---
-
-DROP TABLE IF EXISTS `PREJOB_COMMAND`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PREJOB_COMMAND` (
- `APPDEPLOYMENT_ID` varchar(255) NOT NULL,
- `COMMAND` varchar(255) NOT NULL,
- `COMMAND_ORDER` int(11) DEFAULT NULL,
- PRIMARY KEY (`APPDEPLOYMENT_ID`,`COMMAND`),
- CONSTRAINT `prejob_command_ibfk_1` FOREIGN KEY (`APPDEPLOYMENT_ID`) REFERENCES `APPLICATION_DEPLOYMENT` (`DEPLOYMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PREJOB_COMMAND`
---
-
-LOCK TABLES `PREJOB_COMMAND` WRITE;
-/*!40000 ALTER TABLE `PREJOB_COMMAND` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PREJOB_COMMAND` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `RESOURCE_JOB_MANAGER`
---
-
-DROP TABLE IF EXISTS `RESOURCE_JOB_MANAGER`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `RESOURCE_JOB_MANAGER` (
- `RESOURCE_JOB_MANAGER_ID` varchar(255) NOT NULL,
- `PUSH_MONITORING_ENDPOINT` varchar(255) DEFAULT NULL,
- `JOB_MANAGER_BIN_PATH` varchar(255) DEFAULT NULL,
- `RESOURCE_JOB_MANAGER_TYPE` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`RESOURCE_JOB_MANAGER_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `RESOURCE_JOB_MANAGER`
---
-
-LOCK TABLES `RESOURCE_JOB_MANAGER` WRITE;
-/*!40000 ALTER TABLE `RESOURCE_JOB_MANAGER` DISABLE KEYS */;
-INSERT INTO `RESOURCE_JOB_MANAGER` VALUES ('RJM_11132688-7ff1-48fb-802a-0203f8d8b14b','','/bin','SLURM','2019-02-25 23:52:55','2019-02-25 23:52:55');
-/*!40000 ALTER TABLE `RESOURCE_JOB_MANAGER` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `SCP_DATA_MOVEMENT`
---
-
-DROP TABLE IF EXISTS `SCP_DATA_MOVEMENT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `SCP_DATA_MOVEMENT` (
- `QUEUE_DESCRIPTION` varchar(255) DEFAULT NULL,
- `DATA_MOVEMENT_INTERFACE_ID` varchar(255) NOT NULL,
- `SECURITY_PROTOCOL` varchar(255) NOT NULL,
- `ALTERNATIVE_SCP_HOSTNAME` varchar(255) DEFAULT NULL,
- `SSH_PORT` int(11) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`DATA_MOVEMENT_INTERFACE_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `SCP_DATA_MOVEMENT`
---
-
-LOCK TABLES `SCP_DATA_MOVEMENT` WRITE;
-/*!40000 ALTER TABLE `SCP_DATA_MOVEMENT` DISABLE KEYS */;
-INSERT INTO `SCP_DATA_MOVEMENT` VALUES (NULL,'DO_NOT_SET_AT_CLIENTS','SSH_KEYS','',22222,'2019-02-26 15:12:00','2019-02-26 10:12:01'),(NULL,'SCP_1e775974-c405-4793-9e6c-a88d98013a28','SSH_KEYS','',0,'2019-02-25 23:53:15','2019-02-25 23:53:15'),(NULL,'SCP_e6fa0ccf-6dcd-4361-be09-1fd7df7f7a3c','SSH_KEYS','',22222,'2019-02-26 00:27:42','2019-02-26 00:27:42');
-/*!40000 ALTER TABLE `SCP_DATA_MOVEMENT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `SSH_ACCOUNT_PROVISIONER_CONFIG`
---
-
-DROP TABLE IF EXISTS `SSH_ACCOUNT_PROVISIONER_CONFIG`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `SSH_ACCOUNT_PROVISIONER_CONFIG` (
- `GATEWAY_ID` varchar(255) NOT NULL,
- `RESOURCE_ID` varchar(255) NOT NULL,
- `CONFIG_NAME` varchar(255) NOT NULL,
- `CONFIG_VALUE` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`GATEWAY_ID`,`RESOURCE_ID`,`CONFIG_NAME`),
- CONSTRAINT `ssh_account_provisioner_config_ibfk_1` FOREIGN KEY (`GATEWAY_ID`, `RESOURCE_ID`) REFERENCES `COMPUTE_RESOURCE_PREFERENCE` (`GATEWAY_ID`, `RESOURCE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `SSH_ACCOUNT_PROVISIONER_CONFIG`
---
-
-LOCK TABLES `SSH_ACCOUNT_PROVISIONER_CONFIG` WRITE;
-/*!40000 ALTER TABLE `SSH_ACCOUNT_PROVISIONER_CONFIG` DISABLE KEYS */;
-/*!40000 ALTER TABLE `SSH_ACCOUNT_PROVISIONER_CONFIG` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `SSH_JOB_SUBMISSION`
---
-
-DROP TABLE IF EXISTS `SSH_JOB_SUBMISSION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `SSH_JOB_SUBMISSION` (
- `RESOURCE_JOB_MANAGER_ID` varchar(255) NOT NULL,
- `JOB_SUBMISSION_INTERFACE_ID` varchar(255) NOT NULL,
- `ALTERNATIVE_SSH_HOSTNAME` varchar(255) DEFAULT NULL,
- `SECURITY_PROTOCOL` varchar(255) NOT NULL,
- `SSH_PORT` int(11) DEFAULT NULL,
- `MONITOR_MODE` varchar(255) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`JOB_SUBMISSION_INTERFACE_ID`),
- KEY `RESOURCE_JOB_MANAGER_ID` (`RESOURCE_JOB_MANAGER_ID`),
- CONSTRAINT `ssh_job_submission_ibfk_1` FOREIGN KEY (`RESOURCE_JOB_MANAGER_ID`) REFERENCES `RESOURCE_JOB_MANAGER` (`RESOURCE_JOB_MANAGER_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `SSH_JOB_SUBMISSION`
---
-
-LOCK TABLES `SSH_JOB_SUBMISSION` WRITE;
-/*!40000 ALTER TABLE `SSH_JOB_SUBMISSION` DISABLE KEYS */;
-INSERT INTO `SSH_JOB_SUBMISSION` VALUES ('RJM_11132688-7ff1-48fb-802a-0203f8d8b14b','SSH_8cdb13a9-130b-4527-96fe-dcd0c5ffd185','','SSH_KEYS',0,'JOB_EMAIL_NOTIFICATION_MONITOR','2019-02-25 23:52:55','2019-02-25 23:52:55');
-/*!40000 ALTER TABLE `SSH_JOB_SUBMISSION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `STORAGE_INTERFACE`
---
-
-DROP TABLE IF EXISTS `STORAGE_INTERFACE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `STORAGE_INTERFACE` (
- `STORAGE_RESOURCE_ID` varchar(255) NOT NULL,
- `DATA_MOVEMENT_INTERFACE_ID` varchar(255) NOT NULL,
- `DATA_MOVEMENT_PROTOCOL` varchar(255) NOT NULL,
- `PRIORITY_ORDER` int(11) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`STORAGE_RESOURCE_ID`,`DATA_MOVEMENT_INTERFACE_ID`),
- CONSTRAINT `storage_interface_ibfk_1` FOREIGN KEY (`STORAGE_RESOURCE_ID`) REFERENCES `STORAGE_RESOURCE` (`STORAGE_RESOURCE_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `STORAGE_INTERFACE`
---
-
-LOCK TABLES `STORAGE_INTERFACE` WRITE;
-/*!40000 ALTER TABLE `STORAGE_INTERFACE` DISABLE KEYS */;
-INSERT INTO `STORAGE_INTERFACE` VALUES ('airavata.host_77116e91-f042-4d3a-ab9c-3e7b4ebcd5bd','SCP_e6fa0ccf-6dcd-4361-be09-1fd7df7f7a3c','SCP',0,'2019-02-26 00:27:42','2019-02-26 00:27:42');
-/*!40000 ALTER TABLE `STORAGE_INTERFACE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `STORAGE_PREFERENCE`
---
-
-DROP TABLE IF EXISTS `STORAGE_PREFERENCE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `STORAGE_PREFERENCE` (
- `GATEWAY_ID` varchar(255) NOT NULL,
- `STORAGE_RESOURCE_ID` varchar(255) NOT NULL,
- `LOGIN_USERNAME` varchar(255) DEFAULT NULL,
- `FS_ROOT_LOCATION` varchar(255) DEFAULT NULL,
- `RESOURCE_CS_TOKEN` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`GATEWAY_ID`,`STORAGE_RESOURCE_ID`),
- CONSTRAINT `storage_preference_ibfk_1` FOREIGN KEY (`GATEWAY_ID`) REFERENCES `GATEWAY_PROFILE` (`GATEWAY_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `STORAGE_PREFERENCE`
---
-
-LOCK TABLES `STORAGE_PREFERENCE` WRITE;
-/*!40000 ALTER TABLE `STORAGE_PREFERENCE` DISABLE KEYS */;
-INSERT INTO `STORAGE_PREFERENCE` VALUES ('default','airavata.host_77116e91-f042-4d3a-ab9c-3e7b4ebcd5bd','root','/var/www/portals/gateway-user-data','46a99a5a-8b55-4982-bfd7-90fe72b00d46');
-/*!40000 ALTER TABLE `STORAGE_PREFERENCE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `STORAGE_RESOURCE`
---
-
-DROP TABLE IF EXISTS `STORAGE_RESOURCE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `STORAGE_RESOURCE` (
- `STORAGE_RESOURCE_ID` varchar(255) NOT NULL,
- `HOST_NAME` varchar(255) NOT NULL,
- `DESCRIPTION` varchar(255) DEFAULT NULL,
- `ENABLED` smallint(6) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`STORAGE_RESOURCE_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `STORAGE_RESOURCE`
---
-
-LOCK TABLES `STORAGE_RESOURCE` WRITE;
-/*!40000 ALTER TABLE `STORAGE_RESOURCE` DISABLE KEYS */;
-INSERT INTO `STORAGE_RESOURCE` VALUES ('airavata.host_77116e91-f042-4d3a-ab9c-3e7b4ebcd5bd','airavata.host','',1,'2019-02-25 19:27:15','2019-02-26 10:11:53');
-/*!40000 ALTER TABLE `STORAGE_RESOURCE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `UNICORE_DATAMOVEMENT`
---
-
-DROP TABLE IF EXISTS `UNICORE_DATAMOVEMENT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `UNICORE_DATAMOVEMENT` (
- `DATAMOVEMENT_ID` varchar(255) NOT NULL,
- `SECURITY_PROTOCAL` varchar(255) DEFAULT NULL,
- `UNICORE_ENDPOINT_URL` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`DATAMOVEMENT_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `UNICORE_DATAMOVEMENT`
---
-
-LOCK TABLES `UNICORE_DATAMOVEMENT` WRITE;
-/*!40000 ALTER TABLE `UNICORE_DATAMOVEMENT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `UNICORE_DATAMOVEMENT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `UNICORE_SUBMISSION`
---
-
-DROP TABLE IF EXISTS `UNICORE_SUBMISSION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `UNICORE_SUBMISSION` (
- `SUBMISSION_ID` varchar(255) NOT NULL,
- `SECURITY_PROTOCAL` varchar(255) DEFAULT NULL,
- `UNICORE_ENDPOINT_URL` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`SUBMISSION_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `UNICORE_SUBMISSION`
---
-
-LOCK TABLES `UNICORE_SUBMISSION` WRITE;
-/*!40000 ALTER TABLE `UNICORE_SUBMISSION` DISABLE KEYS */;
-/*!40000 ALTER TABLE `UNICORE_SUBMISSION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USER_COMPUTE_RESOURCE_PREFERENCE`
---
-
-DROP TABLE IF EXISTS `USER_COMPUTE_RESOURCE_PREFERENCE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USER_COMPUTE_RESOURCE_PREFERENCE` (
- `RESOURCE_ID` varchar(255) NOT NULL,
- `USER_ID` varchar(255) NOT NULL,
- `PREFERED_BATCH_QUEUE` varchar(255) DEFAULT NULL,
- `RESOURCE_CS_TOKEN` varchar(255) DEFAULT NULL,
- `GATEWAY_ID` varchar(255) NOT NULL,
- `LOGIN_USERNAME` varchar(255) DEFAULT NULL,
- `ALLOCATION_PROJECT_NUMBER` varchar(255) DEFAULT NULL,
- `QUALITY_OF_SERVICE` varchar(255) DEFAULT NULL,
- `RESERVATION` varchar(255) DEFAULT NULL,
- `RESERVATION_END_TIME` datetime DEFAULT NULL,
- `RESERVATION_START_TIME` datetime DEFAULT NULL,
- `SCRATCH_LOCATION` varchar(255) DEFAULT NULL,
- `VALIDATED` tinyint(1) DEFAULT '0',
- PRIMARY KEY (`RESOURCE_ID`,`USER_ID`,`GATEWAY_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USER_COMPUTE_RESOURCE_PREFERENCE`
---
-
-LOCK TABLES `USER_COMPUTE_RESOURCE_PREFERENCE` WRITE;
-/*!40000 ALTER TABLE `USER_COMPUTE_RESOURCE_PREFERENCE` DISABLE KEYS */;
-/*!40000 ALTER TABLE `USER_COMPUTE_RESOURCE_PREFERENCE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USER_RESOURCE_PROFILE`
---
-
-DROP TABLE IF EXISTS `USER_RESOURCE_PROFILE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USER_RESOURCE_PROFILE` (
- `USER_ID` varchar(255) NOT NULL,
- `CREATION_TIME` datetime DEFAULT NULL,
- `CS_TOKEN` varchar(255) DEFAULT NULL,
- `GATEWAY_ID` varchar(255) NOT NULL,
- `IDENTITY_SERVER_PWD_CRED_TOKEN` varchar(255) DEFAULT NULL,
- `IDENTITY_SERVER_TENANT` varchar(255) DEFAULT NULL,
- `UPDATE_TIME` datetime DEFAULT NULL,
- PRIMARY KEY (`USER_ID`,`GATEWAY_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USER_RESOURCE_PROFILE`
---
-
-LOCK TABLES `USER_RESOURCE_PROFILE` WRITE;
-/*!40000 ALTER TABLE `USER_RESOURCE_PROFILE` DISABLE KEYS */;
-INSERT INTO `USER_RESOURCE_PROFILE` VALUES ('default-admin',NULL,NULL,'default',NULL,NULL,'2019-03-11 14:16:13');
-/*!40000 ALTER TABLE `USER_RESOURCE_PROFILE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USER_STORAGE_PREFERENCE`
---
-
-DROP TABLE IF EXISTS `USER_STORAGE_PREFERENCE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USER_STORAGE_PREFERENCE` (
- `STORAGE_RESOURCE_ID` varchar(255) NOT NULL,
- `USER_ID` varchar(255) NOT NULL,
- `RESOURCE_CS_TOKEN` varchar(255) DEFAULT NULL,
- `FS_ROOT_LOCATION` varchar(255) DEFAULT NULL,
- `GATEWAY_ID` varchar(255) NOT NULL,
- `LOGIN_USERNAME` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`STORAGE_RESOURCE_ID`,`USER_ID`,`GATEWAY_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USER_STORAGE_PREFERENCE`
---
-
-LOCK TABLES `USER_STORAGE_PREFERENCE` WRITE;
-/*!40000 ALTER TABLE `USER_STORAGE_PREFERENCE` DISABLE KEYS */;
-/*!40000 ALTER TABLE `USER_STORAGE_PREFERENCE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Current Database: `credential_store`
---
-
-CREATE DATABASE /*!32312 IF NOT EXISTS*/ `credential_store` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
-
-USE `credential_store`;
-
---
--- Table structure for table `COMMUNITY_USER`
---
-
-DROP TABLE IF EXISTS `COMMUNITY_USER`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `COMMUNITY_USER` (
- `GATEWAY_ID` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
- `COMMUNITY_USER_NAME` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
- `TOKEN_ID` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
- `COMMUNITY_USER_EMAIL` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`GATEWAY_ID`,`COMMUNITY_USER_NAME`,`TOKEN_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `COMMUNITY_USER`
---
-
-LOCK TABLES `COMMUNITY_USER` WRITE;
-/*!40000 ALTER TABLE `COMMUNITY_USER` DISABLE KEYS */;
-/*!40000 ALTER TABLE `COMMUNITY_USER` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `CONFIGURATION`
---
-
-DROP TABLE IF EXISTS `CONFIGURATION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `CONFIGURATION` (
- `CONFIG_KEY` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `CONFIG_VAL` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`CONFIG_KEY`,`CONFIG_VAL`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `CONFIGURATION`
---
-
-LOCK TABLES `CONFIGURATION` WRITE;
-/*!40000 ALTER TABLE `CONFIGURATION` DISABLE KEYS */;
-INSERT INTO `CONFIGURATION` VALUES ('credential_store_version','0.16');
-/*!40000 ALTER TABLE `CONFIGURATION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `CREDENTIALS`
---
-
-DROP TABLE IF EXISTS `CREDENTIALS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `CREDENTIALS` (
- `GATEWAY_ID` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
- `TOKEN_ID` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
- `CREDENTIAL` blob NOT NULL,
- `PORTAL_USER_ID` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `TIME_PERSISTED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `DESCRIPTION` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `CREDENTIAL_OWNER_TYPE` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'GATEWAY',
- PRIMARY KEY (`GATEWAY_ID`,`TOKEN_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `CREDENTIALS`
---
-
-LOCK TABLES `CREDENTIALS` WRITE;
-/*!40000 ALTER TABLE `CREDENTIALS` DISABLE KEYS */;
-INSERT INTO `CREDENTIALS` VALUES ('default','46a99a5a-8b55-4982-bfd7-90fe72b00d46',0x149D247A85EEF670D6919C6F2367E979799D210452590F82AE36956A6C3E303D4BAF4BCC5D3CAE6FC169132BDAF79C5B2D96C37105B1F2565A65B44494051300D51F1A6EE17BDA9E1097D3D95631D60ED2223ECE5C6F6FB518F578F6FE2C6A81BF1E971F58A2CA37933A857133B6BCEE12BE13D9D3A74334C2D5101C69E5279EE6C70623FFB80AC49611E37BC02A6ABC8287814E24D872BC2198D257730526415B65FE1097BC805145242B6113AF55EDD71F855F8D915E40C6FEC204E289BEAF2E24C95DD7E6B79AA90B3871AA9E80EDEA5F1E45BF899F94FFB14B404A300ECB7859D3A595FB7E0DE91645C6602CBDE8F5038A198B5F54ECC1592BBF50D1FEB4AE8AE220601FD22C5138F50DCCEBFD3FFEE64F4FCD2D85BFE055A35449E1926563BB6E2B8FC21E5137DF7F5E312BA7AE15CA4C8F58C3B782B7F4D65BDBDD6A2A4753FB5DEB02AF9C4EDC01A505FCA7F2DA38043024F0430FBBDD51CABE67F062969B419A5522FA9A36AB3D9E9A7B95C2A2BE44113044E574B01A00BF71D2E0C52D26C47AB6ED68A05686BECA2543615F267A74BD85DD240EB554DF4DEED2AA5B1CDCB264EE2F7810177576D47218FF721227C199CCE853F089BAF1AD908E6079A45A27AF53BD8AE4AFC45B6D97174E6985F6BCB1ECB324E5A09A3DB8A60EE401244B023ABAE3DA2DF553C7A286D01B7A9A632A4D11DDB27B9A506C8A1F3C65D5BDCBF270D8797401B34766A93A38B332DC91FA589D48098848E0A337374F6891470F1CBF50C34F53C2A984E908D7510353157DB914AA61246824C61223AF5DEDC4768DF5B110609D0D968F5C632952F854B95CF353F1F64214F6DE68FD08BA1A3C155C2A6DF09FCF2AED839B199F281BB117769ED57D4E34D304FAD2D779F4E98C573FA8850A28CF173709C694B38AEFC1165B3DB2F9DBECD7C97AFA302E5FCA30D74F6EB69ADA6C064D534D1C7F2A83AE1B6827AB6FE52848E70C057854FAC6B0651B1B57296505C4F43C1DCB9EC17F34EB36563829D9D74B720072F2258F65C67D5101521E8D150F9E2AC3FD731376830241B5FC637D326A4D2B5EBA218FCA40329A88717CBF7E9835FB13D2E710D8D4A69012541256C61CB61988368251D22EA443386010EFA40D77908EDE66970FC8D2EBF45111316AE6FB60C40619CAFD310F9D32B2A99FB62299D5A27F8E9919EE3A0A804940AA8D6D637BA857BE9CD56E7C07472317133A68B1E9E88D32C519547E60D5966D496D6D73CD2BDD8AD2F216A6D0817B86D8A2D45F56A51B6B0C05DA557BD186990F16E5F6863AA748195089665BB203F426C24A002B36B34659CB0AD044F51F778EFFECFDF01098C0CE99C4BC54942C962A6C1F3D1E2B5A5BCCB5A143CFEDD01D3C32E0AF08D99BB03485BDFB49465BE93D8A0507E21E52739863C958E752F7C892009BF4811532180630968718E1577B5BA4055B5CEB9FC55FE0F9DCF1598A8F7C6ACD13F8D3BDC13355B4D91F5C7CCA4C5EEC10EE974AEB0AC5E953763C64167AFC7A959B29F64BA1FAFF4FD119B5A526293A54F39C43772E7630FA3AD15D2C8A79ACF2BEE33A99F5EB5D3ACB3A9D7E8B0E013F3002D461356837C1BB2EE7C58E65EAD76A4D40CBFBA50D82CAD529AE5ACBE7256C32D565D4279299B6C0A12D81E584A8ED241163E35D8E5367920878BE6F86EDD469E54BA20AE4E09B5671396837E9CE58BC269A67AD7EFD85500421E21C5E6C7F60FE055CA6354CC3DCFC48A97183925BB994FA7307A037FC145FAE39F50C01714B2EBD5A996E88B8C92016AC1C84A8287AE55C792B377C2ABA76D1234FFA98AA1505E3C9B263C21C39E6F9A72E7CC41A74DD33CA7BBB83348665304E8022AA9A8365FE1315B314AB5500AF918629B05338FF5743BD2B2264AF5EF54BDF565209619A795D17B9AEE6F3D59B593E979013D122BECD00A97E440DFBD0F745CA2BFDE130E1A47DEC322518BE14AC38326898E418684086397B603279EF85D95142DEBCECF5E2EF51E2564E1B8C80ACBC825D7085354E6087A1F8A1559B1BAAAC720782F4B7E95604435E48C059D3822ED0AB522AEF0380DBB4552C83571D3D8FC015C4321AB7189FC37BB2D3551D4CF143EB0B92BE96FF8D9A47385A4F6DD095260F7C4DD697D42D19F51537ACBA6DE73836FA984B3764DD0F8DB3607BB4171CD2E9FC36D2307858A1735C6EE5E957EF92CC381F103C27A41F40CA6ACCC16D4D65308B91EF27C733B0A35DA553F1CE20E458A4328A3B1420D1B22A0E51907F2AD2569821B8AC196019AB3712779039051C1C8AC103894E69616C9535DC9BEE08B0A624A8F2A2CF5637A2609D67339458F34BBA9990092480025C75DE074FBBA485FB05B49DD17A64E8AA595DBFA47DEB0F6674A870656A90339B9DAD269CAC63CDAF4FFB4C5FB8387494CF1EE4A50821260538227E475D00E92DB62A5A70148692D2075B8F159999D7C6FDE20E8384FF82D09CD9485B39C1320093B4DA49123A0673D27AAD7FB64731B0E3F64E1149268F731F13EF57A1567161C4978285244E8DB4B063A95A34685DF843514B1B75F577606CE48F6D23352CA966C6545F7F78DD51124FA8B11C0A21BF97E7DF904752C8D251408A1E924A593447FE6CA96D95E2436068C36836B2A2AFDFF5C4826BDBE95661031DDAA5A19BD50B4CFAD6095D07E960C74EB86420B4F66F274DD1E11053B0D44ABA2AADC7018A0B3603CFDCC43AAAC42E35FA344D4CC6A233470775B944B62EEF5570DBC141BBB5B4D2C0FB70C9BC4FFD951338FA7D9C43735734BBB06B3A1C7D305EF2012D2718AE6AF9961015D27FD4FCFFD16845ABEC781A8D96A85588E5C1AA60EA6F3A241D39376E85D9B6A42E0F1E701E246A8EDEDB504500E00880992CCC88B158624C50BD1FCE1ED91EF2AE073EDA118135D9FABC26FC9581BE3F29E251579A40935BA5C40E424F737163AA2125D70DE593C8570E9D22BDFD9F7265A0CDD6A60A252F7FCD6F4685380ECC1715092A49FBC783FDC2DDA5A87195FC47CC3B24633DCDDC0D0E1E0304F8124A6D1C402E4DDE5307B405D040E00AC25DA7C85C2044DB2FE5A6F5E76709AD9824AFD4FA2A73D45E640FB263A21C16A4FCB9547071A48AE17C59C44ADDCFF7E3CF71CF1C6EDA6A3C51A89A4F33017834615969F469697DABB82B71D30F6FC1915BD4F9323BBEC3C21C68B5196CB4F79AF3FB4E03A10B4E03D47B9EFF02895707C5849ED13721AD99E723A7C57CDF20D046A7D693353A4EEBDC827C8464E9CADF664B8AED89E358F007CB16210653D60AB59A099FFA4AD76C08A10A771326F0E9D0C6E222A28527C3A2820477410BEB192EAC1BC34E5B443C476B1E6A717D57050B1E65D1CF5DABC8A811BF3D6EC73B67898F68795734FFC53D7B31DA2766BB369546E4DAACAE56CA70452132BE57BF7579B3911B7036F74E0930583D24CA805ADD23E02544300441F28784DE57C059290625F8265446F61F5C6D1458C260016895535B237C3EED76D6D613DD21D34B0561087061859F2C354BBFCB3E541F38946974D9243BDFF5524E5FE0EE9B86DC2F53249B5E157E9AC1190B296A860CDF9EC3832FAA6C48F5C00EFE338B432A5BCD5AF05AD35FDFA68B24352171D8C8368F6B0BA736308ED1C5417C3C81E501B67145E5EF0B7D40C31DFFB03822835E4E9226C5A508ABF7ACC27AB4CC8CBFC2334CFA742D320067706409D706520A04CF895FB582DF8053B0791D45F27D61C610AC99DC2CB08A1583976AF56C0E54E356B7EA7D0042DEB45285E72744C80AFBC08555AF1EF611EA7D6F1FE4BA5212EB7280E024CD677E52B60D2DE616D16E6616F0390A6C119AD4CE9298903D46C01E9E647B926072E2FFAD0801ED736DB4C54381E5642FC900F6F0A11EDCA22127E1F998C7A982296008B70A18E0F8327EFD1C8DA2CB7644408A91BE73608C08E6063B3588A36DEDCB6BD69AC3FD3DAE836B0AF192CB4E929607F4E1D349EAC6DA18FE943E5B76965E7634577877461B5D6A346FEE3E7CFF76DB,'default-admin','2019-02-25 19:06:48','Default','GATEWAY'),('default','daf89639-bf92-4161-9c54-34571372d092',0xFB5ECB6FB5AD8335BBB104326FC122571FA73D2CFFC26DD33D56952581215ED1A5DA2623FB4481BE5A1323E3DEAA58CF88F5B63C24D2FEE338A008E8DAB3A5F8ED478901C3517571E259B379E3B2DC5470F66819EBF7A71B652E8BDA2B75E17B225C5263EB0E53CA7057B8CD3BE3B33CCA0ACCCF3DAF4043E546BFFF8D61CEC3069DA041A5B0F2076581F7E98EDD81C4787D7B3F6F4AAB16AB36BBA8FC012B0F68E08FD41DBB724282883AE20616BF9209589ADAA9693DC5737654BC3D77C86AF4A6C66AF7DA4577F71D3F93BAF3B16BC57F559A1CE90585D35D2DC213EB07B16482F612EE1C63A070133C0B83FC7FF090358D9671585F627EC31C64C989C05328AA5867ED9C7D58531EC4244456353C4E616690D5639C9C4856896C817058C1DEEF08BF98ABF44D65D3791F616EE535C57F5DBE06E43BAEA45A470E13FE98F5DA2A98D0C14A07496696ABE51816CA1843B004B6D550D24CAE0E5E3A69ADBA8A93DB05951E9B9617CC8DB1D21043BAFCD3A4AA81DD7002456E6EFC83DFA3811EB894CA7D0D28BE5DDD8C62BA7FDC876BBE81BFE3D2A5E4D7D26C0F1598523EF6D00100C9551BF226471762FBDD39C76200BB8541A14518BB4F3C5C82DD570189646E140D469D3BA24180640E8F88A27EC7FCB1DF951A2486E9990448E18932EF75C95817797B7D4D93FA94E04E25EE0A0DBAADA3600A36A3A523B0DFC64B879BE386D0F1F0113DE64A1F6777CF1651269FDA5EB48E9095254672D1FC7F288CCE757CF7FA5BBAD39FF4E967866F3E1CCE6D51E0476F516605F8057F5482D12ED0AD9E280B1CF4EC2B66ED7F36ED4B2651F44C5F4101184F73FC6FAC2A24FA975D1E15FC92B2FEA2FA668A09F3D1865EA13C0A6DF37725A3F1D823B5AC0863EEE936D9EF593CA0EF48DA33DB12B0B8515B8619C1DA7900ABC9D8015A77C6D54F6629B233443182335C1B9BB6565CDCFBD986FA30569C6B183BB17E45221A2112AC6E399DF072A51A6C6F850ADD62EB22B954D1F5EDC213334D06E7C4DEA20384B2D001DFFBB1DBCF25452BAEE4962750F511380F7458BC5E825EC0CF483ACC6216067B5A7037C948D0F5DE35A5AD3A1C5CB3D0B1354D6FE9B7AEE6F54F265485470280E185A554D5CFE382BD88D3A9C9ED6CF0E2038C9402457111CB0E943BF39E1019FB7A20AB6042541D71714AAC5E10,'default-admin','2019-02-25 18:40:12','Credentials for default gateway','GATEWAY');
-/*!40000 ALTER TABLE `CREDENTIALS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Current Database: `experiment_catalog`
---
-
-CREATE DATABASE /*!32312 IF NOT EXISTS*/ `experiment_catalog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
-
-USE `experiment_catalog`;
-
---
--- Table structure for table `CONFIGURATION`
---
-
-DROP TABLE IF EXISTS `CONFIGURATION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `CONFIGURATION` (
- `CONFIG_KEY` varchar(255) NOT NULL,
- `CONFIG_VAL` varchar(255) NOT NULL,
- `EXPIRE_DATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `CATEGORY_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`CONFIG_KEY`,`CONFIG_VAL`,`CATEGORY_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `CONFIGURATION`
---
-
-LOCK TABLES `CONFIGURATION` WRITE;
-/*!40000 ALTER TABLE `CONFIGURATION` DISABLE KEYS */;
-INSERT INTO `CONFIGURATION` VALUES ('registry.version','0.16','2019-02-25 23:40:05','SYSTEM');
-/*!40000 ALTER TABLE `CONFIGURATION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `EXPERIMENT`
---
-
-DROP TABLE IF EXISTS `EXPERIMENT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `EXPERIMENT` (
- `EXPERIMENT_ID` varchar(255) NOT NULL,
- `PROJECT_ID` varchar(255) NOT NULL,
- `GATEWAY_ID` varchar(255) DEFAULT NULL,
- `EXPERIMENT_TYPE` varchar(255) DEFAULT NULL,
- `USER_NAME` varchar(255) DEFAULT NULL,
- `EXPERIMENT_NAME` varchar(255) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `DESCRIPTION` varchar(255) DEFAULT NULL,
- `EXECUTION_ID` varchar(255) DEFAULT NULL,
- `GATEWAY_EXECUTION_ID` varchar(255) DEFAULT NULL,
- `GATEWAY_INSTANCE_ID` varchar(255) DEFAULT NULL,
- `ENABLE_EMAIL_NOTIFICATION` tinyint(1) DEFAULT NULL,
- `EMAIL_ADDRESSES` text,
- PRIMARY KEY (`EXPERIMENT_ID`),
- KEY `PROJECT_ID` (`PROJECT_ID`),
- CONSTRAINT `experiment_ibfk_1` FOREIGN KEY (`PROJECT_ID`) REFERENCES `PROJECT` (`PROJECT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `EXPERIMENT`
---
-
-LOCK TABLES `EXPERIMENT` WRITE;
-/*!40000 ALTER TABLE `EXPERIMENT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `EXPERIMENT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `EXPERIMENT_ERROR`
---
-
-DROP TABLE IF EXISTS `EXPERIMENT_ERROR`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `EXPERIMENT_ERROR` (
- `ERROR_ID` varchar(255) NOT NULL,
- `EXPERIMENT_ID` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `ACTUAL_ERROR_MESSAGE` text,
- `USER_FRIENDLY_MESSAGE` text,
- `TRANSIENT_OR_PERSISTENT` tinyint(1) DEFAULT NULL,
- `ROOT_CAUSE_ERROR_ID_LIST` text,
- PRIMARY KEY (`ERROR_ID`,`EXPERIMENT_ID`),
- KEY `EXPERIMENT_ID` (`EXPERIMENT_ID`),
- CONSTRAINT `experiment_error_ibfk_1` FOREIGN KEY (`EXPERIMENT_ID`) REFERENCES `EXPERIMENT` (`EXPERIMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `EXPERIMENT_ERROR`
---
-
-LOCK TABLES `EXPERIMENT_ERROR` WRITE;
-/*!40000 ALTER TABLE `EXPERIMENT_ERROR` DISABLE KEYS */;
-/*!40000 ALTER TABLE `EXPERIMENT_ERROR` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `EXPERIMENT_INPUT`
---
-
-DROP TABLE IF EXISTS `EXPERIMENT_INPUT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `EXPERIMENT_INPUT` (
- `EXPERIMENT_ID` varchar(255) NOT NULL,
- `INPUT_NAME` varchar(255) NOT NULL,
- `INPUT_VALUE` text,
- `DATA_TYPE` varchar(255) DEFAULT NULL,
- `APPLICATION_ARGUMENT` varchar(255) DEFAULT NULL,
- `STANDARD_INPUT` tinyint(1) DEFAULT NULL,
- `USER_FRIENDLY_DESCRIPTION` varchar(255) DEFAULT NULL,
- `METADATA` varchar(4096) DEFAULT NULL,
- `INPUT_ORDER` int(11) DEFAULT NULL,
- `IS_REQUIRED` tinyint(1) DEFAULT NULL,
- `REQUIRED_TO_ADDED_TO_CMD` tinyint(1) DEFAULT NULL,
- `DATA_STAGED` tinyint(1) DEFAULT NULL,
- `STORAGE_RESOURCE_ID` varchar(255) DEFAULT NULL,
- `IS_READ_ONLY` tinyint(1) DEFAULT NULL,
- PRIMARY KEY (`EXPERIMENT_ID`,`INPUT_NAME`),
- CONSTRAINT `experiment_input_ibfk_1` FOREIGN KEY (`EXPERIMENT_ID`) REFERENCES `EXPERIMENT` (`EXPERIMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `EXPERIMENT_INPUT`
---
-
-LOCK TABLES `EXPERIMENT_INPUT` WRITE;
-/*!40000 ALTER TABLE `EXPERIMENT_INPUT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `EXPERIMENT_INPUT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `EXPERIMENT_OUTPUT`
---
-
-DROP TABLE IF EXISTS `EXPERIMENT_OUTPUT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `EXPERIMENT_OUTPUT` (
- `EXPERIMENT_ID` varchar(255) NOT NULL,
- `OUTPUT_NAME` varchar(255) NOT NULL,
- `OUTPUT_VALUE` text,
- `DATA_TYPE` varchar(255) DEFAULT NULL,
- `APPLICATION_ARGUMENT` varchar(255) DEFAULT NULL,
- `IS_REQUIRED` tinyint(1) DEFAULT NULL,
- `REQUIRED_TO_ADDED_TO_CMD` tinyint(1) DEFAULT NULL,
- `DATA_MOVEMENT` tinyint(1) DEFAULT NULL,
- `LOCATION` varchar(255) DEFAULT NULL,
- `SEARCH_QUERY` varchar(255) DEFAULT NULL,
- `OUTPUT_STREAMING` smallint(6) DEFAULT NULL,
- `STORAGE_RESOURCE_ID` varchar(255) DEFAULT NULL,
- `METADATA` varchar(4096) DEFAULT NULL,
- PRIMARY KEY (`EXPERIMENT_ID`,`OUTPUT_NAME`),
- CONSTRAINT `experiment_output_ibfk_1` FOREIGN KEY (`EXPERIMENT_ID`) REFERENCES `EXPERIMENT` (`EXPERIMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `EXPERIMENT_OUTPUT`
---
-
-LOCK TABLES `EXPERIMENT_OUTPUT` WRITE;
-/*!40000 ALTER TABLE `EXPERIMENT_OUTPUT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `EXPERIMENT_OUTPUT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `EXPERIMENT_STATUS`
---
-
-DROP TABLE IF EXISTS `EXPERIMENT_STATUS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `EXPERIMENT_STATUS` (
- `STATUS_ID` varchar(255) NOT NULL,
- `EXPERIMENT_ID` varchar(255) NOT NULL,
- `STATE` varchar(255) DEFAULT NULL,
- `TIME_OF_STATE_CHANGE` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
- `REASON` longtext,
- PRIMARY KEY (`STATUS_ID`,`EXPERIMENT_ID`),
- KEY `EXPERIMENT_ID` (`EXPERIMENT_ID`),
- CONSTRAINT `experiment_status_ibfk_1` FOREIGN KEY (`EXPERIMENT_ID`) REFERENCES `EXPERIMENT` (`EXPERIMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `EXPERIMENT_STATUS`
---
-
-LOCK TABLES `EXPERIMENT_STATUS` WRITE;
-/*!40000 ALTER TABLE `EXPERIMENT_STATUS` DISABLE KEYS */;
-/*!40000 ALTER TABLE `EXPERIMENT_STATUS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GATEWAY`
---
-
-DROP TABLE IF EXISTS `GATEWAY`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GATEWAY` (
- `GATEWAY_ID` varchar(255) NOT NULL,
- `GATEWAY_NAME` varchar(255) DEFAULT NULL,
- `DOMAIN` varchar(255) DEFAULT NULL,
- `EMAIL_ADDRESS` varchar(255) DEFAULT NULL,
- `GATEWAY_ACRONYM` varchar(255) DEFAULT NULL,
- `GATEWAY_ADMIN_EMAIL` varchar(255) DEFAULT NULL,
- `GATEWAY_ADMIN_FIRST_NAME` varchar(255) DEFAULT NULL,
- `GATEWAY_APPROVAL_STATUS` varchar(255) DEFAULT NULL,
- `GATEWAY_PUBLIC_ABSTRACT` varchar(255) DEFAULT NULL,
- `GATEWAY_URL` varchar(255) DEFAULT NULL,
- `GATEWAY_ADMIN_LAST_NAME` varchar(255) DEFAULT NULL,
- `IDENTITY_SERVER_PASSWORD_TOKEN` varchar(255) DEFAULT NULL,
- `IDENTITY_SERVER_USERNAME` varchar(255) DEFAULT NULL,
- `GATEWAY_REVIEW_PROPOSAL_DESCRIPTION` varchar(255) DEFAULT NULL,
- `DECLINED_REASON` varchar(255) DEFAULT NULL,
- `OAUTH_CLIENT_SECRET` varchar(255) DEFAULT NULL,
- `OAUTH_CLIENT_ID` varchar(255) DEFAULT NULL,
- `REQUEST_CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `REQUESTER_USERNAME` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`GATEWAY_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GATEWAY`
---
-
-LOCK TABLES `GATEWAY` WRITE;
-/*!40000 ALTER TABLE `GATEWAY` DISABLE KEYS */;
-INSERT INTO `GATEWAY` VALUES ('default',NULL,NULL,NULL,NULL,NULL,NULL,'APPROVED',NULL,NULL,NULL,NULL,NULL,NULL,NULL,'9790c8c4-7d9b-4ccc-a820-ca5aac38d2ad','pga','2019-02-25 18:40:06',NULL);
-/*!40000 ALTER TABLE `GATEWAY` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GATEWAY_WORKER`
---
-
-DROP TABLE IF EXISTS `GATEWAY_WORKER`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GATEWAY_WORKER` (
- `GATEWAY_ID` varchar(255) NOT NULL,
- `USER_NAME` varchar(255) NOT NULL,
- PRIMARY KEY (`GATEWAY_ID`,`USER_NAME`),
- CONSTRAINT `gateway_worker_ibfk_1` FOREIGN KEY (`GATEWAY_ID`) REFERENCES `GATEWAY` (`GATEWAY_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GATEWAY_WORKER`
---
-
-LOCK TABLES `GATEWAY_WORKER` WRITE;
-/*!40000 ALTER TABLE `GATEWAY_WORKER` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GATEWAY_WORKER` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `JOB`
---
-
-DROP TABLE IF EXISTS `JOB`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `JOB` (
- `JOB_ID` varchar(255) NOT NULL,
- `TASK_ID` varchar(255) NOT NULL,
- `PROCESS_ID` varchar(255) DEFAULT NULL,
- `JOB_DESCRIPTION` longtext NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `COMPUTE_RESOURCE_CONSUMED` varchar(255) DEFAULT NULL,
- `JOB_NAME` varchar(255) DEFAULT NULL,
- `WORKING_DIR` varchar(255) DEFAULT NULL,
- `STD_OUT` longtext,
- `STD_ERR` longtext,
- `EXIT_CODE` int(11) DEFAULT NULL,
- PRIMARY KEY (`JOB_ID`,`TASK_ID`),
- KEY `TASK_ID` (`TASK_ID`),
- CONSTRAINT `job_ibfk_1` FOREIGN KEY (`TASK_ID`) REFERENCES `TASK` (`TASK_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `JOB`
---
-
-LOCK TABLES `JOB` WRITE;
-/*!40000 ALTER TABLE `JOB` DISABLE KEYS */;
-/*!40000 ALTER TABLE `JOB` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `JOB_STATUS`
---
-
-DROP TABLE IF EXISTS `JOB_STATUS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `JOB_STATUS` (
- `STATUS_ID` varchar(255) NOT NULL,
- `JOB_ID` varchar(255) NOT NULL,
- `TASK_ID` varchar(255) NOT NULL,
- `STATE` varchar(255) DEFAULT NULL,
- `TIME_OF_STATE_CHANGE` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
- `REASON` longtext,
- PRIMARY KEY (`STATUS_ID`,`JOB_ID`,`TASK_ID`),
- KEY `JOB_ID` (`JOB_ID`,`TASK_ID`),
- CONSTRAINT `job_status_ibfk_1` FOREIGN KEY (`JOB_ID`, `TASK_ID`) REFERENCES `JOB` (`JOB_ID`, `TASK_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `JOB_STATUS`
---
-
-LOCK TABLES `JOB_STATUS` WRITE;
-/*!40000 ALTER TABLE `JOB_STATUS` DISABLE KEYS */;
-/*!40000 ALTER TABLE `JOB_STATUS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `NOTIFICATION`
---
-
-DROP TABLE IF EXISTS `NOTIFICATION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `NOTIFICATION` (
- `NOTIFICATION_ID` varchar(255) NOT NULL,
- `GATEWAY_ID` varchar(255) DEFAULT NULL,
- `TITLE` varchar(255) DEFAULT NULL,
- `PRIORITY` varchar(255) DEFAULT NULL,
- `NOTIFICATION_MESSAGE` varchar(4096) NOT NULL,
- `PUBLISHED_DATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `EXPIRATION_DATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `CREATION_DATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- PRIMARY KEY (`NOTIFICATION_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `NOTIFICATION`
---
-
-LOCK TABLES `NOTIFICATION` WRITE;
-/*!40000 ALTER TABLE `NOTIFICATION` DISABLE KEYS */;
-/*!40000 ALTER TABLE `NOTIFICATION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PROCESS`
---
-
-DROP TABLE IF EXISTS `PROCESS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PROCESS` (
- `PROCESS_ID` varchar(255) NOT NULL,
- `EXPERIMENT_ID` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `LAST_UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `PROCESS_DETAIL` text,
- `APPLICATION_INTERFACE_ID` varchar(255) DEFAULT NULL,
- `TASK_DAG` text,
- `APPLICATION_DEPLOYMENT_ID` varchar(255) DEFAULT NULL,
- `COMPUTE_RESOURCE_ID` varchar(255) DEFAULT NULL,
- `GATEWAY_EXECUTION_ID` varchar(255) DEFAULT NULL,
- `ENABLE_EMAIL_NOTIFICATION` tinyint(1) DEFAULT NULL,
- `EMAIL_ADDRESSES` text,
- `STORAGE_RESOURCE_ID` varchar(255) DEFAULT NULL,
- `USER_DN` varchar(255) DEFAULT NULL,
- `GENERATE_CERT` smallint(6) DEFAULT NULL,
- `EXPERIMENT_DATA_DIR` varchar(512) DEFAULT NULL,
- `USERNAME` varchar(255) DEFAULT NULL,
- `GROUP_RESOURCE_PROFILE_ID` varchar(255) DEFAULT NULL,
- `USE_USER_CR_PREF` tinyint(1) DEFAULT NULL,
- PRIMARY KEY (`PROCESS_ID`),
- KEY `EXPERIMENT_ID` (`EXPERIMENT_ID`),
- CONSTRAINT `process_ibfk_1` FOREIGN KEY (`EXPERIMENT_ID`) REFERENCES `EXPERIMENT` (`EXPERIMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PROCESS`
---
-
-LOCK TABLES `PROCESS` WRITE;
-/*!40000 ALTER TABLE `PROCESS` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PROCESS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PROCESS_ERROR`
---
-
-DROP TABLE IF EXISTS `PROCESS_ERROR`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PROCESS_ERROR` (
- `ERROR_ID` varchar(255) NOT NULL,
- `PROCESS_ID` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `ACTUAL_ERROR_MESSAGE` text,
- `USER_FRIENDLY_MESSAGE` text,
- `TRANSIENT_OR_PERSISTENT` tinyint(1) DEFAULT NULL,
- `ROOT_CAUSE_ERROR_ID_LIST` text,
- PRIMARY KEY (`ERROR_ID`,`PROCESS_ID`),
- KEY `PROCESS_ID` (`PROCESS_ID`),
- CONSTRAINT `process_error_ibfk_1` FOREIGN KEY (`PROCESS_ID`) REFERENCES `PROCESS` (`PROCESS_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PROCESS_ERROR`
---
-
-LOCK TABLES `PROCESS_ERROR` WRITE;
-/*!40000 ALTER TABLE `PROCESS_ERROR` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PROCESS_ERROR` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PROCESS_INPUT`
---
-
-DROP TABLE IF EXISTS `PROCESS_INPUT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PROCESS_INPUT` (
- `PROCESS_ID` varchar(255) NOT NULL,
- `INPUT_NAME` varchar(255) NOT NULL,
- `INPUT_VALUE` text,
- `DATA_TYPE` varchar(255) DEFAULT NULL,
- `APPLICATION_ARGUMENT` varchar(255) DEFAULT NULL,
- `STANDARD_INPUT` tinyint(1) DEFAULT NULL,
- `USER_FRIENDLY_DESCRIPTION` varchar(255) DEFAULT NULL,
- `METADATA` varchar(4096) DEFAULT NULL,
- `INPUT_ORDER` int(11) DEFAULT NULL,
- `IS_REQUIRED` tinyint(1) DEFAULT NULL,
- `REQUIRED_TO_ADDED_TO_CMD` tinyint(1) DEFAULT NULL,
- `DATA_STAGED` tinyint(1) DEFAULT NULL,
- `STORAGE_RESOURCE_ID` varchar(255) DEFAULT NULL,
- `IS_READ_ONLY` tinyint(1) DEFAULT NULL,
- PRIMARY KEY (`PROCESS_ID`,`INPUT_NAME`),
- CONSTRAINT `process_input_ibfk_1` FOREIGN KEY (`PROCESS_ID`) REFERENCES `PROCESS` (`PROCESS_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PROCESS_INPUT`
---
-
-LOCK TABLES `PROCESS_INPUT` WRITE;
-/*!40000 ALTER TABLE `PROCESS_INPUT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PROCESS_INPUT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PROCESS_OUTPUT`
---
-
-DROP TABLE IF EXISTS `PROCESS_OUTPUT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PROCESS_OUTPUT` (
- `PROCESS_ID` varchar(255) NOT NULL,
- `OUTPUT_NAME` varchar(255) NOT NULL,
- `OUTPUT_VALUE` longtext,
- `DATA_TYPE` varchar(255) DEFAULT NULL,
- `APPLICATION_ARGUMENT` varchar(255) DEFAULT NULL,
- `IS_REQUIRED` tinyint(1) DEFAULT NULL,
- `REQUIRED_TO_ADDED_TO_CMD` tinyint(1) DEFAULT NULL,
- `DATA_MOVEMENT` tinyint(1) DEFAULT NULL,
- `LOCATION` varchar(255) DEFAULT NULL,
- `SEARCH_QUERY` varchar(255) DEFAULT NULL,
- `OUTPUT_STREAMING` smallint(6) DEFAULT NULL,
- `STORAGE_RESOURCE_ID` varchar(255) DEFAULT NULL,
- `METADATA` varchar(4096) DEFAULT NULL,
- PRIMARY KEY (`PROCESS_ID`,`OUTPUT_NAME`),
- CONSTRAINT `process_output_ibfk_1` FOREIGN KEY (`PROCESS_ID`) REFERENCES `PROCESS` (`PROCESS_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PROCESS_OUTPUT`
---
-
-LOCK TABLES `PROCESS_OUTPUT` WRITE;
-/*!40000 ALTER TABLE `PROCESS_OUTPUT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PROCESS_OUTPUT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PROCESS_RESOURCE_SCHEDULE`
---
-
-DROP TABLE IF EXISTS `PROCESS_RESOURCE_SCHEDULE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PROCESS_RESOURCE_SCHEDULE` (
- `PROCESS_ID` varchar(255) NOT NULL,
- `RESOURCE_HOST_ID` varchar(255) DEFAULT NULL,
- `TOTAL_CPU_COUNT` int(11) DEFAULT NULL,
- `NODE_COUNT` int(11) DEFAULT NULL,
- `NUMBER_OF_THREADS` int(11) DEFAULT NULL,
- `QUEUE_NAME` varchar(255) DEFAULT NULL,
- `WALL_TIME_LIMIT` int(11) DEFAULT NULL,
- `TOTAL_PHYSICAL_MEMORY` int(11) DEFAULT NULL,
- `STATIC_WORKING_DIR` varchar(255) DEFAULT NULL,
- `OVERRIDE_ALLOCATION_PROJECT_NUMBER` varchar(255) DEFAULT NULL,
- `OVERRIDE_LOGIN_USER_NAME` varchar(255) DEFAULT NULL,
- `OVERRIDE_SCRATCH_LOCATION` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`PROCESS_ID`),
- CONSTRAINT `process_resource_schedule_ibfk_1` FOREIGN KEY (`PROCESS_ID`) REFERENCES `PROCESS` (`PROCESS_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PROCESS_RESOURCE_SCHEDULE`
---
-
-LOCK TABLES `PROCESS_RESOURCE_SCHEDULE` WRITE;
-/*!40000 ALTER TABLE `PROCESS_RESOURCE_SCHEDULE` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PROCESS_RESOURCE_SCHEDULE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PROCESS_STATUS`
---
-
-DROP TABLE IF EXISTS `PROCESS_STATUS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PROCESS_STATUS` (
- `STATUS_ID` varchar(255) NOT NULL,
- `PROCESS_ID` varchar(255) NOT NULL,
- `STATE` varchar(255) DEFAULT NULL,
- `TIME_OF_STATE_CHANGE` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
- `REASON` longtext,
- PRIMARY KEY (`STATUS_ID`,`PROCESS_ID`),
- KEY `PROCESS_ID` (`PROCESS_ID`),
- CONSTRAINT `process_status_ibfk_1` FOREIGN KEY (`PROCESS_ID`) REFERENCES `PROCESS` (`PROCESS_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PROCESS_STATUS`
---
-
-LOCK TABLES `PROCESS_STATUS` WRITE;
-/*!40000 ALTER TABLE `PROCESS_STATUS` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PROCESS_STATUS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PROJECT`
---
-
-DROP TABLE IF EXISTS `PROJECT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PROJECT` (
- `GATEWAY_ID` varchar(255) NOT NULL,
- `USER_NAME` varchar(255) DEFAULT NULL,
- `PROJECT_NAME` varchar(255) DEFAULT NULL,
- `PROJECT_ID` varchar(255) NOT NULL,
- `DESCRIPTION` varchar(255) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (`PROJECT_ID`),
- KEY `GATEWAY_ID` (`GATEWAY_ID`),
- CONSTRAINT `project_ibfk_1` FOREIGN KEY (`GATEWAY_ID`) REFERENCES `GATEWAY` (`GATEWAY_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PROJECT`
---
-
-LOCK TABLES `PROJECT` WRITE;
-/*!40000 ALTER TABLE `PROJECT` DISABLE KEYS */;
-INSERT INTO `PROJECT` VALUES ('default','default-admin','Default Project','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','This is the default project for user default-admin','2019-02-25 18:46:45');
-/*!40000 ALTER TABLE `PROJECT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PROJECT_USER`
---
-
-DROP TABLE IF EXISTS `PROJECT_USER`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PROJECT_USER` (
- `PROJECT_ID` varchar(255) NOT NULL,
- `USER_NAME` varchar(255) NOT NULL,
- PRIMARY KEY (`PROJECT_ID`,`USER_NAME`),
- CONSTRAINT `project_user_ibfk_1` FOREIGN KEY (`PROJECT_ID`) REFERENCES `PROJECT` (`PROJECT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PROJECT_USER`
---
-
-LOCK TABLES `PROJECT_USER` WRITE;
-/*!40000 ALTER TABLE `PROJECT_USER` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PROJECT_USER` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `QUEUE_STATUS`
---
-
-DROP TABLE IF EXISTS `QUEUE_STATUS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `QUEUE_STATUS` (
- `HOST_NAME` varchar(255) NOT NULL,
- `QUEUE_NAME` varchar(255) NOT NULL,
- `CREATED_TIME` bigint(20) NOT NULL,
- `QUEUE_UP` tinyint(1) DEFAULT NULL,
- `RUNNING_JOBS` int(11) DEFAULT NULL,
- `QUEUED_JOBS` int(11) DEFAULT NULL,
- PRIMARY KEY (`HOST_NAME`,`QUEUE_NAME`,`CREATED_TIME`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `QUEUE_STATUS`
---
-
-LOCK TABLES `QUEUE_STATUS` WRITE;
-/*!40000 ALTER TABLE `QUEUE_STATUS` DISABLE KEYS */;
-/*!40000 ALTER TABLE `QUEUE_STATUS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `TASK`
---
-
-DROP TABLE IF EXISTS `TASK`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `TASK` (
- `TASK_ID` varchar(255) NOT NULL,
- `TASK_TYPE` varchar(255) DEFAULT NULL,
- `PARENT_PROCESS_ID` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `LAST_UPDATE_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `TASK_DETAIL` text,
- `SUB_TASK_MODEL` blob,
- PRIMARY KEY (`TASK_ID`),
- KEY `PARENT_PROCESS_ID` (`PARENT_PROCESS_ID`),
- CONSTRAINT `task_ibfk_1` FOREIGN KEY (`PARENT_PROCESS_ID`) REFERENCES `PROCESS` (`PROCESS_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `TASK`
---
-
-LOCK TABLES `TASK` WRITE;
-/*!40000 ALTER TABLE `TASK` DISABLE KEYS */;
-/*!40000 ALTER TABLE `TASK` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `TASK_ERROR`
---
-
-DROP TABLE IF EXISTS `TASK_ERROR`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `TASK_ERROR` (
- `ERROR_ID` varchar(255) NOT NULL,
- `TASK_ID` varchar(255) NOT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `ACTUAL_ERROR_MESSAGE` text,
- `USER_FRIENDLY_MESSAGE` text,
- `TRANSIENT_OR_PERSISTENT` tinyint(1) DEFAULT NULL,
- `ROOT_CAUSE_ERROR_ID_LIST` text,
- PRIMARY KEY (`ERROR_ID`,`TASK_ID`),
- KEY `TASK_ID` (`TASK_ID`),
- CONSTRAINT `task_error_ibfk_1` FOREIGN KEY (`TASK_ID`) REFERENCES `TASK` (`TASK_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `TASK_ERROR`
---
-
-LOCK TABLES `TASK_ERROR` WRITE;
-/*!40000 ALTER TABLE `TASK_ERROR` DISABLE KEYS */;
-/*!40000 ALTER TABLE `TASK_ERROR` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `TASK_STATUS`
---
-
-DROP TABLE IF EXISTS `TASK_STATUS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `TASK_STATUS` (
- `STATUS_ID` varchar(255) NOT NULL,
- `TASK_ID` varchar(255) NOT NULL,
- `STATE` varchar(255) DEFAULT NULL,
- `TIME_OF_STATE_CHANGE` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
- `REASON` longtext,
- PRIMARY KEY (`STATUS_ID`,`TASK_ID`),
- KEY `TASK_ID` (`TASK_ID`),
- CONSTRAINT `task_status_ibfk_1` FOREIGN KEY (`TASK_ID`) REFERENCES `TASK` (`TASK_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `TASK_STATUS`
---
-
-LOCK TABLES `TASK_STATUS` WRITE;
-/*!40000 ALTER TABLE `TASK_STATUS` DISABLE KEYS */;
-/*!40000 ALTER TABLE `TASK_STATUS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USERS`
---
-
-DROP TABLE IF EXISTS `USERS`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USERS` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `USER_NAME` varchar(255) NOT NULL,
- `PASSWORD` varchar(255) DEFAULT NULL,
- `GATEWAY_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`GATEWAY_ID`,`USER_NAME`),
- UNIQUE KEY `AIRAVATA_INTERNAL_USER_ID` (`AIRAVATA_INTERNAL_USER_ID`),
- CONSTRAINT `users_ibfk_1` FOREIGN KEY (`GATEWAY_ID`) REFERENCES `GATEWAY` (`GATEWAY_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USERS`
---
-
-LOCK TABLES `USERS` WRITE;
-/*!40000 ALTER TABLE `USERS` DISABLE KEYS */;
-INSERT INTO `USERS` VALUES ('DO_NOT_SET_AT_CLIENTS','default-admin',NULL,'default');
-/*!40000 ALTER TABLE `USERS` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USER_CONFIGURATION_DATA`
---
-
-DROP TABLE IF EXISTS `USER_CONFIGURATION_DATA`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USER_CONFIGURATION_DATA` (
- `EXPERIMENT_ID` varchar(255) NOT NULL,
- `AIRAVATA_AUTO_SCHEDULE` tinyint(1) DEFAULT NULL,
- `OVERRIDE_MANUAL_SCHEDULED_PARAMS` tinyint(1) DEFAULT NULL,
- `SHARE_EXPERIMENT_PUBLICALLY` tinyint(1) DEFAULT NULL,
- `THROTTLE_RESOURCES` tinyint(1) DEFAULT NULL,
- `USER_DN` varchar(255) DEFAULT NULL,
- `GENERATE_CERT` tinyint(1) DEFAULT NULL,
- `RESOURCE_HOST_ID` varchar(255) DEFAULT NULL,
- `TOTAL_CPU_COUNT` int(11) DEFAULT NULL,
- `NODE_COUNT` int(11) DEFAULT NULL,
- `NUMBER_OF_THREADS` int(11) DEFAULT NULL,
- `QUEUE_NAME` varchar(255) DEFAULT NULL,
- `WALL_TIME_LIMIT` int(11) DEFAULT NULL,
- `TOTAL_PHYSICAL_MEMORY` int(11) DEFAULT NULL,
- `STATIC_WORKING_DIR` varchar(255) DEFAULT NULL,
- `OVERRIDE_LOGIN_USER_NAME` varchar(255) DEFAULT NULL,
- `OVERRIDE_SCRATCH_LOCATION` varchar(255) DEFAULT NULL,
- `OVERRIDE_ALLOCATION_PROJECT_NUMBER` varchar(255) DEFAULT NULL,
- `STORAGE_RESOURCE_ID` varchar(255) DEFAULT NULL,
- `EXPERIMENT_DATA_DIR` varchar(512) DEFAULT NULL,
- `GROUP_RESOURCE_PROFILE_ID` varchar(255) DEFAULT NULL,
- `IS_USE_USER_CR_PREF` tinyint(1) DEFAULT NULL,
- PRIMARY KEY (`EXPERIMENT_ID`),
- CONSTRAINT `user_configuration_data_ibfk_1` FOREIGN KEY (`EXPERIMENT_ID`) REFERENCES `EXPERIMENT` (`EXPERIMENT_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USER_CONFIGURATION_DATA`
---
-
-LOCK TABLES `USER_CONFIGURATION_DATA` WRITE;
-/*!40000 ALTER TABLE `USER_CONFIGURATION_DATA` DISABLE KEYS */;
-/*!40000 ALTER TABLE `USER_CONFIGURATION_DATA` ENABLE KEYS */;
-UNLOCK TABLES;
-
-DROP TABLE IF EXISTS `PROCESS_WORKFLOW`;
-CREATE TABLE PROCESS_WORKFLOW
-(
- `PROCESS_ID` varchar(255) NOT NULL,
- `WORKFLOW_ID` varchar(255) NOT NULL,
- `TYPE` varchar(255) DEFAULT NULL,
- `CREATION_TIME` timestamp DEFAULT NOW(),
- PRIMARY KEY (`PROCESS_ID`, `WORKFLOW_ID`),
- FOREIGN KEY (`PROCESS_ID`) REFERENCES PROCESS(`PROCESS_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-
-
-LOCK TABLES `PROCESS_WORKFLOW` WRITE;
-/*!40000 ALTER TABLE `PROCESS_WORKFLOW` DISABLE KEYS */;
-/*!40000 ALTER TABLE `PROCESS_WORKFLOW` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Temporary table structure for view `experiment_summary`
---
-
-DROP TABLE IF EXISTS `experiment_summary`;
-/*!50001 DROP VIEW IF EXISTS `experiment_summary`*/;
-SET @saved_cs_client = @@character_set_client;
-SET character_set_client = utf8;
-/*!50001 CREATE VIEW `experiment_summary` AS SELECT
- 1 AS `EXPERIMENT_ID`,
- 1 AS `PROJECT_ID`,
- 1 AS `GATEWAY_ID`,
- 1 AS `USER_NAME`,
- 1 AS `EXECUTION_ID`,
- 1 AS `EXPERIMENT_NAME`,
- 1 AS `CREATION_TIME`,
- 1 AS `DESCRIPTION`,
- 1 AS `STATE`,
- 1 AS `RESOURCE_HOST_ID`,
- 1 AS `TIME_OF_STATE_CHANGE`*/;
-SET character_set_client = @saved_cs_client;
-
---
--- Temporary table structure for view `latest_experiment_status`
---
-
-DROP TABLE IF EXISTS `latest_experiment_status`;
-/*!50001 DROP VIEW IF EXISTS `latest_experiment_status`*/;
-SET @saved_cs_client = @@character_set_client;
-SET character_set_client = utf8;
-/*!50001 CREATE VIEW `latest_experiment_status` AS SELECT
- 1 AS `EXPERIMENT_ID`,
- 1 AS `STATE`,
- 1 AS `TIME_OF_STATE_CHANGE`*/;
-SET character_set_client = @saved_cs_client;
-
---
--- Current Database: `profile_service`
---
-
-CREATE DATABASE /*!32312 IF NOT EXISTS*/ `profile_service` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
-
-USE `profile_service`;
-
---
--- Table structure for table `CONFIGURATION`
---
-
-DROP TABLE IF EXISTS `CONFIGURATION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `CONFIGURATION` (
- `CONFIG_KEY` varchar(255) NOT NULL,
- `CONFIG_VAL` varchar(255) NOT NULL,
- PRIMARY KEY (`CONFIG_KEY`,`CONFIG_VAL`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `CONFIGURATION`
---
-
-LOCK TABLES `CONFIGURATION` WRITE;
-/*!40000 ALTER TABLE `CONFIGURATION` DISABLE KEYS */;
-INSERT INTO `CONFIGURATION` VALUES ('user_profile_catalog_version','0.17');
-/*!40000 ALTER TABLE `CONFIGURATION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `CUSTOMIZED_DASHBOARD`
---
-
-DROP TABLE IF EXISTS `CUSTOMIZED_DASHBOARD`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `CUSTOMIZED_DASHBOARD` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `ENABLED_EXPERIMENT_ID` varchar(255) DEFAULT NULL,
- `ENABLED_NAME` varchar(255) DEFAULT NULL,
- `ENABLED_DESCRIPTION` varchar(255) DEFAULT NULL,
- `ENABLED_PROJECT` varchar(255) DEFAULT NULL,
- `ENABLED_OWNER` varchar(255) DEFAULT NULL,
- `ENABLED_APPLICATION` varchar(255) DEFAULT NULL,
- `ENABLED_COMPUTE_RESOURCE` varchar(255) DEFAULT NULL,
- `ENABLED_JOB_NAME` varchar(255) DEFAULT NULL,
- `ENABLED_JOB_ID` varchar(255) DEFAULT NULL,
- `ENABLED_JOB_STATUS` varchar(255) DEFAULT NULL,
- `ENABLED_JOB_CREATION_TIME` varchar(255) DEFAULT NULL,
- `ENABLED_NOTIFICATIONS_TO` varchar(255) DEFAULT NULL,
- `ENABLED_WORKING_DIR` varchar(255) DEFAULT NULL,
- `ENABLED_JOB_DESCRIPTION` varchar(255) DEFAULT NULL,
- `ENABLED_CREATION_TIME` varchar(255) DEFAULT NULL,
- `ENABLED_LAST_MODIFIED_TIME` varchar(255) DEFAULT NULL,
- `ENABLED_WALL_TIME` varchar(255) DEFAULT NULL,
- `ENABLED_CPU_COUNT` varchar(255) DEFAULT NULL,
- `ENABLED_NODE_COUNT` varchar(255) DEFAULT NULL,
- `ENABLED_QUEUE` varchar(255) DEFAULT NULL,
- `ENABLED_INPUTS` varchar(255) DEFAULT NULL,
- `ENABLED_OUTPUTS` varchar(255) DEFAULT NULL,
- `ENABLED_STORAGE_DIR` varchar(255) DEFAULT NULL,
- `ENABLED_ERRORS` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_USER_ID`),
- CONSTRAINT `customized_dashboard_ibfk_1` FOREIGN KEY (`AIRAVATA_INTERNAL_USER_ID`) REFERENCES `USER_PROFILE` (`AIRAVATA_INTERNAL_USER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `CUSTOMIZED_DASHBOARD`
---
-
-LOCK TABLES `CUSTOMIZED_DASHBOARD` WRITE;
-/*!40000 ALTER TABLE `CUSTOMIZED_DASHBOARD` DISABLE KEYS */;
-/*!40000 ALTER TABLE `CUSTOMIZED_DASHBOARD` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GATEWAY`
---
-
-DROP TABLE IF EXISTS `GATEWAY`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GATEWAY` (
- `AIRAVATA_INTERNAL_GATEWAY_ID` varchar(255) NOT NULL,
- `DECLINED_REASON` varchar(255) DEFAULT NULL,
- `GATEWAY_DOMAIN` varchar(255) DEFAULT NULL,
- `EMAIL_ADDRESS` varchar(255) DEFAULT NULL,
- `GATEWAY_ACRONYM` varchar(255) DEFAULT NULL,
- `GATEWAY_ADMIN_EMAIL` varchar(255) DEFAULT NULL,
- `GATEWAY_ADMIN_FIRST_NAME` varchar(255) DEFAULT NULL,
- `GATEWAY_ADMIN_LAST_NAME` varchar(255) DEFAULT NULL,
- `GATEWAY_APPROVAL_STATUS` varchar(255) DEFAULT NULL,
- `GATEWAY_ID` varchar(255) DEFAULT NULL,
- `GATEWAY_NAME` varchar(255) DEFAULT NULL,
- `GATEWAY_PUBLIC_ABSTRACT` varchar(255) DEFAULT NULL,
- `GATEWAY_URL` varchar(255) DEFAULT NULL,
- `IDENTITY_SERVER_PASSWORD_TOKEN` varchar(255) DEFAULT NULL,
- `IDENTITY_SERVER_USERNAME` varchar(255) DEFAULT NULL,
- `OAUTH_CLIENT_ID` varchar(255) DEFAULT NULL,
- `OAUTH_CLIENT_SECRET` varchar(255) DEFAULT NULL,
- `REQUEST_CREATION_TIME` bigint(20) DEFAULT NULL,
- `REQUESTER_USERNAME` varchar(255) DEFAULT NULL,
- `GATEWAY_REVIEW_PROPOSAL_DESCRIPTION` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_GATEWAY_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GATEWAY`
---
-
-LOCK TABLES `GATEWAY` WRITE;
-/*!40000 ALTER TABLE `GATEWAY` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GATEWAY` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `NSF_DEMOGRAPHIC`
---
-
-DROP TABLE IF EXISTS `NSF_DEMOGRAPHIC`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `NSF_DEMOGRAPHIC` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `GENDER` varchar(255) NOT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_USER_ID`),
- CONSTRAINT `nsf_demographic_ibfk_1` FOREIGN KEY (`AIRAVATA_INTERNAL_USER_ID`) REFERENCES `USER_PROFILE` (`AIRAVATA_INTERNAL_USER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `NSF_DEMOGRAPHIC`
---
-
-LOCK TABLES `NSF_DEMOGRAPHIC` WRITE;
-/*!40000 ALTER TABLE `NSF_DEMOGRAPHIC` DISABLE KEYS */;
-/*!40000 ALTER TABLE `NSF_DEMOGRAPHIC` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `NSF_DEMOGRAPHIC_DISABILITY`
---
-
-DROP TABLE IF EXISTS `NSF_DEMOGRAPHIC_DISABILITY`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `NSF_DEMOGRAPHIC_DISABILITY` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `DISABILITY` varchar(255) NOT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_USER_ID`,`DISABILITY`),
- CONSTRAINT `nsf_demographic_disability_ibfk_1` FOREIGN KEY (`AIRAVATA_INTERNAL_USER_ID`) REFERENCES `NSF_DEMOGRAPHIC` (`AIRAVATA_INTERNAL_USER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `NSF_DEMOGRAPHIC_DISABILITY`
---
-
-LOCK TABLES `NSF_DEMOGRAPHIC_DISABILITY` WRITE;
-/*!40000 ALTER TABLE `NSF_DEMOGRAPHIC_DISABILITY` DISABLE KEYS */;
-/*!40000 ALTER TABLE `NSF_DEMOGRAPHIC_DISABILITY` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `NSF_DEMOGRAPHIC_ETHNICITY`
---
-
-DROP TABLE IF EXISTS `NSF_DEMOGRAPHIC_ETHNICITY`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `NSF_DEMOGRAPHIC_ETHNICITY` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `ETHNICITY` varchar(255) NOT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_USER_ID`,`ETHNICITY`),
- CONSTRAINT `nsf_demographic_ethnicity_ibfk_1` FOREIGN KEY (`AIRAVATA_INTERNAL_USER_ID`) REFERENCES `NSF_DEMOGRAPHIC` (`AIRAVATA_INTERNAL_USER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `NSF_DEMOGRAPHIC_ETHNICITY`
---
-
-LOCK TABLES `NSF_DEMOGRAPHIC_ETHNICITY` WRITE;
-/*!40000 ALTER TABLE `NSF_DEMOGRAPHIC_ETHNICITY` DISABLE KEYS */;
-/*!40000 ALTER TABLE `NSF_DEMOGRAPHIC_ETHNICITY` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `NSF_DEMOGRAPHIC_RACE`
---
-
-DROP TABLE IF EXISTS `NSF_DEMOGRAPHIC_RACE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `NSF_DEMOGRAPHIC_RACE` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `RACE` varchar(255) NOT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_USER_ID`,`RACE`),
- CONSTRAINT `nsf_demographic_race_ibfk_1` FOREIGN KEY (`AIRAVATA_INTERNAL_USER_ID`) REFERENCES `NSF_DEMOGRAPHIC` (`AIRAVATA_INTERNAL_USER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `NSF_DEMOGRAPHIC_RACE`
---
-
-LOCK TABLES `NSF_DEMOGRAPHIC_RACE` WRITE;
-/*!40000 ALTER TABLE `NSF_DEMOGRAPHIC_RACE` DISABLE KEYS */;
-/*!40000 ALTER TABLE `NSF_DEMOGRAPHIC_RACE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USER_PROFILE`
---
-
-DROP TABLE IF EXISTS `USER_PROFILE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USER_PROFILE` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `USER_ID` varchar(255) NOT NULL,
- `GATEWAY_ID` varchar(255) NOT NULL,
- `USER_MODEL_VERSION` varchar(255) DEFAULT NULL,
- `FIRST_NAME` varchar(255) DEFAULT NULL,
- `LAST_NAME` varchar(255) DEFAULT NULL,
- `MIDDLE_NAME` varchar(255) DEFAULT NULL,
- `NAME_PREFIX` varchar(255) DEFAULT NULL,
- `NAME_SUFFIX` varchar(255) DEFAULT NULL,
- `ORCID_ID` varchar(255) DEFAULT NULL,
- `COUNTRY` varchar(255) DEFAULT NULL,
- `HOME_ORGANIZATION` varchar(255) DEFAULT NULL,
- `ORIGINATION_AFFILIATION` varchar(255) DEFAULT NULL,
- `CREATION_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `LAST_ACCESS_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
- `VALID_UNTIL` datetime DEFAULT NULL,
- `STATE` varchar(255) DEFAULT NULL,
- `COMMENTS` text,
- `GPG_KEY` text,
- `TIME_ZONE` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_USER_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USER_PROFILE`
---
-
-LOCK TABLES `USER_PROFILE` WRITE;
-/*!40000 ALTER TABLE `USER_PROFILE` DISABLE KEYS */;
-INSERT INTO `USER_PROFILE` VALUES ('default-admin@default','default-admin','default','1.0','dim','Upe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,'2019-02-25 18:46:43','2019-02-25 18:46:43','1969-12-31 19:00:00','ACTIVE',NULL,NULL,NULL);
-/*!40000 ALTER TABLE `USER_PROFILE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USER_PROFILE_EMAIL`
---
-
-DROP TABLE IF EXISTS `USER_PROFILE_EMAIL`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USER_PROFILE_EMAIL` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `EMAIL` varchar(255) NOT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_USER_ID`,`EMAIL`),
- CONSTRAINT `user_profile_email_ibfk_1` FOREIGN KEY (`AIRAVATA_INTERNAL_USER_ID`) REFERENCES `USER_PROFILE` (`AIRAVATA_INTERNAL_USER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USER_PROFILE_EMAIL`
---
-
-LOCK TABLES `USER_PROFILE_EMAIL` WRITE;
-/*!40000 ALTER TABLE `USER_PROFILE_EMAIL` DISABLE KEYS */;
-INSERT INTO `USER_PROFILE_EMAIL` VALUES ('default-admin@default','dimuthu.upeksha2@gmail.com');
-/*!40000 ALTER TABLE `USER_PROFILE_EMAIL` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USER_PROFILE_LABELED_URI`
---
-
-DROP TABLE IF EXISTS `USER_PROFILE_LABELED_URI`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USER_PROFILE_LABELED_URI` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `LABELED_URI` varchar(255) NOT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_USER_ID`,`LABELED_URI`),
- CONSTRAINT `user_profile_labeled_uri_ibfk_1` FOREIGN KEY (`AIRAVATA_INTERNAL_USER_ID`) REFERENCES `USER_PROFILE` (`AIRAVATA_INTERNAL_USER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USER_PROFILE_LABELED_URI`
---
-
-LOCK TABLES `USER_PROFILE_LABELED_URI` WRITE;
-/*!40000 ALTER TABLE `USER_PROFILE_LABELED_URI` DISABLE KEYS */;
-/*!40000 ALTER TABLE `USER_PROFILE_LABELED_URI` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USER_PROFILE_NATIONALITY`
---
-
-DROP TABLE IF EXISTS `USER_PROFILE_NATIONALITY`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USER_PROFILE_NATIONALITY` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `NATIONALITY` varchar(255) NOT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_USER_ID`,`NATIONALITY`),
- CONSTRAINT `user_profile_nationality_ibfk_1` FOREIGN KEY (`AIRAVATA_INTERNAL_USER_ID`) REFERENCES `USER_PROFILE` (`AIRAVATA_INTERNAL_USER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USER_PROFILE_NATIONALITY`
---
-
-LOCK TABLES `USER_PROFILE_NATIONALITY` WRITE;
-/*!40000 ALTER TABLE `USER_PROFILE_NATIONALITY` DISABLE KEYS */;
-/*!40000 ALTER TABLE `USER_PROFILE_NATIONALITY` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USER_PROFILE_PHONE`
---
-
-DROP TABLE IF EXISTS `USER_PROFILE_PHONE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USER_PROFILE_PHONE` (
- `AIRAVATA_INTERNAL_USER_ID` varchar(255) NOT NULL,
- `PHONE` varchar(255) NOT NULL,
- PRIMARY KEY (`AIRAVATA_INTERNAL_USER_ID`,`PHONE`),
- CONSTRAINT `user_profile_phone_ibfk_1` FOREIGN KEY (`AIRAVATA_INTERNAL_USER_ID`) REFERENCES `USER_PROFILE` (`AIRAVATA_INTERNAL_USER_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USER_PROFILE_PHONE`
---
-
-LOCK TABLES `USER_PROFILE_PHONE` WRITE;
-/*!40000 ALTER TABLE `USER_PROFILE_PHONE` DISABLE KEYS */;
-/*!40000 ALTER TABLE `USER_PROFILE_PHONE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Current Database: `replica_catalog`
---
-
-CREATE DATABASE /*!32312 IF NOT EXISTS*/ `replica_catalog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
-
-USE `replica_catalog`;
-
---
--- Table structure for table `CONFIGURATION`
---
-
-DROP TABLE IF EXISTS `CONFIGURATION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `CONFIGURATION` (
- `CONFIG_KEY` varchar(255) NOT NULL,
- `CONFIG_VAL` varchar(255) NOT NULL,
- PRIMARY KEY (`CONFIG_KEY`,`CONFIG_VAL`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `CONFIGURATION`
---
-
-LOCK TABLES `CONFIGURATION` WRITE;
-/*!40000 ALTER TABLE `CONFIGURATION` DISABLE KEYS */;
-INSERT INTO `CONFIGURATION` VALUES ('data_catalog_version','0.16');
-/*!40000 ALTER TABLE `CONFIGURATION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `DATA_PRODUCT`
---
-
-DROP TABLE IF EXISTS `DATA_PRODUCT`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `DATA_PRODUCT` (
- `PRODUCT_URI` varchar(255) NOT NULL,
- `GATEWAY_ID` varchar(255) DEFAULT NULL,
- `PRODUCT_NAME` varchar(255) DEFAULT NULL,
- `PRODUCT_DESCRIPTION` varchar(255) DEFAULT NULL,
- `OWNER_NAME` varchar(255) DEFAULT NULL,
- `PARENT_PRODUCT_URI` varchar(255) DEFAULT NULL,
- `PRODUCT_SIZE` int(11) DEFAULT NULL,
- `CREATION_TIME` timestamp NULL DEFAULT '0000-00-00 00:00:00',
- `LAST_MODIFIED_TIME` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `PRODUCT_TYPE` varchar(10) DEFAULT NULL,
- PRIMARY KEY (`PRODUCT_URI`),
- KEY `PARENT_PRODUCT_URI` (`PARENT_PRODUCT_URI`),
- CONSTRAINT `data_product_ibfk_1` FOREIGN KEY (`PARENT_PRODUCT_URI`) REFERENCES `DATA_PRODUCT` (`PRODUCT_URI`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `DATA_PRODUCT`
---
-
-LOCK TABLES `DATA_PRODUCT` WRITE;
-/*!40000 ALTER TABLE `DATA_PRODUCT` DISABLE KEYS */;
-/*!40000 ALTER TABLE `DATA_PRODUCT` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `DATA_PRODUCT_METADATA`
---
-
-DROP TABLE IF EXISTS `DATA_PRODUCT_METADATA`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `DATA_PRODUCT_METADATA` (
- `PRODUCT_URI` varchar(255) NOT NULL,
- `METADATA_KEY` varchar(255) NOT NULL,
- `METADATA_VALUE` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`PRODUCT_URI`,`METADATA_KEY`),
- CONSTRAINT `data_product_metadata_ibfk_1` FOREIGN KEY (`PRODUCT_URI`) REFERENCES `DATA_PRODUCT` (`PRODUCT_URI`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `DATA_PRODUCT_METADATA`
---
-
-LOCK TABLES `DATA_PRODUCT_METADATA` WRITE;
-/*!40000 ALTER TABLE `DATA_PRODUCT_METADATA` DISABLE KEYS */;
-/*!40000 ALTER TABLE `DATA_PRODUCT_METADATA` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `DATA_REPLICA_LOCATION`
---
-
-DROP TABLE IF EXISTS `DATA_REPLICA_LOCATION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `DATA_REPLICA_LOCATION` (
- `REPLICA_ID` varchar(255) NOT NULL,
- `PRODUCT_URI` varchar(255) NOT NULL,
- `REPLICA_NAME` varchar(255) DEFAULT NULL,
- `REPLICA_DESCRIPTION` varchar(255) DEFAULT NULL,
- `STORAGE_RESOURCE_ID` varchar(255) DEFAULT NULL,
- `FILE_PATH` varchar(255) DEFAULT NULL,
- `CREATION_TIME` timestamp NULL DEFAULT '0000-00-00 00:00:00',
- `LAST_MODIFIED_TIME` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `VALID_UNTIL_TIME` timestamp NULL DEFAULT '0000-00-00 00:00:00',
- `REPLICA_LOCATION_CATEGORY` varchar(26) DEFAULT NULL,
- `REPLICA_PERSISTENT_TYPE` varchar(10) DEFAULT NULL,
- PRIMARY KEY (`REPLICA_ID`),
- KEY `PRODUCT_URI` (`PRODUCT_URI`),
- CONSTRAINT `data_replica_location_ibfk_1` FOREIGN KEY (`PRODUCT_URI`) REFERENCES `DATA_PRODUCT` (`PRODUCT_URI`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `DATA_REPLICA_LOCATION`
---
-
-LOCK TABLES `DATA_REPLICA_LOCATION` WRITE;
-/*!40000 ALTER TABLE `DATA_REPLICA_LOCATION` DISABLE KEYS */;
-/*!40000 ALTER TABLE `DATA_REPLICA_LOCATION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `DATA_REPLICA_METADATA`
---
-
-DROP TABLE IF EXISTS `DATA_REPLICA_METADATA`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `DATA_REPLICA_METADATA` (
- `REPLICA_ID` varchar(255) NOT NULL,
- `METADATA_KEY` varchar(255) NOT NULL,
- `METADATA_VALUE` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`REPLICA_ID`,`METADATA_KEY`),
- CONSTRAINT `data_replica_metadata_ibfk_1` FOREIGN KEY (`REPLICA_ID`) REFERENCES `DATA_REPLICA_LOCATION` (`REPLICA_ID`) ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `DATA_REPLICA_METADATA`
---
-
-LOCK TABLES `DATA_REPLICA_METADATA` WRITE;
-/*!40000 ALTER TABLE `DATA_REPLICA_METADATA` DISABLE KEYS */;
-/*!40000 ALTER TABLE `DATA_REPLICA_METADATA` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Current Database: `sharing_catalog`
---
-
-CREATE DATABASE /*!32312 IF NOT EXISTS*/ `sharing_catalog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
-
-USE `sharing_catalog`;
-
---
--- Table structure for table `CONFIGURATION`
---
-
-DROP TABLE IF EXISTS `CONFIGURATION`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `CONFIGURATION` (
- `CONFIG_KEY` varchar(255) NOT NULL,
- `CONFIG_VALUE` varchar(255) NOT NULL,
- PRIMARY KEY (`CONFIG_KEY`,`CONFIG_VALUE`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `CONFIGURATION`
---
-
-LOCK TABLES `CONFIGURATION` WRITE;
-/*!40000 ALTER TABLE `CONFIGURATION` DISABLE KEYS */;
-INSERT INTO `CONFIGURATION` VALUES ('sharing_reg_version','0.17');
-/*!40000 ALTER TABLE `CONFIGURATION` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `DOMAIN`
---
-
-DROP TABLE IF EXISTS `DOMAIN`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `DOMAIN` (
- `DOMAIN_ID` varchar(255) NOT NULL,
- `NAME` varchar(255) NOT NULL,
- `DESCRIPTION` varchar(255) DEFAULT NULL,
- `CREATED_TIME` bigint(20) NOT NULL,
- `UPDATED_TIME` bigint(20) NOT NULL,
- PRIMARY KEY (`DOMAIN_ID`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `DOMAIN`
---
-
-LOCK TABLES `DOMAIN` WRITE;
-/*!40000 ALTER TABLE `DOMAIN` DISABLE KEYS */;
-INSERT INTO `DOMAIN` VALUES ('default','default','Domain entry for default',1551138011532,1551138011532);
-/*!40000 ALTER TABLE `DOMAIN` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `ENTITY`
---
-
-DROP TABLE IF EXISTS `ENTITY`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `ENTITY` (
- `ENTITY_ID` varchar(255) NOT NULL,
- `DOMAIN_ID` varchar(255) NOT NULL,
- `ENTITY_TYPE_ID` varchar(255) NOT NULL,
- `OWNER_ID` varchar(255) NOT NULL,
- `PARENT_ENTITY_ID` varchar(255) DEFAULT NULL,
- `NAME` varchar(255) NOT NULL,
- `DESCRIPTION` varchar(255) DEFAULT NULL,
- `BINARY_DATA` blob,
- `FULL_TEXT` text,
- `SHARED_COUNT` bigint(20) DEFAULT '0',
- `ORIGINAL_ENTITY_CREATION_TIME` bigint(20) NOT NULL,
- `CREATED_TIME` bigint(20) NOT NULL,
- `UPDATED_TIME` bigint(20) NOT NULL,
- PRIMARY KEY (`ENTITY_ID`,`DOMAIN_ID`),
- KEY `ENTITY_TYPE_ID` (`ENTITY_TYPE_ID`,`DOMAIN_ID`),
- KEY `OWNER_ID` (`OWNER_ID`,`DOMAIN_ID`),
- KEY `PARENT_ENTITY_ID` (`PARENT_ENTITY_ID`,`DOMAIN_ID`),
- FULLTEXT KEY `FULL_TEXT_INDEX` (`FULL_TEXT`),
- CONSTRAINT `entity_ibfk_1` FOREIGN KEY (`ENTITY_TYPE_ID`, `DOMAIN_ID`) REFERENCES `ENTITY_TYPE` (`ENTITY_TYPE_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION,
- CONSTRAINT `entity_ibfk_2` FOREIGN KEY (`OWNER_ID`, `DOMAIN_ID`) REFERENCES `SHARING_USER` (`USER_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION,
- CONSTRAINT `entity_ibfk_3` FOREIGN KEY (`PARENT_ENTITY_ID`, `DOMAIN_ID`) REFERENCES `ENTITY` (`ENTITY_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `ENTITY`
---
-
-LOCK TABLES `ENTITY` WRITE;
-/*!40000 ALTER TABLE `ENTITY` DISABLE KEYS */;
-INSERT INTO `ENTITY` VALUES ('46a99a5a-8b55-4982-bfd7-90fe72b00d46','default','default:CREDENTIAL_TOKEN','default-admin@default',NULL,'46a99a5a-8b55-4982-bfd7-90fe72b00d46','Default',NULL,NULL,0,1551139607733,1551139607733,1551139607733),('af03c63d-a40e-4ed1-aee0-759a6ed0202c','default','default:GROUP_RESOURCE_PROFILE','default-admin@default',NULL,'Default-Group-Resource-Profile',NULL,NULL,NULL,3,1552318871908,1552318871908,1552318871908),('Clone_of_Clone_of_Echo_on_Mar_11,_2019_12:24_PM_9182a6b7-520d-4a8d-92e1-24463aae530b','default','default:EXPERIMENT','default-admin@default',NULL,'Clone of Clone of Echo on Mar 11, 2019 12:24 PM',NULL,NULL,NULL,3,1552323107891,1552323107891,1552323107891),('Clone_of_Echo_on_Mar_11,_2019_12:02_PM_32bd2154-9dd1-46c4-87c1-cd3925174f79','default','default:EXPERIMENT','default-admin@default',NULL,'Clone of Echo on Mar 11, 2019 12:02 PM',NULL,NULL,NULL,3,1552320576004,1552320576004,1552320576004),('Clone_of_Echo_on_Mar_11,_2019_12:24_PM_96170feb-9202-458f-8494-5fea6e5598dd','default','default:EXPERIMENT','default-admin@default',NULL,'Clone of Echo on Mar 11, 2019 12:24 PM',NULL,NULL,NULL,3,1552322784306,1552322784306,1552322784306),('Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','default','default:PROJECT','default-admin@default',NULL,'Default Project','This is the default project for user default-admin',NULL,NULL,2,1551138405526,1551138405526,1551138405526),('echo2_190926b0-f389-4394-864e-0b2f0782692c','default','default:EXPERIMENT','default-admin@default','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','echo2',' ',NULL,NULL,5,1551200818680,1551200818680,1551200818680),('Echo2_56e5b34d-5ce1-47db-9723-29244d41da32','default','default:EXPERIMENT','default-admin@default','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','Echo2',' ',NULL,NULL,5,1551195758420,1551195758420,1551195758420),('Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9','default','default:EXPERIMENT','default-admin@default','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','Echo on Mar 11, 2019 11:47 AM',NULL,NULL,NULL,3,1552319249121,1552319249121,1552319249121),('Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f','default','default:EXPERIMENT','default-admin@default','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','Echo on Mar 11, 2019 11:59 AM',NULL,NULL,NULL,3,1552319973634,1552319973634,1552319973634),('Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e','default','default:EXPERIMENT','default-admin@default','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','Echo on Mar 11, 2019 12:02 PM',NULL,NULL,NULL,3,1552320158357,1552320158357,1552320158357),('Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1','default','default:EXPERIMENT','default-admin@default','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','Echo on Mar 11, 2019 12:11 PM',NULL,NULL,NULL,3,1552320714168,1552320714168,1552320714168),('Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387','default','default:EXPERIMENT','default-admin@default','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','Echo on Mar 11, 2019 12:24 PM',NULL,NULL,NULL,3,1552321490571,1552321490571,1552321490571),('js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84','default','default:APPLICATION_DEPLOYMENT','default-admin@default',NULL,'js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84','',NULL,NULL,3,1551195720578,1551195720578,1551195720578);
-/*!40000 ALTER TABLE `ENTITY` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `ENTITY_TYPE`
---
-
-DROP TABLE IF EXISTS `ENTITY_TYPE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `ENTITY_TYPE` (
- `ENTITY_TYPE_ID` varchar(255) NOT NULL,
- `DOMAIN_ID` varchar(255) NOT NULL,
- `NAME` varchar(255) NOT NULL,
- `DESCRIPTION` varchar(255) DEFAULT NULL,
- `CREATED_TIME` bigint(20) NOT NULL,
- `UPDATED_TIME` bigint(20) NOT NULL,
- PRIMARY KEY (`ENTITY_TYPE_ID`,`DOMAIN_ID`),
- KEY `DOMAIN_ID` (`DOMAIN_ID`),
- CONSTRAINT `entity_type_ibfk_1` FOREIGN KEY (`DOMAIN_ID`) REFERENCES `DOMAIN` (`DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `ENTITY_TYPE`
---
-
-LOCK TABLES `ENTITY_TYPE` WRITE;
-/*!40000 ALTER TABLE `ENTITY_TYPE` DISABLE KEYS */;
-INSERT INTO `ENTITY_TYPE` VALUES ('default:APPLICATION_DEPLOYMENT','default','APPLICATION-DEPLOYMENT','Application Deployment entity type',1551138011706,1551138011706),('default:CREDENTIAL_TOKEN','default','CREDENTIAL_TOKEN','Credential Store Token entity type',1551138011735,1551138011735),('default:EXPERIMENT','default','EXPERIMENT','Experiment entity type',1551138011685,1551138011685),('default:FILE','default','FILE','File entity type',1551138011696,1551138011696),('default:GROUP_RESOURCE_PROFILE','default','GROUP_RESOURCE_PROFILE','Group Resource Profile entity type',1551138011716,1551138011716),('default:PROJECT','default','PROJECT','Project entity type',1551138011665,1551138011665);
-/*!40000 ALTER TABLE `ENTITY_TYPE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GROUP_ADMIN`
---
-
-DROP TABLE IF EXISTS `GROUP_ADMIN`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GROUP_ADMIN` (
- `ADMIN_ID` varchar(255) NOT NULL,
- `GROUP_ID` varchar(255) NOT NULL,
- `DOMAIN_ID` varchar(255) NOT NULL,
- PRIMARY KEY (`ADMIN_ID`,`GROUP_ID`,`DOMAIN_ID`),
- KEY `ADMIN_ID` (`ADMIN_ID`,`DOMAIN_ID`),
- CONSTRAINT `group_admin_ibfk_1` FOREIGN KEY (`ADMIN_ID`, `DOMAIN_ID`) REFERENCES `SHARING_USER` (`USER_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GROUP_ADMIN`
---
-
-LOCK TABLES `GROUP_ADMIN` WRITE;
-/*!40000 ALTER TABLE `GROUP_ADMIN` DISABLE KEYS */;
-/*!40000 ALTER TABLE `GROUP_ADMIN` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `GROUP_MEMBERSHIP`
---
-
-DROP TABLE IF EXISTS `GROUP_MEMBERSHIP`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `GROUP_MEMBERSHIP` (
- `PARENT_ID` varchar(255) NOT NULL,
- `CHILD_ID` varchar(255) NOT NULL,
- `DOMAIN_ID` varchar(255) NOT NULL,
- `CHILD_TYPE` varchar(255) NOT NULL,
- `CREATED_TIME` bigint(20) NOT NULL,
- `UPDATED_TIME` bigint(20) NOT NULL,
- PRIMARY KEY (`PARENT_ID`,`CHILD_ID`,`DOMAIN_ID`),
- KEY `PARENT_ID` (`PARENT_ID`,`DOMAIN_ID`),
- KEY `CHILD_ID` (`CHILD_ID`,`DOMAIN_ID`),
- CONSTRAINT `group_membership_ibfk_1` FOREIGN KEY (`PARENT_ID`, `DOMAIN_ID`) REFERENCES `USER_GROUP` (`GROUP_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION,
- CONSTRAINT `group_membership_ibfk_2` FOREIGN KEY (`CHILD_ID`, `DOMAIN_ID`) REFERENCES `USER_GROUP` (`GROUP_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `GROUP_MEMBERSHIP`
---
-
-LOCK TABLES `GROUP_MEMBERSHIP` WRITE;
-/*!40000 ALTER TABLE `GROUP_MEMBERSHIP` DISABLE KEYS */;
-INSERT INTO `GROUP_MEMBERSHIP` VALUES ('Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','default-admin@default','default','USER',1551138405079,1551138405079),('Gateway_Users_2e4e6eb4-05ad-4a0f-92d1-02aa26c56004','default-admin@default','default','USER',1551138405029,1551138405029),('Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','default-admin@default','default','USER',1551138405107,1551138405107);
-/*!40000 ALTER TABLE `GROUP_MEMBERSHIP` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `PERMISSION_TYPE`
---
-
-DROP TABLE IF EXISTS `PERMISSION_TYPE`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `PERMISSION_TYPE` (
- `PERMISSION_TYPE_ID` varchar(255) NOT NULL,
- `DOMAIN_ID` varchar(255) NOT NULL,
- `NAME` varchar(255) NOT NULL,
- `DESCRIPTION` varchar(255) DEFAULT NULL,
- `CREATED_TIME` bigint(20) NOT NULL,
- `UPDATED_TIME` bigint(20) NOT NULL,
- PRIMARY KEY (`PERMISSION_TYPE_ID`,`DOMAIN_ID`),
- KEY `DOMAIN_ID` (`DOMAIN_ID`),
- CONSTRAINT `permission_type_ibfk_1` FOREIGN KEY (`DOMAIN_ID`) REFERENCES `DOMAIN` (`DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `PERMISSION_TYPE`
---
-
-LOCK TABLES `PERMISSION_TYPE` WRITE;
-/*!40000 ALTER TABLE `PERMISSION_TYPE` DISABLE KEYS */;
-INSERT INTO `PERMISSION_TYPE` VALUES ('default:OWNER','default','OWNER','GLOBAL permission to default',1551138011561,1551138011561),('default:READ','default','READ','Read permission type',1551138011783,1551138011783),('default:WRITE','default','WRITE','Write permission type',1551138011808,1551138011808),('default:MANAGE_SHARING','default','MANAGE_SHARING','Sharing permission type',1551138011853,1551138011853);
-/*!40000 ALTER TABLE `PERMISSION_TYPE` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `SHARING`
---
-
-DROP TABLE IF EXISTS `SHARING`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `SHARING` (
- `PERMISSION_TYPE_ID` varchar(255) NOT NULL,
- `ENTITY_ID` varchar(255) NOT NULL,
- `DOMAIN_ID` varchar(255) NOT NULL,
- `GROUP_ID` varchar(255) NOT NULL,
- `SHARING_TYPE` varchar(255) NOT NULL,
- `INHERITED_PARENT_ID` varchar(255) NOT NULL,
- `CREATED_TIME` bigint(20) NOT NULL,
- `UPDATED_TIME` bigint(20) NOT NULL,
- PRIMARY KEY (`PERMISSION_TYPE_ID`,`ENTITY_ID`,`GROUP_ID`,`DOMAIN_ID`,`INHERITED_PARENT_ID`),
- KEY `SHARING_PERMISSION_TYPE_ID_DOMAIN_ID_FK` (`PERMISSION_TYPE_ID`,`DOMAIN_ID`),
- KEY `SHARING_ENTITY_ID_DOMAIN_ID_FK` (`ENTITY_ID`,`DOMAIN_ID`),
- KEY `SHARING_INHERITED_PARENT_ID_DOMAIN_ID_FK` (`INHERITED_PARENT_ID`,`DOMAIN_ID`),
- KEY `SHARING_GROUP_ID_DOMAIN_ID_FK` (`GROUP_ID`,`DOMAIN_ID`),
- CONSTRAINT `SHARING_ENTITY_ID_DOMAIN_ID_FK` FOREIGN KEY (`ENTITY_ID`, `DOMAIN_ID`) REFERENCES `ENTITY` (`ENTITY_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION,
- CONSTRAINT `SHARING_GROUP_ID_DOMAIN_ID_FK` FOREIGN KEY (`GROUP_ID`, `DOMAIN_ID`) REFERENCES `USER_GROUP` (`GROUP_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION,
- CONSTRAINT `SHARING_INHERITED_PARENT_ID_DOMAIN_ID_FK` FOREIGN KEY (`INHERITED_PARENT_ID`, `DOMAIN_ID`) REFERENCES `ENTITY` (`ENTITY_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION,
- CONSTRAINT `SHARING_PERMISSION_TYPE_ID_DOMAIN_ID_FK` FOREIGN KEY (`PERMISSION_TYPE_ID`, `DOMAIN_ID`) REFERENCES `PERMISSION_TYPE` (`PERMISSION_TYPE_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `SHARING`
---
-
-LOCK TABLES `SHARING` WRITE;
-/*!40000 ALTER TABLE `SHARING` DISABLE KEYS */;
-INSERT INTO `SHARING` VALUES ('default:OWNER','46a99a5a-8b55-4982-bfd7-90fe72b00d46','default','default-admin@default','DIRECT_CASCADING','46a99a5a-8b55-4982-bfd7-90fe72b00d46',1551139607745,1551139607745),('default:OWNER','af03c63d-a40e-4ed1-aee0-759a6ed0202c','default','default-admin@default','DIRECT_CASCADING','af03c63d-a40e-4ed1-aee0-759a6ed0202c',1552318871935,1552318871935),('default:OWNER','Clone_of_Clone_of_Echo_on_Mar_11,_2019_12:24_PM_9182a6b7-520d-4a8d-92e1-24463aae530b','default','default-admin@default','DIRECT_CASCADING','Clone_of_Clone_of_Echo_on_Mar_11,_2019_12:24_PM_9182a6b7-520d-4a8d-92e1-24463aae530b',1552323107903,1552323107903),('default:OWNER','Clone_of_Echo_on_Mar_11,_2019_12:02_PM_32bd2154-9dd1-46c4-87c1-cd3925174f79','default','default-admin@default','DIRECT_CASCADING','Clone_of_Echo_on_Mar_11,_2019_12:02_PM_32bd2154-9dd1-46c4-87c1-cd3925174f79',1552320576016,1552320576016),('default:OWNER','Clone_of_Echo_on_Mar_11,_2019_12:24_PM_96170feb-9202-458f-8494-5fea6e5598dd','default','default-admin@default','DIRECT_CASCADING','Clone_of_Echo_on_Mar_11,_2019_12:24_PM_96170feb-9202-458f-8494-5fea6e5598dd',1552322784315,1552322784315),('default:OWNER','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','default','default-admin@default','DIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1551138405554,1551138405554),('default:OWNER','echo2_190926b0-f389-4394-864e-0b2f0782692c','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1551200818759,1551200818759),('default:OWNER','echo2_190926b0-f389-4394-864e-0b2f0782692c','default','default-admin@default','DIRECT_CASCADING','echo2_190926b0-f389-4394-864e-0b2f0782692c',1551200818705,1551200818705),('default:OWNER','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1551195758468,1551195758468),('default:OWNER','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32','default','default-admin@default','DIRECT_CASCADING','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32',1551195758430,1551195758430),('default:OWNER','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552319249178,1552319249178),('default:OWNER','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9','default','default-admin@default','DIRECT_CASCADING','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9',1552319249136,1552319249136),('default:OWNER','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552319973685,1552319973685),('default:OWNER','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f','default','default-admin@default','DIRECT_CASCADING','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f',1552319973651,1552319973651),('default:OWNER','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552320158415,1552320158415),('default:OWNER','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e','default','default-admin@default','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e',1552320158375,1552320158375),('default:OWNER','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552320714215,1552320714215),('default:OWNER','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1','default','default-admin@default','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1',1552320714184,1552320714184),('default:OWNER','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552321490609,1552321490609),('default:OWNER','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387','default','default-admin@default','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387',1552321490586,1552321490586),('default:OWNER','js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84','default','default-admin@default','DIRECT_CASCADING','js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84',1551195720607,1551195720607),('default:READ','af03c63d-a40e-4ed1-aee0-759a6ed0202c','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','af03c63d-a40e-4ed1-aee0-759a6ed0202c',1552318872021,1552318872021),('default:READ','af03c63d-a40e-4ed1-aee0-759a6ed0202c','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','af03c63d-a40e-4ed1-aee0-759a6ed0202c',1552318872021,1552318872021),('default:READ','Clone_of_Clone_of_Echo_on_Mar_11,_2019_12:24_PM_9182a6b7-520d-4a8d-92e1-24463aae530b','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Clone_of_Clone_of_Echo_on_Mar_11,_2019_12:24_PM_9182a6b7-520d-4a8d-92e1-24463aae530b',1552323107949,1552323107949),('default:READ','Clone_of_Clone_of_Echo_on_Mar_11,_2019_12:24_PM_9182a6b7-520d-4a8d-92e1-24463aae530b','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','Clone_of_Clone_of_Echo_on_Mar_11,_2019_12:24_PM_9182a6b7-520d-4a8d-92e1-24463aae530b',1552323107949,1552323107949),('default:READ','Clone_of_Echo_on_Mar_11,_2019_12:02_PM_32bd2154-9dd1-46c4-87c1-cd3925174f79','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Clone_of_Echo_on_Mar_11,_2019_12:02_PM_32bd2154-9dd1-46c4-87c1-cd3925174f79',1552320576060,1552320576060),('default:READ','Clone_of_Echo_on_Mar_11,_2019_12:02_PM_32bd2154-9dd1-46c4-87c1-cd3925174f79','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','Clone_of_Echo_on_Mar_11,_2019_12:02_PM_32bd2154-9dd1-46c4-87c1-cd3925174f79',1552320576060,1552320576060),('default:READ','Clone_of_Echo_on_Mar_11,_2019_12:24_PM_96170feb-9202-458f-8494-5fea6e5598dd','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Clone_of_Echo_on_Mar_11,_2019_12:24_PM_96170feb-9202-458f-8494-5fea6e5598dd',1552322784367,1552322784367),('default:READ','Clone_of_Echo_on_Mar_11,_2019_12:24_PM_96170feb-9202-458f-8494-5fea6e5598dd','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','Clone_of_Echo_on_Mar_11,_2019_12:24_PM_96170feb-9202-458f-8494-5fea6e5598dd',1552322784367,1552322784367),('default:READ','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','default','default-admin@default','DIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1551138406052,1551138406052),('default:READ','echo2_190926b0-f389-4394-864e-0b2f0782692c','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','echo2_190926b0-f389-4394-864e-0b2f0782692c',1551200818860,1551200818860),('default:READ','echo2_190926b0-f389-4394-864e-0b2f0782692c','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1551200818736,1551200818736),('default:READ','echo2_190926b0-f389-4394-864e-0b2f0782692c','default','default-admin@default','DIRECT_CASCADING','echo2_190926b0-f389-4394-864e-0b2f0782692c',1551200819351,1551200819351),('default:READ','echo2_190926b0-f389-4394-864e-0b2f0782692c','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','echo2_190926b0-f389-4394-864e-0b2f0782692c',1551200818860,1551200818860),('default:READ','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32',1551195758537,1551195758537),('default:READ','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1551195758452,1551195758452),('default:READ','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32','default','default-admin@default','DIRECT_CASCADING','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32',1551195759028,1551195759028),('default:READ','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32',1551195758537,1551195758537),('default:READ','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9',1552319249231,1552319249231),('default:READ','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552319249161,1552319249161),('default:READ','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9',1552319249231,1552319249231),('default:READ','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f',1552319973762,1552319973762),('default:READ','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552319973669,1552319973669),('default:READ','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f',1552319973762,1552319973762),('default:READ','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e',1552320158500,1552320158500),('default:READ','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552320158395,1552320158395),('default:READ','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e',1552320158500,1552320158500),('default:READ','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1',1552320714263,1552320714263),('default:READ','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552320714198,1552320714198),('default:READ','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1',1552320714263,1552320714263),('default:READ','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387',1552321490657,1552321490657),('default:READ','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552321490595,1552321490595),('default:READ','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387',1552321490657,1552321490657),('default:READ','js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84',1551195720704,1551195720704),('default:READ','js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84','default','Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','DIRECT_CASCADING','js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84',1551195720704,1551195720704),('default:WRITE','af03c63d-a40e-4ed1-aee0-759a6ed0202c','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','af03c63d-a40e-4ed1-aee0-759a6ed0202c',1552318871972,1552318871972),('default:WRITE','Clone_of_Clone_of_Echo_on_Mar_11,_2019_12:24_PM_9182a6b7-520d-4a8d-92e1-24463aae530b','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Clone_of_Clone_of_Echo_on_Mar_11,_2019_12:24_PM_9182a6b7-520d-4a8d-92e1-24463aae530b',1552323107921,1552323107921),('default:WRITE','Clone_of_Echo_on_Mar_11,_2019_12:02_PM_32bd2154-9dd1-46c4-87c1-cd3925174f79','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Clone_of_Echo_on_Mar_11,_2019_12:02_PM_32bd2154-9dd1-46c4-87c1-cd3925174f79',1552320576035,1552320576035),('default:WRITE','Clone_of_Echo_on_Mar_11,_2019_12:24_PM_96170feb-9202-458f-8494-5fea6e5598dd','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Clone_of_Echo_on_Mar_11,_2019_12:24_PM_96170feb-9202-458f-8494-5fea6e5598dd',1552322784331,1552322784331),('default:WRITE','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc','default','default-admin@default','DIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1551138405837,1551138405837),('default:WRITE','echo2_190926b0-f389-4394-864e-0b2f0782692c','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','echo2_190926b0-f389-4394-864e-0b2f0782692c',1551200818802,1551200818802),('default:WRITE','echo2_190926b0-f389-4394-864e-0b2f0782692c','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1551200818748,1551200818748),('default:WRITE','echo2_190926b0-f389-4394-864e-0b2f0782692c','default','default-admin@default','DIRECT_CASCADING','echo2_190926b0-f389-4394-864e-0b2f0782692c',1551200819185,1551200819185),('default:WRITE','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32',1551195758494,1551195758494),('default:WRITE','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1551195758461,1551195758461),('default:WRITE','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32','default','default-admin@default','DIRECT_CASCADING','Echo2_56e5b34d-5ce1-47db-9723-29244d41da32',1551195758871,1551195758871),('default:WRITE','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9',1552319249200,1552319249200),('default:WRITE','Echo_on_Mar_11,_2019_11:47_AM_40ff0d5e-5053-43f7-8f80-faa53f623ca9','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552319249169,1552319249169),('default:WRITE','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f',1552319973716,1552319973716),('default:WRITE','Echo_on_Mar_11,_2019_11:59_AM_d17b5f17-f9d3-442a-82d2-b386c3a9eb6f','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552319973676,1552319973676),('default:WRITE','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e',1552320158451,1552320158451),('default:WRITE','Echo_on_Mar_11,_2019_12:02_PM_d7d67775-e7f6-4255-9c05-764b29ac9e3e','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552320158405,1552320158405),('default:WRITE','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1',1552320714233,1552320714233),('default:WRITE','Echo_on_Mar_11,_2019_12:11_PM_f2576ab2-c3af-47e9-8fe2-099802ba25c1','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552320714207,1552320714207),('default:WRITE','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387',1552321490625,1552321490625),('default:WRITE','Echo_on_Mar_11,_2019_12:24_PM_e78780cb-35a5-467b-baea-e9df5f498387','default','default-admin@default','INDIRECT_CASCADING','Default_Project_93d9a30a-4299-44dd-9e48-a7b8961464dc',1552321490602,1552321490602),('default:WRITE','js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84','default','Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','DIRECT_CASCADING','js-156-93.jetstream-cloud.org_Echo_88202f35-beef-414a-84a4-6ee9dd4f5a84',1551195720648,1551195720648);
-/*!40000 ALTER TABLE `SHARING` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `SHARING_USER`
---
-
-DROP TABLE IF EXISTS `SHARING_USER`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `SHARING_USER` (
- `USER_ID` varchar(255) NOT NULL,
- `DOMAIN_ID` varchar(255) NOT NULL,
- `USER_NAME` varchar(255) NOT NULL,
- `FIRST_NAME` varchar(255) DEFAULT NULL,
- `LAST_NAME` varchar(255) DEFAULT NULL,
- `EMAIL` varchar(255) DEFAULT NULL,
- `ICON` blob,
- `CREATED_TIME` bigint(20) NOT NULL,
- `UPDATED_TIME` bigint(20) NOT NULL,
- PRIMARY KEY (`USER_ID`,`DOMAIN_ID`),
- KEY `DOMAIN_ID` (`DOMAIN_ID`),
- CONSTRAINT `sharing_user_ibfk_1` FOREIGN KEY (`DOMAIN_ID`) REFERENCES `DOMAIN` (`DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `SHARING_USER`
---
-
-LOCK TABLES `SHARING_USER` WRITE;
-/*!40000 ALTER TABLE `SHARING_USER` DISABLE KEYS */;
-INSERT INTO `SHARING_USER` VALUES ('default-admin@default','default','dim Upe',NULL,NULL,'dimuthu.upeksha2@gmail.com',NULL,1551138011603,1551138404868);
-/*!40000 ALTER TABLE `SHARING_USER` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Table structure for table `USER_GROUP`
---
-
-DROP TABLE IF EXISTS `USER_GROUP`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `USER_GROUP` (
- `GROUP_ID` varchar(255) NOT NULL,
- `DOMAIN_ID` varchar(255) NOT NULL,
- `NAME` varchar(255) NOT NULL,
- `DESCRIPTION` varchar(255) DEFAULT NULL,
- `OWNER_ID` varchar(255) NOT NULL,
- `GROUP_TYPE` varchar(255) NOT NULL,
- `GROUP_CARDINALITY` varchar(255) NOT NULL,
- `CREATED_TIME` bigint(20) NOT NULL,
- `UPDATED_TIME` bigint(20) NOT NULL,
- PRIMARY KEY (`GROUP_ID`,`DOMAIN_ID`),
- KEY `OWNER_ID` (`OWNER_ID`,`DOMAIN_ID`),
- CONSTRAINT `user_group_ibfk_1` FOREIGN KEY (`OWNER_ID`, `DOMAIN_ID`) REFERENCES `SHARING_USER` (`USER_ID`, `DOMAIN_ID`) ON DELETE CASCADE ON UPDATE NO ACTION
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Dumping data for table `USER_GROUP`
---
-
-LOCK TABLES `USER_GROUP` WRITE;
-/*!40000 ALTER TABLE `USER_GROUP` DISABLE KEYS */;
-INSERT INTO `USER_GROUP` VALUES ('Admin_Users_ba3e4af7-9b1d-4f46-b43a-2b44790f6868','default','Admin Users','Admin users group.','default-admin@default','DOMAIN_LEVEL_GROUP','MULTI_USER',1551138405064,1551138405064),('default-admin@default','default','dim Upe','user dim Upe group','default-admin@default','USER_LEVEL_GROUP','SINGLE_USER',0,1551138404890),('Gateway_Users_2e4e6eb4-05ad-4a0f-92d1-02aa26c56004','default','Gateway Users','Default group for users of the gateway.','default-admin@default','DOMAIN_LEVEL_GROUP','MULTI_USER',1551138405011,1551138405011),('Read_Only_Admin_Users_68f97cb6-9d9a-4a41-a15e-1db24ec087b3','default','Read Only Admin Users','Group of admin users with read-only access.','default-admin@default','DOMAIN_LEVEL_GROUP','MULTI_USER',1551138405099,1551138405099);
-/*!40000 ALTER TABLE `USER_GROUP` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Current Database: `workflow_catalog`
---
-
-CREATE DATABASE /*!32312 IF NOT EXISTS*/ `workflow_catalog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
-
-USE `workflow_catalog`;
-
---
--- Current Database: `research_catalog`
---
-
-CREATE DATABASE /*!32312 IF NOT EXISTS*/ `research_catalog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
-
-USE `research_catalog`;
-
---
--- Current Database: `app_catalog`
---
-
-USE `app_catalog`;
-
---
--- Current Database: `credential_store`
---
-
-USE `credential_store`;
-
---
--- Current Database: `experiment_catalog`
---
-
-USE `experiment_catalog`;
-
---
--- Final view structure for view `experiment_summary`
---
-
-/*!50001 DROP VIEW IF EXISTS `experiment_summary`*/;
-/*!50001 SET @saved_cs_client = @@character_set_client */;
-/*!50001 SET @saved_cs_results = @@character_set_results */;
-/*!50001 SET @saved_col_connection = @@collation_connection */;
-/*!50001 SET character_set_client = utf8mb4 */;
-/*!50001 SET character_set_results = utf8mb4 */;
-/*!50001 SET collation_connection = utf8mb4_unicode_ci */;
-/*!50001 CREATE ALGORITHM=UNDEFINED */
-/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */
-/*!50001 VIEW `experiment_summary` AS select `E`.`EXPERIMENT_ID` AS `EXPERIMENT_ID`,`E`.`PROJECT_ID` AS `PROJECT_ID`,`E`.`GATEWAY_ID` AS `GATEWAY_ID`,`E`.`USER_NAME` AS `USER_NAME`,`E`.`EXECUTION_ID` AS `EXECUTION_ID`,`E`.`EXPERIMENT_NAME` AS `EXPERIMENT_NAME`,`E`.`CREATION_TIME` AS `CREATION_TIME`,`E`.`DESCRIPTION` AS `DESCRIPTION`,`es`.`STATE` AS `STATE`,`UD`.`RESOURCE_HOST_ID` AS `RESOURCE_HOST_ID`,`es`.`TIME_OF_STATE_CHANGE` AS `TIME_OF_STATE_CHANGE` from ((`EXPERIMENT` `E` left join `latest_experiment_status` `es` on((`E`.`EXPERIMENT_ID` = `es`.`EXPERIMENT_ID`))) left join `USER_CONFIGURATION_DATA` `UD` on((`E`.`EXPERIMENT_ID` = `UD`.`EXPERIMENT_ID`))) where 1 */;
-/*!50001 SET character_set_client = @saved_cs_client */;
-/*!50001 SET character_set_results = @saved_cs_results */;
-/*!50001 SET collation_connection = @saved_col_connection */;
-
---
--- Final view structure for view `latest_experiment_status`
---
-
-/*!50001 DROP VIEW IF EXISTS `latest_experiment_status`*/;
-/*!50001 SET @saved_cs_client = @@character_set_client */;
-/*!50001 SET @saved_cs_results = @@character_set_results */;
-/*!50001 SET @saved_col_connection = @@collation_connection */;
-/*!50001 SET character_set_client = utf8mb4 */;
-/*!50001 SET character_set_results = utf8mb4 */;
-/*!50001 SET collation_connection = utf8mb4_unicode_ci */;
-/*!50001 CREATE ALGORITHM=UNDEFINED */
-/*!50013 DEFINER=`root`@`%` SQL SECURITY DEFINER */
-/*!50001 VIEW `latest_experiment_status` AS select `ES1`.`EXPERIMENT_ID` AS `EXPERIMENT_ID`,`ES1`.`STATE` AS `STATE`,`ES1`.`TIME_OF_STATE_CHANGE` AS `TIME_OF_STATE_CHANGE` from (`EXPERIMENT_STATUS` `ES1` left join `EXPERIMENT_STATUS` `ES2` on(((`ES1`.`EXPERIMENT_ID` = `ES2`.`EXPERIMENT_ID`) and (`ES1`.`TIME_OF_STATE_CHANGE` < `ES2`.`TIME_OF_STATE_CHANGE`)))) where isnull(`ES2`.`TIME_OF_STATE_CHANGE`) */;
-/*!50001 SET character_set_client = @saved_cs_client */;
-/*!50001 SET character_set_results = @saved_cs_results */;
-/*!50001 SET collation_connection = @saved_col_connection */;
-
---
--- Current Database: `profile_service`
---
-
-USE `profile_service`;
-
---
--- Current Database: `replica_catalog`
---
-
-USE `replica_catalog`;
-
---
--- Current Database: `sharing_catalog`
---
-
-USE `sharing_catalog`;
-
---
--- Current Database: `workflow_catalog`
---
-
-USE `workflow_catalog`;
-/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
-
-/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
-/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
-/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
-/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
-/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
-/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
-/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-
--- Dump completed on 2019-03-11 19:13:25
diff --git a/.devcontainer/database_scripts/init/02-sharing-registry-migrations.sql b/.devcontainer/database_scripts/init/02-sharing-registry-migrations.sql
deleted file mode 100644
index 9b5733b8b09..00000000000
--- a/.devcontainer/database_scripts/init/02-sharing-registry-migrations.sql
+++ /dev/null
@@ -1,4 +0,0 @@
-use sharing_catalog;
-
-ALTER TABLE DOMAIN ADD COLUMN IF NOT EXISTS INITIAL_USER_GROUP_ID varchar(255);
-ALTER TABLE DOMAIN ADD CONSTRAINT `DOMAIN_INITIAL_USER_GROUP_ID_FK` FOREIGN KEY IF NOT EXISTS (INITIAL_USER_GROUP_ID, DOMAIN_ID) REFERENCES USER_GROUP(GROUP_ID, DOMAIN_ID) ON DELETE CASCADE ON UPDATE NO ACTION;
diff --git a/.devcontainer/database_scripts/init/03-appcatalog-migrations.sql b/.devcontainer/database_scripts/init/03-appcatalog-migrations.sql
deleted file mode 100644
index e7ef4a55c85..00000000000
--- a/.devcontainer/database_scripts/init/03-appcatalog-migrations.sql
+++ /dev/null
@@ -1,22 +0,0 @@
-use app_catalog;
-
-ALTER TABLE APPLICATION_INPUT ADD COLUMN IF NOT EXISTS OVERRIDE_FILENAME VARCHAR(255);
--- AIRAVATA-3126
-CREATE TABLE IF NOT EXISTS COMPUTE_RESOURCE_RESERVATION -- ComputeResourceReservationEntity
- (RESERVATION_ID VARCHAR(255) NOT NULL, END_TIME TIMESTAMP NOT NULL, RESERVATION_NAME VARCHAR(255) NOT NULL, START_TIME TIMESTAMP NOT NULL, RESOURCE_ID VARCHAR(255) NOT NULL, GROUP_RESOURCE_PROFILE_ID VARCHAR(255) NOT NULL, PRIMARY KEY (RESERVATION_ID)
-)ENGINE=InnoDB DEFAULT CHARSET=latin1;
-CREATE TABLE IF NOT EXISTS COMPUTE_RESOURCE_RESERVATION_QUEUE (RESERVATION_ID VARCHAR(255), QUEUE_NAME VARCHAR(255) NOT NULL
-)ENGINE=InnoDB DEFAULT CHARSET=latin1;
-CREATE INDEX IF NOT EXISTS I_CMPTN_Q_RESERVATION_ID ON COMPUTE_RESOURCE_RESERVATION_QUEUE (RESERVATION_ID);
-ALTER TABLE COMPUTE_RESOURCE_RESERVATION ADD CONSTRAINT FK_COMPUTE_RESOURCE_RESERVATION FOREIGN KEY IF NOT EXISTS (RESOURCE_ID, GROUP_RESOURCE_PROFILE_ID) REFERENCES GROUP_COMPUTE_RESOURCE_PREFERENCE (RESOURCE_ID, GROUP_RESOURCE_PROFILE_ID) ON DELETE CASCADE;
-
--- AIRAVATA-3327: Remove deprecated reservation fields
---alter table GROUP_COMPUTE_RESOURCE_PREFERENCE drop column IF EXISTS RESERVATION;
---alter table GROUP_COMPUTE_RESOURCE_PREFERENCE drop column IF EXISTS RESERVATION_START_TIME;
---alter table GROUP_COMPUTE_RESOURCE_PREFERENCE drop column IF EXISTS RESERVATION_END_TIME;
-
--- AIRAVATA-3369: Convert USER_FRIENDLY_DESC from VARCHAR to TEXT (CLOB)
-alter table APPLICATION_INPUT modify column USER_FRIENDLY_DESC TEXT;
-
--- Add cleanAfterStaged flag to APPLICATION_INTERFACE
-ALTER TABLE APPLICATION_INTERFACE ADD COLUMN IF NOT EXISTS CLEAN_AFTER_STAGED SMALLINT DEFAULT 0;
diff --git a/.devcontainer/database_scripts/init/04-expcatalog-migrations.sql b/.devcontainer/database_scripts/init/04-expcatalog-migrations.sql
deleted file mode 100644
index bb32d3f438d..00000000000
--- a/.devcontainer/database_scripts/init/04-expcatalog-migrations.sql
+++ /dev/null
@@ -1,54 +0,0 @@
-use experiment_catalog;
-
-ALTER TABLE EXPERIMENT_INPUT ADD COLUMN IF NOT EXISTS OVERRIDE_FILENAME VARCHAR(255);
-ALTER TABLE PROCESS_INPUT ADD COLUMN IF NOT EXISTS OVERRIDE_FILENAME VARCHAR(255);
-
-CREATE TABLE IF NOT EXISTS GATEWAY_USAGE_REPORTING_COMMAND (
- GATEWAY_ID VARCHAR(255) NOT NULL,
- COMPUTE_RESOURCE_ID VARCHAR(255) NOT NULL,
- COMMAND LONGTEXT NOT NULL,
- PRIMARY KEY (GATEWAY_ID, COMPUTE_RESOURCE_ID)
-)ENGINE=InnoDB DEFAULT CHARSET=latin1;
-
--- AIRAVATA-3369: Convert USER_FRIENDLY_DESCRIPTION from VARCHAR to TEXT (CLOB)
-alter table EXPERIMENT_INPUT modify column USER_FRIENDLY_DESCRIPTION TEXT;
-alter table PROCESS_INPUT modify column USER_FRIENDLY_DESCRIPTION TEXT;
-
-CREATE TABLE IF NOT EXISTS COMPUTE_RESOURCE_SCHEDULING (
- EXPERIMENT_ID varchar(255) NOT NULL,
- RESOURCE_HOST_ID varchar(255) NOT NULL,
- TOTAL_CPU_COUNT INT,
- NODE_COUNT INT,
- NUMBER_OF_THREADS INT,
- QUEUE_NAME varchar(255) NOT NULL,
- WALL_TIME_LIMIT INT,
- TOTAL_PHYSICAL_MEMORY INT,
- STATIC_WORKING_DIR varchar(255),
- OVERRIDE_LOGIN_USER_NAME varchar(255),
- OVERRIDE_SCRATCH_LOCATION varchar(255),
- OVERRIDE_ALLOCATION_PROJECT_NUMBER varchar(255),
- PARALLEL_GROUP_COUNT INT,
- PRIMARY KEY (EXPERIMENT_ID,RESOURCE_HOST_ID,QUEUE_NAME),
- FOREIGN KEY (EXPERIMENT_ID) REFERENCES EXPERIMENT(EXPERIMENT_ID) ON DELETE CASCADE
-)ENGINE=InnoDB DEFAULT CHARSET=latin1;
-
--- Rename storage resource ID to input storage resource ID
-ALTER TABLE USER_CONFIGURATION_DATA CHANGE COLUMN STORAGE_RESOURCE_ID INPUT_STORAGE_RESOURCE_ID VARCHAR(255) DEFAULT NULL;
-ALTER TABLE PROCESS CHANGE COLUMN STORAGE_RESOURCE_ID INPUT_STORAGE_RESOURCE_ID VARCHAR(255) DEFAULT NULL;
-
--- Add output storage resource ID columns
-ALTER TABLE USER_CONFIGURATION_DATA ADD COLUMN IF NOT EXISTS OUTPUT_STORAGE_RESOURCE_ID VARCHAR(255) DEFAULT NULL;
-ALTER TABLE PROCESS ADD COLUMN IF NOT EXISTS OUTPUT_STORAGE_RESOURCE_ID VARCHAR(255) DEFAULT NULL;
-
--- Update OUTPUT_STORAGE_RESOURCE_ID with INPUT_STORAGE_RESOURCE_ID when missing
-UPDATE USER_CONFIGURATION_DATA
-SET OUTPUT_STORAGE_RESOURCE_ID = INPUT_STORAGE_RESOURCE_ID
-WHERE (OUTPUT_STORAGE_RESOURCE_ID IS NULL OR OUTPUT_STORAGE_RESOURCE_ID = '')
- AND INPUT_STORAGE_RESOURCE_ID IS NOT NULL
- AND INPUT_STORAGE_RESOURCE_ID != '';
-
-UPDATE PROCESS
-SET OUTPUT_STORAGE_RESOURCE_ID = INPUT_STORAGE_RESOURCE_ID
-WHERE (OUTPUT_STORAGE_RESOURCE_ID IS NULL OR OUTPUT_STORAGE_RESOURCE_ID = '')
- AND INPUT_STORAGE_RESOURCE_ID IS NOT NULL
- AND INPUT_STORAGE_RESOURCE_ID != '';
\ No newline at end of file
diff --git a/.devcontainer/database_scripts/init/05-replica-catalog-migrations.sql b/.devcontainer/database_scripts/init/05-replica-catalog-migrations.sql
deleted file mode 100644
index 1c3b8d1c3ff..00000000000
--- a/.devcontainer/database_scripts/init/05-replica-catalog-migrations.sql
+++ /dev/null
@@ -1,27 +0,0 @@
-USE replica_catalog;
-
--- AIRAVATA-3697: Support file names that have UTF8 characters and that are long
-ALTER TABLE
- DATA_PRODUCT
-MODIFY
- PRODUCT_NAME text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
-
-ALTER TABLE
- DATA_PRODUCT
-MODIFY
- PRODUCT_DESCRIPTION varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
-
-ALTER TABLE
- DATA_REPLICA_LOCATION
-MODIFY
- REPLICA_NAME text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
-
-ALTER TABLE
- DATA_REPLICA_LOCATION
-MODIFY
- REPLICA_DESCRIPTION varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
-
-ALTER TABLE
- DATA_REPLICA_LOCATION
-MODIFY
- FILE_PATH varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
diff --git a/.devcontainer/database_scripts/init/06-cloud-execution-support-migration.sql b/.devcontainer/database_scripts/init/06-cloud-execution-support-migration.sql
deleted file mode 100644
index 81718b8f964..00000000000
--- a/.devcontainer/database_scripts/init/06-cloud-execution-support-migration.sql
+++ /dev/null
@@ -1,84 +0,0 @@
-USE app_catalog;
-
--- Add RESOURCE_TYPE column to base table
-ALTER TABLE `GROUP_COMPUTE_RESOURCE_PREFERENCE`
- ADD COLUMN `RESOURCE_TYPE` VARCHAR(255) NOT NULL DEFAULT 'SLURM';
-
--- Make sure all future inserts require an explicit RESOURCE_TYPE
-ALTER TABLE `GROUP_COMPUTE_RESOURCE_PREFERENCE`
- ALTER COLUMN `RESOURCE_TYPE` DROP DEFAULT;
-
--- Create the new Slurm-specific table
-CREATE TABLE `SLURM_GROUP_COMPUTE_RESOURCE_PREFERENCE`
-(
- `RESOURCE_ID` VARCHAR(255) NOT NULL,
- `GROUP_RESOURCE_PROFILE_ID` VARCHAR(255) NOT NULL,
- `PREFERED_BATCH_QUEUE` VARCHAR(255) DEFAULT NULL,
- `ALLOCATION_PROJECT_NUMBER` VARCHAR(255) DEFAULT NULL,
- `USAGE_REPORTING_GATEWAY_ID` VARCHAR(255) DEFAULT NULL,
- `QUALITY_OF_SERVICE` VARCHAR(255) DEFAULT NULL,
- `RESERVATION` VARCHAR(255) DEFAULT NULL,
- `RESERVATION_START_TIME` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `RESERVATION_END_TIME` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
- `SSH_ACCOUNT_PROVISIONER` VARCHAR(255) DEFAULT NULL,
- `SSH_ACCOUNT_PROVISIONER_ADDITIONAL_INFO` TEXT DEFAULT NULL,
- PRIMARY KEY (`RESOURCE_ID`, `GROUP_RESOURCE_PROFILE_ID`),
- CONSTRAINT `FK_SLURM_PREF_TO_BASE` FOREIGN KEY (`RESOURCE_ID`, `GROUP_RESOURCE_PROFILE_ID`)
- REFERENCES `GROUP_COMPUTE_RESOURCE_PREFERENCE` (`RESOURCE_ID`, `GROUP_RESOURCE_PROFILE_ID`)
- ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-
--- Migrate Slurm-specific field data from the base table into the new Slurm table
-INSERT INTO `SLURM_GROUP_COMPUTE_RESOURCE_PREFERENCE` (
- `RESOURCE_ID`,
- `GROUP_RESOURCE_PROFILE_ID`,
- `PREFERED_BATCH_QUEUE`,
- `ALLOCATION_PROJECT_NUMBER`,
- `USAGE_REPORTING_GATEWAY_ID`,
- `QUALITY_OF_SERVICE`,
- `RESERVATION`,
- `RESERVATION_START_TIME`,
- `RESERVATION_END_TIME`,
- `SSH_ACCOUNT_PROVISIONER`,
- `SSH_ACCOUNT_PROVISIONER_ADDITIONAL_INFO`
-)
-SELECT
- `RESOURCE_ID`,
- `GROUP_RESOURCE_PROFILE_ID`,
- `PREFERED_BATCH_QUEUE`,
- `ALLOCATION_PROJECT_NUMBER`,
- `USAGE_REPORTING_GATEWAY_ID`,
- `QUALITY_OF_SERVICE`,
- `RESERVATION`,
- `RESERVATION_START_TIME`,
- `RESERVATION_END_TIME`,
- `SSH_ACCOUNT_PROVISIONER`,
- `SSH_ACCOUNT_PROVISIONER_ADDITIONAL_INFO`
-FROM `GROUP_COMPUTE_RESOURCE_PREFERENCE`;
-
--- Drop the Slurm-specific columns from the base table
-ALTER TABLE `GROUP_COMPUTE_RESOURCE_PREFERENCE`
-DROP COLUMN `PREFERED_BATCH_QUEUE`,
- DROP COLUMN `ALLOCATION_PROJECT_NUMBER`,
- DROP COLUMN `USAGE_REPORTING_GATEWAY_ID`,
- DROP COLUMN `QUALITY_OF_SERVICE`,
- DROP COLUMN `RESERVATION`,
- DROP COLUMN `RESERVATION_START_TIME`,
- DROP COLUMN `RESERVATION_END_TIME`,
- DROP COLUMN `SSH_ACCOUNT_PROVISIONER`,
- DROP COLUMN `SSH_ACCOUNT_PROVISIONER_ADDITIONAL_INFO`;
-
--- Create the AWS-specific group compute preference table
-CREATE TABLE `AWS_GROUP_COMPUTE_RESOURCE_PREFERENCE`
-(
- `RESOURCE_ID` VARCHAR(255) NOT NULL,
- `GROUP_RESOURCE_PROFILE_ID` VARCHAR(255) NOT NULL,
- `AWS_REGION` VARCHAR(255) NOT NULL,
- `PREFERRED_AMI_ID` VARCHAR(255) NOT NULL,
- `PREFERRED_INSTANCE_TYPE` VARCHAR(255) NOT NULL,
-
- PRIMARY KEY (`RESOURCE_ID`, `GROUP_RESOURCE_PROFILE_ID`),
- CONSTRAINT `FK_AWS_PREF_TO_BASE` FOREIGN KEY (`RESOURCE_ID`, `GROUP_RESOURCE_PROFILE_ID`)
- REFERENCES `GROUP_COMPUTE_RESOURCE_PREFERENCE` (`RESOURCE_ID`, `GROUP_RESOURCE_PROFILE_ID`)
- ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
\ No newline at end of file
diff --git a/.devcontainer/database_scripts/init/07-cleanup-strategy-migration.sql b/.devcontainer/database_scripts/init/07-cleanup-strategy-migration.sql
deleted file mode 100644
index 4b40dbd7ed0..00000000000
--- a/.devcontainer/database_scripts/init/07-cleanup-strategy-migration.sql
+++ /dev/null
@@ -1,4 +0,0 @@
-USE experiment_catalog;
-
--- Add cleanupStrategy flag to EXPERIMENT
-ALTER TABLE EXPERIMENT ADD COLUMN IF NOT EXISTS CLEANUP_STRATEGY VARCHAR(255) DEFAULT 'NONE';
diff --git a/.devcontainer/dev.env.defaults b/.devcontainer/dev.env.defaults
new file mode 100644
index 00000000000..ec6909a129d
--- /dev/null
+++ b/.devcontainer/dev.env.defaults
@@ -0,0 +1,19 @@
+# Airavata dev defaults — single source of truth for local development
+# Used by: docker-compose (keycloak-setup), portal .env.local
+# Shared config (DB init, Keycloak setup): ../conf/
+
+# Database — db.airavata.localhost:13306 (host) / db:3306 (docker network)
+DB_HOST=localhost
+DB_PORT=13306
+DB_USER=airavata
+DB_PASSWORD=123456
+
+# Keycloak — auth.airavata.localhost:18080 (host) / keycloak:18080 (docker network)
+KEYCLOAK_URL=http://localhost:18080
+KEYCLOAK_REALM=default
+REALM_NAME=default
+PGA_CLIENT_SECRET=m36BXQIxX3j3VILadeHMK5IvbOeRlCCc
+DEFAULT_ADMIN_USERNAME=default-admin
+DEFAULT_ADMIN_PASSWORD=admin123
+KEYCLOAK_ADMIN=admin
+KEYCLOAK_ADMIN_PASSWORD=admin
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index ce760a63344..9c2f5bbf807 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,11 +1,12 @@
-// For format details, see https://aka.ms/devcontainer.json. For config options, see the
-// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu
+// For format details, see https://aka.ms/devcontainer.json
{
"name": "Airavata",
- "dockerComposeFile": "docker-compose.yml",
+ "dockerComposeFile": "compose.yml",
"service": "devcontainer",
- "features": {},
"workspaceFolder": "/home/developer/workspace",
+ "features": {
+ "ghcr.io/devcontainers/features/java:1": { "version": "25", "installMaven": "true" }
+ },
"forwardPorts": [],
"customizations": {
"vscode": {
@@ -20,8 +21,8 @@
"ms-python.isort",
"ms-toolsai.jupyter",
"njpwerner.autodocstring",
- "vscjava.vscode-java-pack",
+ "vscjava.vscode-java-pack"
]
- },
+ }
}
}
\ No newline at end of file
diff --git a/.devcontainer/docker-compose-alt.yml b/.devcontainer/docker-compose-alt.yml
deleted file mode 100644
index 50ff2937cc0..00000000000
--- a/.devcontainer/docker-compose-alt.yml
+++ /dev/null
@@ -1,279 +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.
-
-version: '3'
-services:
-
- controller:
- image: airavata/controller
- restart: always
- depends_on:
- - zookeeper
- - db
- - kafka
- - keycloak
- environment:
- - zookeeper.server.connection=zookeeper:2181
- command: ["/tmp/wait-for-it.sh", "zookeeper:2181", "--" , "/opt/apache-airavata-controller/bin/controller.sh"]
-
- apiserver:
- image: airavata/api-server
- restart: always
- depends_on:
- - zookeeper
- - db
- - kafka
- - keycloak
- - rabbitmq
- links:
- - "keycloak:airavata.host"
- ports:
- - "8930:8930"
- - "7878:7878"
- - "8962:8962"
- - "8960:8960"
- - "8970:8970"
- volumes:
- - ../resources/keystores:/opt/keystores
- environment:
- - registry.jdbc.driver=org.mariadb.jdbc.Driver
- - registry.jdbc.url=jdbc:mariadb://db:3306/experiment_catalog
- - registry.jdbc.user=root
- - registry.jdbc.password=123456
- - start.derby.server.mode=false
- - validationQuery=SELECT 1 from CONFIGURATION
- - enable.sharing=true
- - default.registry.user=default-admin
- - default.registry.password=123456
- - default.registry.gateway=default
- - default.registry.oauth.client.id=pga
- - default.registry.oauth.client.secret=9790c8c4-7d9b-4ccc-a820-ca5aac38d2ad
- - super.tenant.gatewayId=default
- - cluster.status.monitoring.enable=false
- - cluster.status.monitoring.repeat.time=18000
- - appcatalog.jdbc.driver=org.mariadb.jdbc.Driver
- - appcatalog.jdbc.url=jdbc:mariadb://db:3306/app_catalog
- - appcatalog.jdbc.user=root
- - appcatalog.jdbc.password=123456
- - appcatalog.validationQuery=SELECT 1 from CONFIGURATION
- - replicacatalog.jdbc.driver=org.mariadb.jdbc.Driver
- - replicacatalog.jdbc.url=jdbc:mariadb://db:3306/replica_catalog
- - replicacatalog.jdbc.user=root
- - replicacatalog.jdbc.password=123456
- - replicacatalog.validationQuery=SELECT 1 from CONFIGURATION
- - workflowcatalog.jdbc.driver=org.mariadb.jdbc.Driver
- - workflowcatalog.jdbc.url=jdbc:mariadb://db:3306/workflow_catalog
- - workflowcatalog.jdbc.user=root
- - workflowcatalog.jdbc.password=123456
- - workflowcatalog.validationQuery=SELECT 1 from CONFIGURATION
- - sharingcatalog.jdbc.driver=org.mariadb.jdbc.Driver
- - sharingcatalog.jdbc.url=jdbc:mariadb://db:3306/sharing_catalog
- - sharingcatalog.jdbc.user=root
- - sharingcatalog.jdbc.password=123456
- - sharingcatalog.validationQuery=SELECT 1 from CONFIGURATION
- - sharing_server=org.apache.airavata.sharing.registry.server.SharingRegistryServer
- - sharing.registry.server.host=0.0.0.0
- - sharing.registry.server.port=7878
- - apiserver.class=org.apache.airavata.api.server.AiravataAPIServer
- - apiserver.host=0.0.0.0
- - apiserver.port=8930
- - orchestrator=org.apache.airavata.orchestrator.server.OrchestratorServer
- - orchestrator.server.host=0.0.0.0
- - orchestrator.server.port=8940
- - orchestrator.server.min.threads=50
- - job.validators=org.apache.airavata.orchestrator.core.validator.impl.BatchQueueValidator,org.apache.airavata.orchestrator.core.validator.impl.ExperimentStatusValidator
- - enable.validation=true
- - host.scheduler=org.apache.airavata.orchestrator.core.schedule.DefaultHostScheduler
- - regserver=org.apache.airavata.registry.api.service.RegistryAPIServer
- - regserver.server.host=localhost
- - regserver.server.port=8970
- - regserver.server.min.threads=50
- - job.notification.enable=true
- - job.notification.emailids=
- - credential.store.keystore.url=/opt/keystores/airavata.sym.p12
- - credential.store.keystore.alias=airavata
- - credential.store.keystore.password=airavata
- - credential.store.jdbc.url=jdbc:mariadb://db:3306/credential_store
- - credential.store.jdbc.user=root
- - credential.store.jdbc.password=123456
- - credential.store.jdbc.driver=org.mariadb.jdbc.Driver
- - credential.store.server.host=0.0.0.0
- - credential.store.server.port=8960
- - credential.store.class=org.apache.airavata.credential.store.server.CredentialStoreServer
- - credential.store.jdbc.validationQuery=SELECT 1 from CONFIGURATION
- - rabbitmq.broker.url=amqp://guest:guest@rabbitmq:5672/develop
- - rabbitmq.status.exchange.name=status_exchange
- - rabbitmq.process.exchange.name=process_exchange
- - rabbitmq.experiment.exchange.name=experiment_exchange
- - durable.queue=false
- - prefetch.count=200
- - embedded.zk=false
- - zookeeper.server.connection=zookeeper:2181
- - security.manager.class=org.apache.airavata.service.security.KeyCloakSecurityManager
- - TLS.enabled=true
- - TLS.client.timeout=10000
- - keystore.path=/opt/keystores/airavata.p12
- - keystore.password=airavata
- - authz.cache.enabled=true
- - authz.cache.manager.class=org.apache.airavata.service.security.authzcache.DefaultAuthzCacheManager
- - in.memory.cache.size=1000
- - profile.service.server.host=0.0.0.0
- - profile.service.server.port=8962
- - profile_service.class=org.apache.airavata.service.profile.server.ProfileServiceServer
- - profile.service.jdbc.url=jdbc:mariadb://db:3306/profile_service
- - profile.service.jdbc.user=root
- - profile.service.jdbc.password=123456
- - profile.service.jdbc.driver=org.mariadb.jdbc.Driver
- - profile.service.validationQuery=SELECT 1
- - iam.server.url=https://airavata.host:8443
- - iam.server.super.admin.username=admin
- - iam.server.super.admin.password=admin
- - db_event_manager.class=org.apache.airavata.db.event.manager.DBEventManagerRunner
- - thrift.client.pool.abandoned.removal.enabled=true
- command: ["/tmp/wait-for-it.sh", "db:3306", "--", "/tmp/wait-for-it.sh", "rabbitmq:5672", "--" , "/opt/apache-airavata-api-server/bin/airavata-server-start.sh", "api-orch"]
-
- prewm:
- image: airavata/pre-wm
- restart: always
- depends_on:
- - zookeeper
- - db
- - kafka
- - keycloak
- - apiserver
- - rabbitmq
- environment:
- - zookeeper.server.connection=zookeeper:2181
- - regserver.server.host=apiserver
- - rabbitmq.broker.url=amqp://guest:guest@rabbitmq:5672/develop
- - pre.workflow.manager.loadbalance.clusters=false
- command: ["/tmp/wait-for-it.sh", "zookeeper:2181", "--", "/tmp/wait-for-it.sh", "apiserver:8970", "--" , "/tmp/wait-for-it.sh", "rabbitmq:5672", "--", "/opt/apache-airavata-pre-wm/bin/pre-wm.sh"]
-
- participant:
- image: airavata/participant
- restart: always
- depends_on:
- - zookeeper
- - db
- - kafka
- - keycloak
- - apiserver
- - rabbitmq
- environment:
- - zookeeper.server.connection=zookeeper:2181
- - regserver.server.host=apiserver
- - rabbitmq.broker.url=amqp://guest:guest@rabbitmq:5672/develop
- - credential.store.server.host=apiserver
- - email.based.monitor.address=CHANGEME
- - enable.realtime.monitor=false
- - profile.service.server.host=apiserver
- - profile.service.server.port=8962
- command: ["/tmp/wait-for-it.sh", "zookeeper:2181", "--", "/tmp/wait-for-it.sh", "apiserver:8970", "--" , "/tmp/wait-for-it.sh", "rabbitmq:5672", "--", "/opt/apache-airavata-participant/bin/participant.sh"]
-
- emailmonitor:
- image: airavata/email-monitor
- restart: always
- depends_on:
- - zookeeper
- - db
- - kafka
- - keycloak
- - apiserver
- - rabbitmq
- environment:
- - zookeeper.server.connection=zookeeper:2181
- - regserver.server.host=apiserver
- - regserver.server.port=8970
- - email.based.monitor.address=CHANGEME
- - email.based.monitor.password=CHANGEME
- - kafka.broker.url=kafka:9092
- command: ["/tmp/wait-for-it.sh", "zookeeper:2181", "--", "/tmp/wait-for-it.sh", "apiserver:8970", "--" , "/tmp/wait-for-it.sh", "kafka:9092", "--", "/opt/apache-airavata-email-monitor/bin/email-monitor.sh"]
-
- db:
- image: mariadb:10.4.13
- environment:
- MYSQL_ROOT_PASSWORD: 123456
- ports:
- - "3306:3306"
- volumes:
- - mariadb_data:/var/lib/mysql
-
- keycloak:
- image: keycloak/keycloak:24.0
- environment:
- KEYCLOAK_ADMIN: admin
- KEYCLOAK_ADMIN_PASSWORD: admin
- command: ["start-dev", "--https-port=8443"]
- ports:
- - "8443:8443"
- - "18080:8080"
-
- rabbitmq:
- image: rabbitmq:3.12.14-management
- ports:
- - "5672:5672"
- - "15672:15672"
- environment:
- - RABBITMQ_DEFAULT_USER=guest
- - RABBITMQ_DEFAULT_PASS=guest
- command: >
- bash -c "
- rabbitmq-server &
- until rabbitmqctl status > /dev/null 2>&1; do
- echo 'Waiting for RabbitMQ to be ready...'
- sleep 2
- done
- rabbitmqctl add_vhost develop
- rabbitmqctl set_permissions -p develop guest '.*' '.*' '.*'
- wait
- "
-
- zookeeper:
- image: wurstmeister/zookeeper
- hostname: zookeeper
- ports:
- - "2181:2181"
-
- kafka:
- image: wurstmeister/kafka
- hostname: kafka
- ports:
- - "9092:9092"
- environment:
- KAFKA_ADVERTISED_HOST_NAME: kafka
- KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
-
- postwm:
- image: airavata/post-wm
- restart: always
- depends_on:
- - zookeeper
- - db
- - kafka
- - keycloak
- - apiserver
- - rabbitmq
- environment:
- - zookeeper.server.connection=zookeeper:2181
- - regserver.server.host=apiserver
- - rabbitmq.broker.url=amqp://guest:guest@rabbitmq:5672/develop
- - post.workflow.manager.loadbalance.clusters=false
- - kafka.broker.url=kafka:9092
- command: ["/tmp/wait-for-it.sh", "zookeeper:2181", "--", "/tmp/wait-for-it.sh", "apiserver:8970", "--" , "/tmp/wait-for-it.sh", "rabbitmq:5672", "--", "/tmp/wait-for-it.sh", "kafka:9092", "--", "/opt/apache-airavata-post-wm/bin/post-wm.sh"]
-
-volumes:
- mariadb_data:
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
deleted file mode 100644
index ec91fabdf51..00000000000
--- a/.devcontainer/docker-compose.yml
+++ /dev/null
@@ -1,263 +0,0 @@
-networks:
- airavata-network:
- driver: bridge
- name: airavata.host
- ipam:
- config:
- - subnet: 192.168.100.0/24
- gateway: 192.168.100.1
-
-volumes:
- db_data:
- driver: local
-
-services:
- devcontainer:
- profiles:
- - development
- build:
- dockerfile: Dockerfile
- target: deps
- container_name: devcontainer
- restart: always
- volumes:
- - ..:/home/developer/workspace:cached
- - $HOME/.m2:/home/developer/.m2
- - $HOME/.ssh:/home/developer/.ssh
- networks:
- airavata-network:
- ipv4_address: 192.168.100.9
- extra_hosts:
- - "airavata.host:192.168.100.1"
- ports:
- - "7878:7878" # sharing registry service
- - "8000:8000" # tunnel service
- - "17000:17000" # tunnel service (ingress)
- - "8050:8050" # file service
- - "8082:8082" # rest proxy
- - "8930:8930" # api service
- - "8960:8960" # cred store service
- - "8962:8962" # profile service
- - "8970:8970" # registry service
- - "18800:18800" # agent service (http)
- - "19900:19900" # agent service (gRPC)
- - "18889:18889" # research service (http)
- - "19908:19908" # research service (gRPC)
- command: sleep infinity
-
- microservices:
- profiles:
- - staging
- build:
- dockerfile: Dockerfile
- target: runner
- container_name: microservices
- restart: always
- volumes:
- - ../vault:/app/vault
- networks:
- airavata-network:
- ipv4_address: 192.168.100.9
- extra_hosts:
- - "airavata.host:192.168.100.1"
- ports:
- - "7878:7878" # sharing registry service
- - "8000:8000" # tunnel service
- - "17000:17000" # tunnel service (ingress)
- - "8050:8050" # file service
- - "8930:8930" # api service
- - "8960:8960" # cred store service
- - "8962:8962" # profile service
- - "8970:8970" # registry service
- - "18800:18800" # agent service (http)
- - "19900:19900" # agent service (gRPC)
- - "18889:18889" # research service (http)
- - "19908:19908" # research service (gRPC)
- depends_on:
- - db
- - kafka
- - keycloak
- - rabbitmq
- - sshd
-
- portals:
- profiles:
- - development
- - staging
- build:
- context: portals
- dockerfile: Dockerfile
- container_name: portals
- restart: always
- volumes:
- - /tmp:/tmp
- - ./portals/pga_config.php:/var/www/html/default/config/pga_config.php
- networks:
- airavata-network:
- ipv4_address: 192.168.100.11
- extra_hosts:
- - "airavata.host:192.168.100.1"
-
- jupyterhub:
- profiles:
- - development
- - staging
- build:
- context: ../modules/research-framework/research-hub/compose
- dockerfile: Dockerfile
- container_name: jupyterhub
- restart: always
- environment:
- OAUTH_CLIENT_ID: "cs-jupyterlab"
- OAUTH_CLIENT_SECRET: "DxeMtfiWU1qkDEmaGHf13RDahCujzhy1"
- JUPYTERHUB_CRYPT_KEY: "a99323294a5d6f9b1d0e7e33450dff44db664264231b985e069c6eba8f9a3e09"
- DOCKER_NETWORK_NAME: jupyterhub_network
- DOCKER_NOTEBOOK_IMAGE: cybershuttle/dev_jupyterlab-base
- volumes:
- - ../modules/research-framework/research-hub/compose/jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py
- - /var/run/docker.sock:/var/run/docker.sock
- networks:
- airavata-network:
- ipv4_address: 192.168.100.12
- extra_hosts:
- - "airavata.host:192.168.100.1"
-
- proxy:
- image: nginx:stable
- container_name: proxy
- restart: always
- volumes:
- - ../vault.local/server.key:/vault/server.key:ro
- - ../vault.local/server.crt:/vault/server.crt:ro
- - ./proxy/nginx.conf:/etc/nginx/nginx.conf:ro
- - ./proxy/http.conf:/etc/nginx/conf.d/http.conf:ro
- - ./proxy/stream.conf:/etc/nginx/conf.d/stream.conf:ro
- networks:
- airavata-network:
- ipv4_address: 192.168.100.13
- extra_hosts:
- - "airavata.host:192.168.100.1"
- ports:
- - "5173:5173" # tls + research portal
- - "8008:8008" # tls + php portal
- - "8009:8009" # tls + django portal
- - "8443:8443" # tls + keycloak
- - "9930:9930" # tls + api
- - "20000:20000" # tls + jupyterhub
-
- keycloak:
- image: keycloak/keycloak:25.0
- container_name: keycloak
- restart: always
- environment:
- KEYCLOAK_ADMIN: admin
- KEYCLOAK_ADMIN_PASSWORD: admin
- volumes:
- - ./keycloak/realm-default.json:/opt/keycloak/data/import/realm-default.json
- - ./keycloak/keycloak.conf:/opt/keycloak/conf/keycloak.conf
- command: [ "start", "--import-realm" ]
- networks:
- airavata-network:
- ipv4_address: 192.168.100.14
- extra_hosts:
- - "airavata.host:192.168.100.1"
- ports:
- - "18080:18080"
-
- db:
- image: mariadb:10.4.13
- container_name: db
- restart: always
- environment:
- MYSQL_ROOT_PASSWORD: 123456
- MYSQL_USER: airavata
- MYSQL_PASSWORD: 123456
- volumes:
- - ./database_scripts/init:/docker-entrypoint-initdb.d
- - db_data:/var/lib/mysql
- command:
- [
- "mysqld",
- "--character-set-server=utf8mb4",
- "--collation-server=utf8mb4_unicode_ci",
- "--sql-mode=NO_ENGINE_SUBSTITUTION",
- ]
- networks:
- airavata-network:
- ipv4_address: 192.168.100.15
- extra_hosts:
- - "airavata.host:192.168.100.1"
- ports:
- - "13306:3306"
-
- rabbitmq:
- image: rabbitmq:4.0-management
- container_name: rabbitmq
- restart: always
- environment:
- RABBITMQ_DEFAULT_VHOST: develop
- networks:
- airavata-network:
- ipv4_address: 192.168.100.16
- extra_hosts:
- - "airavata.host:192.168.100.1"
- ports:
- - "5672:5672"
- - "15672:15672"
-
- zookeeper:
- image: zookeeper:latest
- container_name: zookeeper
- restart: always
- networks:
- airavata-network:
- ipv4_address: 192.168.100.17
- extra_hosts:
- - "airavata.host:192.168.100.1"
- ports:
- - "2181:2181"
-
- kafka:
- image: wurstmeister/kafka:latest
- container_name: kafka
- restart: always
- environment:
- KAFKA_ADVERTISED_HOST_NAME: airavata.host
- KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
- networks:
- airavata-network:
- ipv4_address: 192.168.100.18
- extra_hosts:
- - "airavata.host:192.168.100.1"
- ports:
- - "9092:9092"
- depends_on:
- - zookeeper
-
- sshd:
- image: panubo/sshd
- container_name: sshd
- restart: always
- volumes:
- - /tmp:/tmp
- networks:
- airavata-network:
- ipv4_address: 192.168.100.20
- extra_hosts:
- - "airavata.host:192.168.100.1"
- ports:
- - "22222:22"
-
- adminer:
- image: adminer:latest
- container_name: adminer
- restart: always
- networks:
- airavata-network:
- ipv4_address: 192.168.100.21
- extra_hosts:
- - "airavata.host:192.168.100.1"
- ports:
- - "18088:8080"
- depends_on:
- - db
diff --git a/.devcontainer/gateway-storage/Dockerfile b/.devcontainer/gateway-storage/Dockerfile
deleted file mode 100644
index 4e1ddb220ad..00000000000
--- a/.devcontainer/gateway-storage/Dockerfile
+++ /dev/null
@@ -1,22 +0,0 @@
-FROM ubuntu:16.04
-
-RUN apt-get update && apt-get install -y openssh-server
-RUN mkdir /var/run/sshd
-RUN echo 'root:root' | chpasswd
-RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
-
-# SSH login fix. Otherwise user is kicked off after login
-RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
-
-ENV NOTVISIBLE "in users profile"
-RUN echo "export VISIBLE=now" >> /etc/profile
-
-RUN mkdir -p /var/www/portals/gateway-user-data
-RUN mkdir /root/.ssh
-ADD authorized_keys /root/.ssh/authorized_keys
-
-RUN chmod 700 /root/.ssh
-RUN chmod 644 /root/.ssh/authorized_keys
-
-EXPOSE 22
-CMD ["/usr/sbin/sshd", "-D"]
\ No newline at end of file
diff --git a/.devcontainer/gateway-storage/authorized_keys b/.devcontainer/gateway-storage/authorized_keys
deleted file mode 100644
index de688215ec7..00000000000
--- a/.devcontainer/gateway-storage/authorized_keys
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCr0zWkrd3MJe397xm1q7EBZd7HuIcgLva0Dl3uyE3N7vqcWeXjXngtVicJLIROIASwy1+MAbhDDQLykQtIcJTJ5cvZM29nqqsIZ9l0acURVtDVnMd7PqRs+0wBQnrdZm34NT3/QJhxZTOuSn+i44VPFM1HWFObDP7GGtRMheGAKKV2tTbF1o/q1XaBiQJABOsP/Yt20nox1Hd6OCl0E3loK0sA+DnVVK9Ox0WShr4CWOLUCWavghXbgbDQPe/3VMSvwYqvLjNd+VfrNiAx7jNm+nxVKQSzYJ46RSwVs4jLKrnrG85+eAFwLeIFjlLyrsVNvBlOCs6XQ6lmVDD1u97B
\ No newline at end of file
diff --git a/.devcontainer/keycloak/realm-default.json b/.devcontainer/keycloak/realm-default.json
deleted file mode 100644
index 2bc90a07665..00000000000
--- a/.devcontainer/keycloak/realm-default.json
+++ /dev/null
@@ -1,2649 +0,0 @@
-{
- "id" : "afc8036c-62c3-462e-ae10-e1727c4bd8f7",
- "realm" : "default",
- "displayName" : "",
- "displayNameHtml" : "",
- "notBefore" : 0,
- "defaultSignatureAlgorithm" : "RS256",
- "revokeRefreshToken" : false,
- "refreshTokenMaxReuse" : 0,
- "accessTokenLifespan" : 7200,
- "accessTokenLifespanForImplicitFlow" : 3600,
- "ssoSessionIdleTimeout" : 604800,
- "ssoSessionMaxLifespan" : 604800,
- "ssoSessionIdleTimeoutRememberMe" : 0,
- "ssoSessionMaxLifespanRememberMe" : 0,
- "offlineSessionIdleTimeout" : 2592000,
- "offlineSessionMaxLifespanEnabled" : false,
- "offlineSessionMaxLifespan" : 5184000,
- "clientSessionIdleTimeout" : 0,
- "clientSessionMaxLifespan" : 0,
- "clientOfflineSessionIdleTimeout" : 0,
- "clientOfflineSessionMaxLifespan" : 0,
- "accessCodeLifespan" : 60,
- "accessCodeLifespanUserAction" : 300,
- "accessCodeLifespanLogin" : 1800,
- "actionTokenGeneratedByAdminLifespan" : 43200,
- "actionTokenGeneratedByUserLifespan" : 300,
- "oauth2DeviceCodeLifespan" : 600,
- "oauth2DevicePollingInterval" : 5,
- "enabled" : true,
- "sslRequired" : "external",
- "registrationAllowed" : false,
- "registrationEmailAsUsername" : false,
- "rememberMe" : false,
- "verifyEmail" : false,
- "loginWithEmailAllowed" : true,
- "duplicateEmailsAllowed" : false,
- "resetPasswordAllowed" : false,
- "editUsernameAllowed" : false,
- "bruteForceProtected" : false,
- "permanentLockout" : false,
- "maxTemporaryLockouts" : 0,
- "maxFailureWaitSeconds" : 900,
- "minimumQuickLoginWaitSeconds" : 60,
- "waitIncrementSeconds" : 60,
- "quickLoginCheckMilliSeconds" : 1000,
- "maxDeltaTimeSeconds" : 43200,
- "failureFactor" : 30,
- "roles" : {
- "realm" : [ {
- "id" : "4cd8497d-db71-41dd-9186-f7df0c22d446",
- "name" : "gateway-provider",
- "description" : "",
- "composite" : false,
- "clientRole" : false,
- "containerId" : "afc8036c-62c3-462e-ae10-e1727c4bd8f7",
- "attributes" : { }
- }, {
- "id" : "b585e111-f934-43b7-b9c2-cbad0c7dc08a",
- "name" : "default-roles-10000000",
- "description" : "${role_default-roles}",
- "composite" : true,
- "composites" : {
- "realm" : [ "offline_access", "uma_authorization" ],
- "client" : {
- "account" : [ "view-profile", "manage-account" ]
- }
- },
- "clientRole" : false,
- "containerId" : "afc8036c-62c3-462e-ae10-e1727c4bd8f7",
- "attributes" : { }
- }, {
- "id" : "420f07dc-c07c-4ea8-bf56-f6adf3f2bbc7",
- "name" : "uma_authorization",
- "description" : "${role_uma_authorization}",
- "composite" : false,
- "clientRole" : false,
- "containerId" : "afc8036c-62c3-462e-ae10-e1727c4bd8f7",
- "attributes" : { }
- }, {
- "id" : "61fafc5e-96fc-4644-98a9-94f9baf654e6",
- "name" : "admin",
- "description" : "",
- "composite" : false,
- "clientRole" : false,
- "containerId" : "afc8036c-62c3-462e-ae10-e1727c4bd8f7",
- "attributes" : { }
- }, {
- "id" : "1f03206b-d918-491b-a33f-ee96147b310d",
- "name" : "admin-read-only",
- "description" : "",
- "composite" : false,
- "clientRole" : false,
- "containerId" : "afc8036c-62c3-462e-ae10-e1727c4bd8f7",
- "attributes" : { }
- }, {
- "id" : "3f7e69dc-75d4-4388-8a34-e82d32071dc9",
- "name" : "offline_access",
- "description" : "${role_offline-access}",
- "composite" : false,
- "clientRole" : false,
- "containerId" : "afc8036c-62c3-462e-ae10-e1727c4bd8f7",
- "attributes" : { }
- }, {
- "id" : "ebb21957-06c9-4350-9157-576b10cc8761",
- "name" : "user-pending",
- "description" : "",
- "composite" : false,
- "clientRole" : false,
- "containerId" : "afc8036c-62c3-462e-ae10-e1727c4bd8f7",
- "attributes" : { }
- }, {
- "id" : "a2acdfe6-eb2a-4104-bb6a-be961e380d97",
- "name" : "gateway-user",
- "description" : "",
- "composite" : false,
- "clientRole" : false,
- "containerId" : "afc8036c-62c3-462e-ae10-e1727c4bd8f7",
- "attributes" : { }
- } ],
- "client" : {
- "realm-management" : [ {
- "id" : "d14d392e-59cf-49fd-8ba9-507ffaa329cc",
- "name" : "manage-identity-providers",
- "description" : "${role_manage-identity-providers}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "66a7f387-fd45-4b01-9457-12692a2d5180",
- "name" : "realm-admin",
- "description" : "${role_realm-admin}",
- "composite" : true,
- "composites" : {
- "client" : {
- "realm-management" : [ "manage-identity-providers", "create-client", "manage-authorization", "impersonation", "view-identity-providers", "manage-clients", "manage-users", "query-realms", "query-clients", "query-users", "view-clients", "manage-realm", "view-users", "view-authorization", "view-realm", "query-groups", "manage-events", "view-events" ]
- }
- },
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "d5d0c66d-b530-4fa4-af75-bf3b556873b0",
- "name" : "create-client",
- "description" : "${role_create-client}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "370cf6dc-2013-4a9b-a726-227edf3b6e04",
- "name" : "impersonation",
- "description" : "${role_impersonation}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "2fea86aa-2a95-429f-83c2-c2a9594ce050",
- "name" : "manage-authorization",
- "description" : "${role_manage-authorization}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "febffa99-60f8-4933-97c5-fe73d082802c",
- "name" : "view-identity-providers",
- "description" : "${role_view-identity-providers}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "658c0ab4-ded5-410a-ad62-f3c4a6a99ff4",
- "name" : "manage-clients",
- "description" : "${role_manage-clients}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "1cf0e329-8b88-4e64-95bb-e1a9e3ed2d28",
- "name" : "manage-users",
- "description" : "${role_manage-users}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "9ea990ff-0ece-463e-9792-c274aa005b3a",
- "name" : "query-realms",
- "description" : "${role_query-realms}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "6c20b3e8-97c4-4028-85c3-33531c8b8ed3",
- "name" : "query-clients",
- "description" : "${role_query-clients}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "c6aa40d9-b892-41e3-b59f-8dc332db8724",
- "name" : "query-users",
- "description" : "${role_query-users}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "24a3d19f-0d0a-4e33-b913-3166b675f5f6",
- "name" : "view-clients",
- "description" : "${role_view-clients}",
- "composite" : true,
- "composites" : {
- "client" : {
- "realm-management" : [ "query-clients" ]
- }
- },
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "65a3c9f0-9d28-43d8-bb95-d34eaf643493",
- "name" : "manage-realm",
- "description" : "${role_manage-realm}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "2fc992a6-c78e-4b64-9b44-b7241c993e05",
- "name" : "view-users",
- "description" : "${role_view-users}",
- "composite" : true,
- "composites" : {
- "client" : {
- "realm-management" : [ "query-users", "query-groups" ]
- }
- },
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "e0fd87a5-f6fd-4415-b38f-b767ee595812",
- "name" : "view-authorization",
- "description" : "${role_view-authorization}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "3ce7f726-99cc-4738-91d0-0d2d5559e013",
- "name" : "view-realm",
- "description" : "${role_view-realm}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "43cb7af1-22b5-49f2-a98a-2ac0b8ad887f",
- "name" : "query-groups",
- "description" : "${role_query-groups}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "03528436-9a19-41c6-bb7b-29a504fe7fa8",
- "name" : "manage-events",
- "description" : "${role_manage-events}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- }, {
- "id" : "8586e2c7-b3a5-4f64-beb6-5f61617f661e",
- "name" : "view-events",
- "description" : "${role_view-events}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "attributes" : { }
- } ],
- "cybershuttle-agent" : [ ],
- "cs-jupyterlab" : [ {
- "id" : "bc3da200-4725-43cc-abdc-efee7c26a748",
- "name" : "uma_protection",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "ac2469cf-1760-4d47-9079-7306fee96ae4",
- "attributes" : { }
- } ],
- "security-admin-console" : [ ],
- "admin-cli" : [ ],
- "account-console" : [ ],
- "pga" : [ {
- "id" : "d8d76309-d081-4159-b2cd-d9ca93eb7d02",
- "name" : "uma_protection",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "5e2398e0-3498-4da3-9262-4f2dcc7448fa",
- "attributes" : { }
- }, {
- "id" : "f8051cd8-10cb-44e6-8826-d323daa236d1",
- "name" : "gateway-provider",
- "description" : "",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "5e2398e0-3498-4da3-9262-4f2dcc7448fa",
- "attributes" : { }
- }, {
- "id" : "fb2c5f47-09e2-4f4b-b858-625f3c5442cd",
- "name" : "user-pending",
- "description" : "",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "5e2398e0-3498-4da3-9262-4f2dcc7448fa",
- "attributes" : { }
- }, {
- "id" : "c7d75283-b7c3-4b93-8804-9ce55bccf74c",
- "name" : "admin",
- "description" : "",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "5e2398e0-3498-4da3-9262-4f2dcc7448fa",
- "attributes" : { }
- }, {
- "id" : "da796582-cbf2-4b23-a31d-5fc9b4010bb0",
- "name" : "admin-read-only",
- "description" : "",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "5e2398e0-3498-4da3-9262-4f2dcc7448fa",
- "attributes" : { }
- }, {
- "id" : "42660438-3a37-466f-b748-d25a25ff9082",
- "name" : "gateway-user",
- "description" : "",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "5e2398e0-3498-4da3-9262-4f2dcc7448fa",
- "attributes" : { }
- } ],
- "broker" : [ {
- "id" : "dd71ed3a-bb48-41b6-9dae-e81170c4c445",
- "name" : "read-token",
- "description" : "${role_read-token}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "af99dd7f-6d3a-4fec-93c0-8deab50edf0e",
- "attributes" : { }
- } ],
- "account" : [ {
- "id" : "3fa60a39-7c55-434e-b602-3789dd70ec15",
- "name" : "view-applications",
- "description" : "${role_view-applications}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "c1602103-6c60-4e27-a7ed-c2c21d7801f2",
- "attributes" : { }
- }, {
- "id" : "2f3dc94f-9347-49cf-914e-dc3615e550e1",
- "name" : "view-profile",
- "description" : "${role_view-profile}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "c1602103-6c60-4e27-a7ed-c2c21d7801f2",
- "attributes" : { }
- }, {
- "id" : "501821b9-9fd0-42df-b89d-28375acfcbaa",
- "name" : "manage-account-links",
- "description" : "${role_manage-account-links}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "c1602103-6c60-4e27-a7ed-c2c21d7801f2",
- "attributes" : { }
- }, {
- "id" : "c9752755-7d07-40b7-bf61-da14c3d524f2",
- "name" : "view-groups",
- "description" : "${role_view-groups}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "c1602103-6c60-4e27-a7ed-c2c21d7801f2",
- "attributes" : { }
- }, {
- "id" : "de86f028-34c5-422e-a8a0-0508bd5071ed",
- "name" : "view-consent",
- "description" : "${role_view-consent}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "c1602103-6c60-4e27-a7ed-c2c21d7801f2",
- "attributes" : { }
- }, {
- "id" : "c5b77ca2-7619-4ad9-994d-0bd6ba8e8747",
- "name" : "manage-account",
- "description" : "${role_manage-account}",
- "composite" : true,
- "composites" : {
- "client" : {
- "account" : [ "manage-account-links" ]
- }
- },
- "clientRole" : true,
- "containerId" : "c1602103-6c60-4e27-a7ed-c2c21d7801f2",
- "attributes" : { }
- }, {
- "id" : "fd21c288-87c3-414c-9152-ea3f5a23b2ca",
- "name" : "manage-consent",
- "description" : "${role_manage-consent}",
- "composite" : true,
- "composites" : {
- "client" : {
- "account" : [ "view-consent" ]
- }
- },
- "clientRole" : true,
- "containerId" : "c1602103-6c60-4e27-a7ed-c2c21d7801f2",
- "attributes" : { }
- }, {
- "id" : "ea81e9c3-3f7b-4cda-901b-4bbe6a30e5e7",
- "name" : "delete-account",
- "description" : "${role_delete-account}",
- "composite" : false,
- "clientRole" : true,
- "containerId" : "c1602103-6c60-4e27-a7ed-c2c21d7801f2",
- "attributes" : { }
- } ]
- }
- },
- "groups" : [ ],
- "defaultRole" : {
- "id" : "b585e111-f934-43b7-b9c2-cbad0c7dc08a",
- "name" : "default-roles-10000000",
- "description" : "${role_default-roles}",
- "composite" : true,
- "clientRole" : false,
- "containerId" : "afc8036c-62c3-462e-ae10-e1727c4bd8f7"
- },
- "requiredCredentials" : [ "password" ],
- "otpPolicyType" : "totp",
- "otpPolicyAlgorithm" : "HmacSHA1",
- "otpPolicyInitialCounter" : 0,
- "otpPolicyDigits" : 6,
- "otpPolicyLookAheadWindow" : 1,
- "otpPolicyPeriod" : 30,
- "otpPolicyCodeReusable" : false,
- "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ],
- "localizationTexts" : { },
- "webAuthnPolicyRpEntityName" : "keycloak",
- "webAuthnPolicySignatureAlgorithms" : [ "ES256" ],
- "webAuthnPolicyRpId" : "",
- "webAuthnPolicyAttestationConveyancePreference" : "not specified",
- "webAuthnPolicyAuthenticatorAttachment" : "not specified",
- "webAuthnPolicyRequireResidentKey" : "not specified",
- "webAuthnPolicyUserVerificationRequirement" : "not specified",
- "webAuthnPolicyCreateTimeout" : 0,
- "webAuthnPolicyAvoidSameAuthenticatorRegister" : false,
- "webAuthnPolicyAcceptableAaguids" : [ ],
- "webAuthnPolicyExtraOrigins" : [ ],
- "webAuthnPolicyPasswordlessRpEntityName" : "keycloak",
- "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ],
- "webAuthnPolicyPasswordlessRpId" : "",
- "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified",
- "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified",
- "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified",
- "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified",
- "webAuthnPolicyPasswordlessCreateTimeout" : 0,
- "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false,
- "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ],
- "webAuthnPolicyPasswordlessExtraOrigins" : [ ],
- "users" : [ {
- "id" : "33865abf-b336-4f77-b370-c2aaadcefaa8",
- "username" : "default-admin",
- "firstName" : "admin",
- "lastName" : "admin",
- "email" : "default-admin@default",
- "emailVerified" : true,
- "createdTimestamp" : 1741788577569,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ {
- "id" : "21ba2cc3-1794-4f87-8df4-a7350cc570ca",
- "type" : "password",
- "userLabel" : "My password",
- "createdDate" : 1741788838245,
- "secretData" : "{\"value\":\"qQ+RHT09vJb1Mv4snElCbOh67CM7cO8r2oFX5UtZunk33EG/uplFAOTIeklRMU5HydfeL1u8gisa9ui+8e2A2g==\",\"salt\":\"tbme6ZolnVOjWqLtWTmzSA==\",\"additionalParameters\":{}}",
- "credentialData" : "{\"hashIterations\":210000,\"algorithm\":\"pbkdf2-sha512\",\"additionalParameters\":{}}"
- } ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "realmRoles" : [ "default-roles-10000000" ],
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "9a3569d7-7cd6-43d2-89ab-48c3040b8a07",
- "username" : "dimuthuw@gatech.edu",
- "firstName" : "Dimuthu",
- "lastName" : "Wannipurage",
- "email" : "dimuthuw@gatech.edu",
- "emailVerified" : true,
- "createdTimestamp" : 1741869545145,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverE/users/194529",
- "userName" : "dimuthuw@gatech.edu"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "23ea1ee4-22f8-4804-858b-ad9b69f866a3",
- "username" : "dwannipu@iu.edu",
- "firstName" : "Dimuthu",
- "lastName" : "Wannipurage",
- "email" : "dwannipu@iu.edu",
- "emailVerified" : true,
- "createdTimestamp" : 1741871229151,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverA/users/287161",
- "userName" : "dwannipu@iu.edu"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "3e7d3897-5dbc-486f-b5c6-00135e8fb0a5",
- "username" : "eroma.abey@gmail.com",
- "firstName" : "Eroma",
- "lastName" : "Abey",
- "email" : "eroma.abey@gmail.com",
- "emailVerified" : true,
- "createdTimestamp" : 1741789737864,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverE/users/8674",
- "userName" : "eroma.abey@gmail.com"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "b36672e8-ccb2-40ed-8522-9eec5a2d8662",
- "username" : "eroma@gatech.edu",
- "firstName" : "Eroma",
- "lastName" : "Abeysinghe",
- "email" : "eroma@gatech.edu",
- "emailVerified" : true,
- "createdTimestamp" : 1741787443518,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverF/users/141980",
- "userName" : "eroma@gatech.edu"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "ac7b156b-564c-4b44-80b4-409114e88510",
- "username" : "ganning.xu@gatech.edu",
- "firstName" : "Ganning",
- "lastName" : "Xu",
- "email" : "ganning.xu@gatech.edu",
- "emailVerified" : true,
- "createdTimestamp" : 1741888661947,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverE/users/152691",
- "userName" : "ganning.xu@gatech.edu"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "455c7d22-6cd2-4b9b-ac91-949307380a5c",
- "username" : "isralewi@illinois.edu",
- "firstName" : "Barry",
- "lastName" : "Isralewitz",
- "email" : "isralewi@illinois.edu",
- "emailVerified" : true,
- "createdTimestamp" : 1741737143337,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverA/users/72206",
- "userName" : "isralewi@illinois.edu"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "7d66792c-2027-428b-bede-2efb7046d0ae",
- "username" : "lahiruj.jayathilake@gmail.com",
- "firstName" : "Lahiru",
- "lastName" : "Jayathilake",
- "email" : "lahiruj.jayathilake@gmail.com",
- "emailVerified" : true,
- "createdTimestamp" : 1741788322225,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverE/users/59623",
- "userName" : "lahiruj.jayathilake@gmail.com"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "12ae3225-636e-4545-952b-115b22596c98",
- "username" : "ljayathilake3@gatech.edu",
- "firstName" : "Lahiru",
- "lastName" : "Jayathilake",
- "email" : "ljayathilake3@gatech.edu",
- "emailVerified" : true,
- "createdTimestamp" : 1741725237000,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverE/users/173020",
- "userName" : "ljayathilake3@gatech.edu"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "clientConsents" : [ {
- "clientId" : "cybershuttle-agent",
- "grantedClientScopes" : [ "roles", "email", "profile" ],
- "createdDate" : 1741726104600,
- "lastUpdatedDate" : 1741726104611
- } ],
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "dfe38185-9db7-4820-97c3-d0712ff35159",
- "username" : "pamidigs@iu.edu",
- "firstName" : "Sudhakar",
- "lastName" : "Pamidighantam",
- "email" : "pamidigs@iu.edu",
- "emailVerified" : true,
- "createdTimestamp" : 1741728655391,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverA/users/20201",
- "userName" : "pamidigs@iu.edu"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "clientConsents" : [ {
- "clientId" : "cybershuttle-agent",
- "grantedClientScopes" : [ "roles", "email", "profile" ],
- "createdDate" : 1741729390542,
- "lastUpdatedDate" : 1741729390548
- } ],
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "e3fb714e-9ed6-4d26-a403-781e71bbb025",
- "username" : "service-account-cs-jupyterlab",
- "emailVerified" : false,
- "createdTimestamp" : 1733075288051,
- "enabled" : true,
- "totp" : false,
- "serviceAccountClientId" : "cs-jupyterlab",
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "cs-jupyterlab" : [ "uma_protection" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "e9d5a7b9-c093-4916-a331-12fbc9101c70",
- "username" : "service-account-pga",
- "emailVerified" : false,
- "createdTimestamp" : 1726317784923,
- "enabled" : true,
- "totp" : false,
- "serviceAccountClientId" : "pga",
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "realm-management" : [ "manage-users" ],
- "pga" : [ "uma_protection" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "e68c29c2-7f0f-471c-a3e1-dfe1033a8889",
- "username" : "smarru@gatech.edu",
- "firstName" : "Suresh",
- "lastName" : "Marru",
- "email" : "smarru@gatech.edu",
- "emailVerified" : true,
- "createdTimestamp" : 1741871207623,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverE/users/121218",
- "userName" : "smarru@gatech.edu"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- }, {
- "id" : "433fd8f3-db52-4bb5-8d7d-2d1b74dea467",
- "username" : "tenbarge@princeton.edu",
- "firstName" : "Jason",
- "lastName" : "TenBarge",
- "email" : "tenbarge@princeton.edu",
- "emailVerified" : true,
- "createdTimestamp" : 1741791838996,
- "enabled" : true,
- "totp" : false,
- "credentials" : [ ],
- "disableableCredentialTypes" : [ ],
- "requiredActions" : [ ],
- "federatedIdentities" : [ {
- "identityProvider" : "oidc",
- "userId" : "http://cilogon.org/serverA/users/6168781",
- "userName" : "tenbarge@princeton.edu"
- } ],
- "realmRoles" : [ "default-roles-10000000" ],
- "clientRoles" : {
- "broker" : [ "read-token" ]
- },
- "notBefore" : 0,
- "groups" : [ ]
- } ],
- "scopeMappings" : [ {
- "clientScope" : "offline_access",
- "roles" : [ "offline_access" ]
- } ],
- "clientScopeMappings" : {
- "account" : [ {
- "client" : "account-console",
- "roles" : [ "manage-account", "view-groups" ]
- } ]
- },
- "clients" : [ {
- "id" : "c1602103-6c60-4e27-a7ed-c2c21d7801f2",
- "clientId" : "account",
- "name" : "${client_account}",
- "rootUrl" : "${authBaseUrl}",
- "baseUrl" : "/realms/default/account/",
- "surrogateAuthRequired" : false,
- "enabled" : true,
- "alwaysDisplayInConsole" : false,
- "clientAuthenticatorType" : "client-secret",
- "redirectUris" : [ "/realms/default/account/*" ],
- "webOrigins" : [ ],
- "notBefore" : 0,
- "bearerOnly" : false,
- "consentRequired" : false,
- "standardFlowEnabled" : true,
- "implicitFlowEnabled" : false,
- "directAccessGrantsEnabled" : false,
- "serviceAccountsEnabled" : false,
- "publicClient" : true,
- "frontchannelLogout" : false,
- "protocol" : "openid-connect",
- "attributes" : {
- "post.logout.redirect.uris" : "+"
- },
- "authenticationFlowBindingOverrides" : { },
- "fullScopeAllowed" : false,
- "nodeReRegistrationTimeout" : 0,
- "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
- "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
- }, {
- "id" : "def3d25e-919c-4b1b-b3fd-0a976a33e4b6",
- "clientId" : "account-console",
- "name" : "${client_account-console}",
- "rootUrl" : "${authBaseUrl}",
- "baseUrl" : "/realms/default/account/",
- "surrogateAuthRequired" : false,
- "enabled" : true,
- "alwaysDisplayInConsole" : false,
- "clientAuthenticatorType" : "client-secret",
- "redirectUris" : [ "/realms/default/account/*" ],
- "webOrigins" : [ ],
- "notBefore" : 0,
- "bearerOnly" : false,
- "consentRequired" : false,
- "standardFlowEnabled" : true,
- "implicitFlowEnabled" : false,
- "directAccessGrantsEnabled" : false,
- "serviceAccountsEnabled" : false,
- "publicClient" : true,
- "frontchannelLogout" : false,
- "protocol" : "openid-connect",
- "attributes" : {
- "post.logout.redirect.uris" : "+",
- "pkce.code.challenge.method" : "S256"
- },
- "authenticationFlowBindingOverrides" : { },
- "fullScopeAllowed" : false,
- "nodeReRegistrationTimeout" : 0,
- "protocolMappers" : [ {
- "id" : "a2d8962f-2ac2-4fa5-b42e-f37990a4d098",
- "name" : "audience resolve",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-audience-resolve-mapper",
- "consentRequired" : false,
- "config" : { }
- } ],
- "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
- "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
- }, {
- "id" : "4a5fd0fd-6842-4528-8fea-ca0727dce936",
- "clientId" : "admin-cli",
- "name" : "${client_admin-cli}",
- "surrogateAuthRequired" : false,
- "enabled" : true,
- "alwaysDisplayInConsole" : false,
- "clientAuthenticatorType" : "client-secret",
- "redirectUris" : [ ],
- "webOrigins" : [ ],
- "notBefore" : 0,
- "bearerOnly" : false,
- "consentRequired" : false,
- "standardFlowEnabled" : false,
- "implicitFlowEnabled" : false,
- "directAccessGrantsEnabled" : true,
- "serviceAccountsEnabled" : false,
- "publicClient" : true,
- "frontchannelLogout" : false,
- "protocol" : "openid-connect",
- "attributes" : {
- "post.logout.redirect.uris" : "+"
- },
- "authenticationFlowBindingOverrides" : { },
- "fullScopeAllowed" : false,
- "nodeReRegistrationTimeout" : 0,
- "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
- "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
- }, {
- "id" : "af99dd7f-6d3a-4fec-93c0-8deab50edf0e",
- "clientId" : "broker",
- "name" : "${client_broker}",
- "surrogateAuthRequired" : false,
- "enabled" : true,
- "alwaysDisplayInConsole" : false,
- "clientAuthenticatorType" : "client-secret",
- "redirectUris" : [ ],
- "webOrigins" : [ ],
- "notBefore" : 0,
- "bearerOnly" : true,
- "consentRequired" : false,
- "standardFlowEnabled" : true,
- "implicitFlowEnabled" : false,
- "directAccessGrantsEnabled" : false,
- "serviceAccountsEnabled" : false,
- "publicClient" : false,
- "frontchannelLogout" : false,
- "protocol" : "openid-connect",
- "attributes" : {
- "post.logout.redirect.uris" : "+"
- },
- "authenticationFlowBindingOverrides" : { },
- "fullScopeAllowed" : false,
- "nodeReRegistrationTimeout" : 0,
- "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
- "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
- }, {
- "id" : "ac2469cf-1760-4d47-9079-7306fee96ae4",
- "clientId" : "cs-jupyterlab",
- "name" : "JupyterLab",
- "description" : "",
- "rootUrl" : "",
- "adminUrl" : "",
- "baseUrl" : "http://localhost:8080/",
- "surrogateAuthRequired" : false,
- "enabled" : true,
- "alwaysDisplayInConsole" : false,
- "clientAuthenticatorType" : "client-secret",
- "secret" : "DxeMtfiWU1qkDEmaGHf13RDahCujzhy1",
- "redirectUris" : [ "", "/*", "http://airavata.host:20000/hub/oauth_callback" ],
- "webOrigins" : [ "/*" ],
- "notBefore" : 0,
- "bearerOnly" : false,
- "consentRequired" : false,
- "standardFlowEnabled" : true,
- "implicitFlowEnabled" : false,
- "directAccessGrantsEnabled" : true,
- "serviceAccountsEnabled" : true,
- "authorizationServicesEnabled" : true,
- "publicClient" : false,
- "frontchannelLogout" : false,
- "protocol" : "openid-connect",
- "attributes" : {
- "client.secret.creation.time" : "1741725835",
- "post.logout.redirect.uris" : "+",
- "oauth2.device.authorization.grant.enabled" : "false",
- "backchannel.logout.revoke.offline.tokens" : "false",
- "use.refresh.tokens" : "true",
- "oidc.ciba.grant.enabled" : "false",
- "client.use.lightweight.access.token.enabled" : "false",
- "backchannel.logout.session.required" : "true",
- "client_credentials.use_refresh_token" : "false",
- "acr.loa.map" : "{}",
- "require.pushed.authorization.requests" : "false",
- "tls.client.certificate.bound.access.tokens" : "false",
- "display.on.consent.screen" : "false",
- "token.response.type.bearer.lower-case" : "false"
- },
- "authenticationFlowBindingOverrides" : { },
- "fullScopeAllowed" : true,
- "nodeReRegistrationTimeout" : -1,
- "protocolMappers" : [ {
- "id" : "8674e3d9-f43c-4f89-a290-2666ed0567c1",
- "name" : "Client IP Address",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usersessionmodel-note-mapper",
- "consentRequired" : false,
- "config" : {
- "user.session.note" : "clientAddress",
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "clientAddress",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "b0fdec17-a04b-48c7-870b-ba71c0bb6680",
- "name" : "Client ID",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usersessionmodel-note-mapper",
- "consentRequired" : false,
- "config" : {
- "user.session.note" : "client_id",
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "client_id",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "f8031842-1fd7-41ac-b5e8-b22330cdcdda",
- "name" : "Client Host",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usersessionmodel-note-mapper",
- "consentRequired" : false,
- "config" : {
- "user.session.note" : "clientHost",
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "clientHost",
- "jsonType.label" : "String"
- }
- } ],
- "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
- "optionalClientScopes" : [ "address", "phone", "offline_access", "preferred_username", "microprofile-jwt" ],
- "authorizationSettings" : {
- "allowRemoteResourceManagement" : true,
- "policyEnforcementMode" : "ENFORCING",
- "resources" : [ ],
- "policies" : [ ],
- "scopes" : [ ],
- "decisionStrategy" : "UNANIMOUS"
- }
- }, {
- "id" : "f4c30fed-2f14-471d-a922-b5ad262273f2",
- "clientId" : "cybershuttle-agent",
- "name" : "CyberShuttle Agent",
- "description" : "",
- "rootUrl" : "",
- "adminUrl" : "",
- "baseUrl" : "http://airavata.host:8009/",
- "surrogateAuthRequired" : false,
- "enabled" : true,
- "alwaysDisplayInConsole" : false,
- "clientAuthenticatorType" : "client-secret",
- "redirectUris" : [ "/*" ],
- "webOrigins" : [ "/*" ],
- "notBefore" : 0,
- "bearerOnly" : false,
- "consentRequired" : false,
- "standardFlowEnabled" : false,
- "implicitFlowEnabled" : false,
- "directAccessGrantsEnabled" : false,
- "serviceAccountsEnabled" : false,
- "publicClient" : true,
- "frontchannelLogout" : true,
- "protocol" : "openid-connect",
- "attributes" : {
- "post.logout.redirect.uris" : "+",
- "oauth2.device.authorization.grant.enabled" : "true",
- "backchannel.logout.revoke.offline.tokens" : "false",
- "use.refresh.tokens" : "true",
- "oidc.ciba.grant.enabled" : "false",
- "client.use.lightweight.access.token.enabled" : "false",
- "backchannel.logout.session.required" : "true",
- "client_credentials.use_refresh_token" : "false",
- "acr.loa.map" : "{}",
- "require.pushed.authorization.requests" : "false",
- "tls.client.certificate.bound.access.tokens" : "false",
- "display.on.consent.screen" : "false",
- "token.response.type.bearer.lower-case" : "false"
- },
- "authenticationFlowBindingOverrides" : { },
- "fullScopeAllowed" : true,
- "nodeReRegistrationTimeout" : -1,
- "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
- "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
- }, {
- "id" : "5e2398e0-3498-4da3-9262-4f2dcc7448fa",
- "clientId" : "pga",
- "name" : "Cybeshuttle Client",
- "description" : "Client For Cybershuttle Services",
- "rootUrl" : "",
- "adminUrl" : "",
- "baseUrl" : "",
- "surrogateAuthRequired" : false,
- "enabled" : true,
- "alwaysDisplayInConsole" : false,
- "clientAuthenticatorType" : "client-secret",
- "secret" : "m36BXQIxX3j3VILadeHMK5IvbOeRlCCc",
- "redirectUris" : [ "http://airavata.host:8008/callback*", "https://airavata.host:8009/auth/callback*" ],
- "webOrigins" : [ "*" ],
- "notBefore" : 0,
- "bearerOnly" : false,
- "consentRequired" : false,
- "standardFlowEnabled" : true,
- "implicitFlowEnabled" : false,
- "directAccessGrantsEnabled" : true,
- "serviceAccountsEnabled" : true,
- "authorizationServicesEnabled" : true,
- "publicClient" : false,
- "frontchannelLogout" : true,
- "protocol" : "openid-connect",
- "attributes" : {
- "oidc.ciba.grant.enabled" : "false",
- "client.secret.creation.time" : "1741724922",
- "backchannel.logout.session.required" : "true",
- "frontchannel.logout.url" : "http://airavata.host:8009/",
- "post.logout.redirect.uris" : "+##http://airavata.host:8009/",
- "display.on.consent.screen" : "false",
- "oauth2.device.authorization.grant.enabled" : "true",
- "backchannel.logout.revoke.offline.tokens" : "false"
- },
- "authenticationFlowBindingOverrides" : { },
- "fullScopeAllowed" : true,
- "nodeReRegistrationTimeout" : -1,
- "protocolMappers" : [ {
- "id" : "f15a7de0-0c1e-40d8-bd05-c1aaf0deb3e1",
- "name" : "Client IP Address",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usersessionmodel-note-mapper",
- "consentRequired" : false,
- "config" : {
- "user.session.note" : "clientAddress",
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "clientAddress",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "a0956d0b-e5c4-4d9a-aebf-89efa6881438",
- "name" : "Client ID",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usersessionmodel-note-mapper",
- "consentRequired" : false,
- "config" : {
- "user.session.note" : "client_id",
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "client_id",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "6863299e-7d4f-43f4-8d0e-fc8cd4a8ceac",
- "name" : "Client Host",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usersessionmodel-note-mapper",
- "consentRequired" : false,
- "config" : {
- "user.session.note" : "clientHost",
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "clientHost",
- "jsonType.label" : "String"
- }
- } ],
- "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
- "optionalClientScopes" : [ "address", "phone", "offline_access", "preferred_username", "microprofile-jwt" ],
- "authorizationSettings" : {
- "allowRemoteResourceManagement" : true,
- "policyEnforcementMode" : "ENFORCING",
- "resources" : [ ],
- "policies" : [ ],
- "scopes" : [ ],
- "decisionStrategy" : "UNANIMOUS"
- }
- }, {
- "id" : "be9976ab-8e62-4d5a-8176-b6efaba2e1bf",
- "clientId" : "realm-management",
- "name" : "${client_realm-management}",
- "surrogateAuthRequired" : false,
- "enabled" : true,
- "alwaysDisplayInConsole" : false,
- "clientAuthenticatorType" : "client-secret",
- "redirectUris" : [ ],
- "webOrigins" : [ ],
- "notBefore" : 0,
- "bearerOnly" : true,
- "consentRequired" : false,
- "standardFlowEnabled" : true,
- "implicitFlowEnabled" : false,
- "directAccessGrantsEnabled" : false,
- "serviceAccountsEnabled" : false,
- "publicClient" : false,
- "frontchannelLogout" : false,
- "protocol" : "openid-connect",
- "attributes" : {
- "post.logout.redirect.uris" : "+"
- },
- "authenticationFlowBindingOverrides" : { },
- "fullScopeAllowed" : false,
- "nodeReRegistrationTimeout" : 0,
- "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
- "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
- }, {
- "id" : "cedda658-7913-4763-89b8-71d1d1794c1c",
- "clientId" : "security-admin-console",
- "name" : "${client_security-admin-console}",
- "rootUrl" : "${authAdminUrl}",
- "baseUrl" : "/admin/default/console/",
- "surrogateAuthRequired" : false,
- "enabled" : true,
- "alwaysDisplayInConsole" : false,
- "clientAuthenticatorType" : "client-secret",
- "redirectUris" : [ "/admin/default/console/*" ],
- "webOrigins" : [ "+" ],
- "notBefore" : 0,
- "bearerOnly" : false,
- "consentRequired" : false,
- "standardFlowEnabled" : true,
- "implicitFlowEnabled" : false,
- "directAccessGrantsEnabled" : false,
- "serviceAccountsEnabled" : false,
- "publicClient" : true,
- "frontchannelLogout" : false,
- "protocol" : "openid-connect",
- "attributes" : {
- "post.logout.redirect.uris" : "+",
- "pkce.code.challenge.method" : "S256"
- },
- "authenticationFlowBindingOverrides" : { },
- "fullScopeAllowed" : false,
- "nodeReRegistrationTimeout" : 0,
- "protocolMappers" : [ {
- "id" : "f8a3200c-fed5-4d3e-9fc7-80c23c458056",
- "name" : "locale",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "locale",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "locale",
- "jsonType.label" : "String"
- }
- } ],
- "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
- "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
- } ],
- "clientScopes" : [ {
- "id" : "a8292b23-f927-4a5c-a432-2f0ae8867105",
- "name" : "roles",
- "description" : "OpenID Connect scope for add user roles to the access token",
- "protocol" : "openid-connect",
- "attributes" : {
- "include.in.token.scope" : "false",
- "display.on.consent.screen" : "false",
- "gui.order" : "",
- "consent.screen.text" : "${rolesScopeConsentText}"
- },
- "protocolMappers" : [ {
- "id" : "bfd13362-6c5d-405c-b7c0-bd37063ca9f9",
- "name" : "client roles",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-client-role-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "multivalued" : "true",
- "user.attribute" : "foo",
- "access.token.claim" : "true",
- "claim.name" : "resource_access.${client_id}.roles",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "be26563f-80a7-43d7-bf9f-d71205ec53be",
- "name" : "audience resolve",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-audience-resolve-mapper",
- "consentRequired" : false,
- "config" : {
- "access.token.claim" : "true",
- "introspection.token.claim" : "true"
- }
- }, {
- "id" : "9565b5e4-dfbb-43bc-aa8d-3e845c188669",
- "name" : "realm roles",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-realm-role-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "multivalued" : "true",
- "user.attribute" : "foo",
- "access.token.claim" : "true",
- "claim.name" : "realm_access.roles",
- "jsonType.label" : "String"
- }
- } ]
- }, {
- "id" : "34779656-fdf3-41c5-a963-d1e2b533b8f0",
- "name" : "phone",
- "description" : "OpenID Connect built-in scope: phone",
- "protocol" : "openid-connect",
- "attributes" : {
- "include.in.token.scope" : "true",
- "display.on.consent.screen" : "true",
- "consent.screen.text" : "${phoneScopeConsentText}"
- },
- "protocolMappers" : [ {
- "id" : "6d5c53bd-a91a-4066-baba-52de991b5d53",
- "name" : "phone number",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "phoneNumber",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "phone_number",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "a4f0d91a-e59e-4794-b71c-726413ba5be8",
- "name" : "phone number verified",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "phoneNumberVerified",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "phone_number_verified",
- "jsonType.label" : "boolean"
- }
- } ]
- }, {
- "id" : "8240c399-c132-49a0-8e41-48266e89b2ce",
- "name" : "address",
- "description" : "OpenID Connect built-in scope: address",
- "protocol" : "openid-connect",
- "attributes" : {
- "include.in.token.scope" : "true",
- "display.on.consent.screen" : "true",
- "consent.screen.text" : "${addressScopeConsentText}"
- },
- "protocolMappers" : [ {
- "id" : "cbd43ec1-4467-4148-a435-081cd1c0d161",
- "name" : "address",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-address-mapper",
- "consentRequired" : false,
- "config" : {
- "user.attribute.formatted" : "formatted",
- "user.attribute.country" : "country",
- "introspection.token.claim" : "true",
- "user.attribute.postal_code" : "postal_code",
- "userinfo.token.claim" : "true",
- "user.attribute.street" : "street",
- "id.token.claim" : "true",
- "user.attribute.region" : "region",
- "access.token.claim" : "true",
- "user.attribute.locality" : "locality"
- }
- } ]
- }, {
- "id" : "7d17e91f-6691-4f1d-9f6c-7a6a7ae0b5ae",
- "name" : "email",
- "description" : "OpenID Connect built-in scope: email",
- "protocol" : "openid-connect",
- "attributes" : {
- "include.in.token.scope" : "true",
- "display.on.consent.screen" : "false",
- "gui.order" : "",
- "consent.screen.text" : "${emailScopeConsentText}"
- },
- "protocolMappers" : [ {
- "id" : "c0d4e90d-2c57-4ed5-9e82-01a6d1d16cf0",
- "name" : "email",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "email",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "email",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "c7e78134-c7dd-45a9-a921-cc5dbdc9fc68",
- "name" : "email verified",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-property-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "emailVerified",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "email_verified",
- "jsonType.label" : "boolean"
- }
- } ]
- }, {
- "id" : "c0509e2e-01ae-49d6-8d24-0e86628a85aa",
- "name" : "preferred_username",
- "description" : "preferred_username",
- "protocol" : "openid-connect",
- "attributes" : {
- "include.in.token.scope" : "true",
- "display.on.consent.screen" : "true",
- "gui.order" : "",
- "consent.screen.text" : ""
- },
- "protocolMappers" : [ {
- "id" : "e56aa26c-4614-43bb-a96b-56c1ad959e45",
- "name" : "email",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-property-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "email",
- "id.token.claim" : "true",
- "lightweight.claim" : "false",
- "access.token.claim" : "true",
- "claim.name" : "email",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "b50ae18b-52e8-44f6-ad4d-ad596de89cfd",
- "name" : "username",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-property-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "username",
- "id.token.claim" : "true",
- "lightweight.claim" : "false",
- "access.token.claim" : "true",
- "claim.name" : "preferred_username",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "0a93294b-2a7c-4098-af47-4faaa535c8d4",
- "name" : "ClientId",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usersessionmodel-note-mapper",
- "consentRequired" : false,
- "config" : {
- "user.session.note" : "clientId",
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "id.token.claim" : "true",
- "lightweight.claim" : "false",
- "access.token.claim" : "true",
- "claim.name" : "clientId",
- "jsonType.label" : "String",
- "access.tokenResponse.claim" : "false"
- }
- }, {
- "id" : "37b8b958-2396-4e2c-871c-f56f6ad393aa",
- "name" : "family name",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-property-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "lastName",
- "id.token.claim" : "true",
- "lightweight.claim" : "false",
- "access.token.claim" : "true",
- "claim.name" : "family_name",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "d779bc1f-5c49-46c6-ae84-1d9e419984ba",
- "name" : "given name",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-property-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "firstName",
- "id.token.claim" : "true",
- "lightweight.claim" : "false",
- "access.token.claim" : "true",
- "claim.name" : "given_name",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "75c6a588-0993-446e-90e0-52046505959e",
- "name" : "full name",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-full-name-mapper",
- "consentRequired" : false,
- "config" : {
- "id.token.claim" : "true",
- "lightweight.claim" : "false",
- "access.token.claim" : "true",
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "false"
- }
- } ]
- }, {
- "id" : "33b54357-7cdd-494c-9806-6e6d59f6af69",
- "name" : "role_list",
- "description" : "SAML role list",
- "protocol" : "saml",
- "attributes" : {
- "consent.screen.text" : "${samlRoleListScopeConsentText}",
- "display.on.consent.screen" : "true"
- },
- "protocolMappers" : [ {
- "id" : "c56cb073-719d-4f74-8a23-b2105804ee80",
- "name" : "role list",
- "protocol" : "saml",
- "protocolMapper" : "saml-role-list-mapper",
- "consentRequired" : false,
- "config" : {
- "single" : "false",
- "attribute.nameformat" : "Basic",
- "attribute.name" : "Role"
- }
- } ]
- }, {
- "id" : "658e931d-3b55-464e-b199-48e9b43f9c3f",
- "name" : "profile",
- "description" : "OpenID Connect built-in scope: profile",
- "protocol" : "openid-connect",
- "attributes" : {
- "include.in.token.scope" : "true",
- "display.on.consent.screen" : "false",
- "gui.order" : "",
- "consent.screen.text" : "${profileScopeConsentText}"
- },
- "protocolMappers" : [ {
- "id" : "a352424d-d48b-4e7e-9394-c769f9542605",
- "name" : "website",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "website",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "website",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "3710a2dd-969f-4562-89af-55e31e30f565",
- "name" : "middle name",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "middleName",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "middle_name",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "862fa550-a9df-40e5-8fa3-473c45de5e59",
- "name" : "zoneinfo",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "zoneinfo",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "zoneinfo",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "0ae8854b-1589-401c-b0e0-24e423d8e723",
- "name" : "given name",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "firstName",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "given_name",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "a72f9f11-04bb-4905-81fe-7b23b7446e20",
- "name" : "gender",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "gender",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "gender",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "eaded814-dc28-450e-95ca-fbd2712e6a40",
- "name" : "profile",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "profile",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "profile",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "652dd31b-d4d8-49e9-8f53-125404b2a1f1",
- "name" : "locale",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "locale",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "locale",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "b20740b4-9fea-4035-b3af-cf0a40852b2f",
- "name" : "birthdate",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "birthdate",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "birthdate",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "7d36aacc-aa67-4ccb-98b0-b02a111c7dea",
- "name" : "username",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "username",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "preferred_username",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "4ed1750d-d48a-4005-8398-2c34f0c6548e",
- "name" : "full name",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-full-name-mapper",
- "consentRequired" : false,
- "config" : {
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true"
- }
- }, {
- "id" : "4b111e20-11ee-4429-be4d-93414b08e506",
- "name" : "updated at",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "updatedAt",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "updated_at",
- "jsonType.label" : "long"
- }
- }, {
- "id" : "7a7e5200-ae1c-44dc-a5a7-5546391929b5",
- "name" : "family name",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "lastName",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "family_name",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "d353e69e-7d1e-4c41-9fdd-f76a59dacf2b",
- "name" : "picture",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "picture",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "picture",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "a0e4a0a3-1428-46a5-a2a8-66480b1ecf9f",
- "name" : "nickname",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "nickname",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "nickname",
- "jsonType.label" : "String"
- }
- } ]
- }, {
- "id" : "af72f98c-7086-4e99-97ad-2cdfe9f46cd4",
- "name" : "offline_access",
- "description" : "OpenID Connect built-in scope: offline_access",
- "protocol" : "openid-connect",
- "attributes" : {
- "consent.screen.text" : "${offlineAccessScopeConsentText}",
- "display.on.consent.screen" : "true"
- }
- }, {
- "id" : "c98af4d7-e60f-469d-b76b-c8673b3d641c",
- "name" : "acr",
- "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token",
- "protocol" : "openid-connect",
- "attributes" : {
- "include.in.token.scope" : "false",
- "display.on.consent.screen" : "false"
- },
- "protocolMappers" : [ {
- "id" : "d000afcc-348f-414e-8e18-fbf9dd73bec0",
- "name" : "acr loa level",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-acr-mapper",
- "consentRequired" : false,
- "config" : {
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true"
- }
- } ]
- }, {
- "id" : "9bc461ac-7b67-48ae-af92-03b51bdd2336",
- "name" : "microprofile-jwt",
- "description" : "Microprofile - JWT built-in scope",
- "protocol" : "openid-connect",
- "attributes" : {
- "include.in.token.scope" : "true",
- "display.on.consent.screen" : "false"
- },
- "protocolMappers" : [ {
- "id" : "075ba449-99e1-49c5-8642-755d9fd1ca08",
- "name" : "upn",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-attribute-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "username",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "upn",
- "jsonType.label" : "String"
- }
- }, {
- "id" : "4d1db13c-a944-4205-95fc-6b9c184e3cc4",
- "name" : "groups",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-usermodel-realm-role-mapper",
- "consentRequired" : false,
- "config" : {
- "introspection.token.claim" : "true",
- "multivalued" : "true",
- "userinfo.token.claim" : "true",
- "user.attribute" : "foo",
- "id.token.claim" : "true",
- "access.token.claim" : "true",
- "claim.name" : "groups",
- "jsonType.label" : "String"
- }
- } ]
- }, {
- "id" : "162a761a-4a0b-4c30-a388-1ee5df06eb8e",
- "name" : "web-origins",
- "description" : "OpenID Connect scope for add allowed web origins to the access token",
- "protocol" : "openid-connect",
- "attributes" : {
- "include.in.token.scope" : "false",
- "display.on.consent.screen" : "false",
- "consent.screen.text" : ""
- },
- "protocolMappers" : [ {
- "id" : "2ecc5181-9142-45b5-b11a-4d7936f9e38e",
- "name" : "allowed web origins",
- "protocol" : "openid-connect",
- "protocolMapper" : "oidc-allowed-origins-mapper",
- "consentRequired" : false,
- "config" : {
- "access.token.claim" : "true",
- "introspection.token.claim" : "true"
- }
- } ]
- } ],
- "defaultDefaultClientScopes" : [ "web-origins", "role_list", "profile", "email", "roles", "acr" ],
- "defaultOptionalClientScopes" : [ "phone", "address", "microprofile-jwt", "offline_access", "preferred_username" ],
- "browserSecurityHeaders" : {
- "contentSecurityPolicyReportOnly" : "",
- "xContentTypeOptions" : "nosniff",
- "referrerPolicy" : "no-referrer",
- "xRobotsTag" : "none",
- "xFrameOptions" : "SAMEORIGIN",
- "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';",
- "xXSSProtection" : "1; mode=block",
- "strictTransportSecurity" : "max-age=31536000; includeSubDomains"
- },
- "smtpServer" : { },
- "loginTheme" : "custom-theme",
- "accountTheme" : "",
- "adminTheme" : "",
- "emailTheme" : "",
- "eventsEnabled" : false,
- "eventsListeners" : [ "jboss-logging" ],
- "enabledEventTypes" : [ ],
- "adminEventsEnabled" : false,
- "adminEventsDetailsEnabled" : false,
- "identityProviders" : [ {
- "alias" : "oidc",
- "displayName" : "CILogon",
- "internalId" : "f0427047-bcb0-414d-9e9a-dc97b7cddefa",
- "providerId" : "oidc",
- "enabled" : true,
- "updateProfileFirstLoginMode" : "on",
- "trustEmail" : true,
- "storeToken" : true,
- "addReadTokenRoleOnCreate" : true,
- "authenticateByDefault" : false,
- "linkOnly" : false,
- "firstBrokerLoginFlowAlias" : "first broker login",
- "config" : {
- "acceptsPromptNoneForwardFromClient" : "false",
- "tokenUrl" : "https://cilogon.org/oauth2/token",
- "isAccessTokenJWT" : "false",
- "filteredByClaim" : "false",
- "backchannelSupported" : "false",
- "issuer" : "https://cilogon.org",
- "loginHint" : "false",
- "clientAuthMethod" : "client_secret_post",
- "syncMode" : "IMPORT",
- "clientSecret" : "xNgIF3suP5NFRiBnyDdBnJRP5XG3D5fgCfg5jxdkMlBUOe735UDXayX3OpOh2ra-zXY1VZKV4uou2qYw4WvAOg",
- "allowedClockSkew" : "0",
- "defaultScope" : "openid profile email org.cilogon.userinfo",
- "guiOrder" : "1",
- "hideOnLoginPage" : "false",
- "userInfoUrl" : "https://cilogon.org/oauth2/userinfo",
- "validateSignature" : "false",
- "clientId" : "cilogon:/client_id/392446d1fe4981d3eab8adb6da2a1952",
- "uiLocales" : "false",
- "disableNonce" : "false",
- "sendClientIdOnLogout" : "false",
- "pkceEnabled" : "false",
- "forwardParameters" : "kc_idp_hint",
- "authorizationUrl" : "https://cilogon.org/authorize",
- "disableUserInfo" : "false",
- "logoutUrl" : "http://airavata.host:8009/",
- "sendIdTokenOnLogout" : "true",
- "passMaxAge" : "false"
- }
- } ],
- "identityProviderMappers" : [ {
- "id" : "59cabb48-742d-471c-9bb5-8741c98675ad",
- "name" : "family_name",
- "identityProviderAlias" : "oidc",
- "identityProviderMapper" : "oidc-user-attribute-idp-mapper",
- "config" : {
- "syncMode" : "INHERIT",
- "claim" : "family_name",
- "user.attribute" : "lastName"
- }
- }, {
- "id" : "6d5651e9-75cd-4c70-9bbc-6f2acc5496ab",
- "name" : "given_name",
- "identityProviderAlias" : "oidc",
- "identityProviderMapper" : "oidc-user-attribute-idp-mapper",
- "config" : {
- "syncMode" : "INHERIT",
- "claim" : "given_name",
- "user.attribute" : "firstName"
- }
- } ],
- "components" : {
- "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ {
- "id" : "d446bfe5-46d7-4b5a-a569-41268f1f0e87",
- "name" : "Trusted Hosts",
- "providerId" : "trusted-hosts",
- "subType" : "anonymous",
- "subComponents" : { },
- "config" : {
- "host-sending-registration-request-must-match" : [ "true" ],
- "client-uris-must-match" : [ "true" ]
- }
- }, {
- "id" : "d6c586ad-07ab-4a3e-8f61-9ee3d4f7e03f",
- "name" : "Max Clients Limit",
- "providerId" : "max-clients",
- "subType" : "anonymous",
- "subComponents" : { },
- "config" : {
- "max-clients" : [ "200" ]
- }
- }, {
- "id" : "fcf2e8da-427f-4232-8e02-cf08151c0211",
- "name" : "Allowed Protocol Mapper Types",
- "providerId" : "allowed-protocol-mappers",
- "subType" : "authenticated",
- "subComponents" : { },
- "config" : {
- "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper" ]
- }
- }, {
- "id" : "9d32bc1e-fa04-4ab7-8911-cb344ff8b5c8",
- "name" : "Consent Required",
- "providerId" : "consent-required",
- "subType" : "anonymous",
- "subComponents" : { },
- "config" : { }
- }, {
- "id" : "eff493d8-9108-420f-88ca-522e9df73f46",
- "name" : "Allowed Protocol Mapper Types",
- "providerId" : "allowed-protocol-mappers",
- "subType" : "anonymous",
- "subComponents" : { },
- "config" : {
- "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper" ]
- }
- }, {
- "id" : "64e7e77a-3489-45bd-b440-e1c42941b22a",
- "name" : "Allowed Client Scopes",
- "providerId" : "allowed-client-templates",
- "subType" : "anonymous",
- "subComponents" : { },
- "config" : {
- "allow-default-scopes" : [ "true" ]
- }
- }, {
- "id" : "b3315017-dfb7-4e2f-a682-f9278ae9008a",
- "name" : "Allowed Client Scopes",
- "providerId" : "allowed-client-templates",
- "subType" : "authenticated",
- "subComponents" : { },
- "config" : {
- "allow-default-scopes" : [ "true" ]
- }
- }, {
- "id" : "35fa6e56-7042-4df8-bdda-feeeb6b39c45",
- "name" : "Full Scope Disabled",
- "providerId" : "scope",
- "subType" : "anonymous",
- "subComponents" : { },
- "config" : { }
- } ],
- "org.keycloak.keys.KeyProvider" : [ {
- "id" : "33a8177a-0278-4ce3-abee-a4394ec04aed",
- "name" : "rsa-enc-generated",
- "providerId" : "rsa-enc-generated",
- "subComponents" : { },
- "config" : {
- "privateKey" : [ "MIIEowIBAAKCAQEArMNxvbCXCLZBiqnNEcZ+XpGJVvKXnGVOvc6P4CT4IDDqjuSUWbpGAECPiygYjlUDRkNw5qY0Z8Foo4g0/wBOBvEMotkBEW8JiT9ND1JZAFcqlVksSMdE9YvmrZSoteSs5wSKHxvh0903cLEtRUmwlq1AVt6Eux90ime5iuBbLmwskk882SUcxgRsDp4iocrBZ99bl6oq10D+7h6JyNS0k7fh9o7q5F4fsqENjXvoPASRk1R0Q3pJPT7Uih/wetm4iv9S+jqmoFccYjBnamy7U/NRF2XauTLr0Xj/S2WRPb+eDF59onPyz13Cr/jZC5YzaKqjFGBReW+c3YJ5ETDdWQIDAQABAoIBAAkRpGec/LyuFpAxHCz18r3DnYZFK0zAK6s+i+JsBmNoNfPkz13Lb6/FM9PN+cYE8+xND4DoEiGtLzFAbenB6hamsi9dPVddMQ61ljW68KWaLcfTu8WhQj8yhhwwDNApjiL9Y8PAyrC8sNOXVWVI+j5an2FAAo8xFkTKr5x47QhperQoOyRglNgxaPhZTv98EFKzTFGyyC3zJsffNyyRn2KRATGH3G2wQEIqcaWiIcqypPruwdNy/C42SRLhWTLCW4iUiZvACPBXhTktb/BaJ8dE6h+PAMM1leeor6JolDB62jE8NuBj3sLstnqjXrXlx5xKhK4OWkr3IGu5zibeCQUCgYEA0vSZYab8KyEd1MOPYVUHMxuLrS0WYb9fe+nLulL1G4DiTityUPkEKh0SwJEwUrgI1Zzfe0lRAhmpZSRHq9qrgYtB23JorjyKWtugxqd6Ri0WgA1p3DDkn9lakxDBTEESmaM/fQl65/Jbw9WrrGvDP/L4qvOM6ggXFkj+l+TiKRUCgYEA0acqa7xKmXGvfpZclYXom/03dWqDkpGA1PZyGQPsvD0EJADojWCFLcD1/afCe7m244a1NYjAQKtCl4zOTzi5MmiaA3r5J0K+qxzO1VTdRwCsmtS7eQT1kbHdstvXjS6an9WtpOTkpFZSYzXEOmOy6mBF8SpBV5s1j1pv+7BD7DUCgYEAupNhEPAqeU7J3oKzzibwvi/volONRxiGL8cAy6NRa2jbPr3IVntXRpP+INiIf7CLB7q+IYEfp5bgrjafOQymwWVT8u3GTcv3phI3qVs4ltaL3ud+KCQKIKKRLB8WhwXKmJ28qi73SCufI55YPp/0yRtw+Wl8yQQsvyYCHn9t010CgYAsHhBIMYxFM+4pJjz/Xflv8d4cwDhFvIauydmCuBe2GOTpKqPFNF1yHlvlb8r2PENnJ660QD2snh1aRNAZTadzGx3lw5fwkhQLb/l6XOxfh53Kyx9UPR3r9dDgVXDLjdYN8moi++O9TUjzBZpwaxB4T6AIOssbQ1cG/pH4FcSFTQKBgCbqnSWORX1XLCbLGLS4DPC0kBj/sTRa+weemHZ+P6kIIzDTBgd5ORsxbq3ea7sO47cRFCWdNCxHGwRer0pb9jeb6og5nm1NW0z4M90gIICDWQgBeoS2Cj+S00hnQfSFR4RN3V7yI0Oitjbmah9mtw55bMBhuncqh3agrwxyqLCx" ],
- "certificate" : [ "MIICnTCCAYUCBgGVhuGg5jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdkZWZhdWx0MB4XDTI1MDMxMTIwMjMwOFoXDTM1MDMxMTIwMjQ0OFowEjEQMA4GA1UEAwwHZGVmYXVsdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKzDcb2wlwi2QYqpzRHGfl6RiVbyl5xlTr3Oj+Ak+CAw6o7klFm6RgBAj4soGI5VA0ZDcOamNGfBaKOINP8ATgbxDKLZARFvCYk/TQ9SWQBXKpVZLEjHRPWL5q2UqLXkrOcEih8b4dPdN3CxLUVJsJatQFbehLsfdIpnuYrgWy5sLJJPPNklHMYEbA6eIqHKwWffW5eqKtdA/u4eicjUtJO34faO6uReH7KhDY176DwEkZNUdEN6ST0+1Iof8HrZuIr/Uvo6pqBXHGIwZ2psu1PzURdl2rky69F4/0tlkT2/ngxefaJz8s9dwq/42QuWM2iqoxRgUXlvnN2CeREw3VkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEADI8SCMfX98Y9D1/62CEEIcM6Ws5y8tCcQz+iWefjB+vWFOfzwHd3Fx64P4sC6Orl+hlYWbU3Qn2H+8F1FD4PmsEKDLRZMzSyXqTH5q4Z7UMSLUQe3s5jUfDmHesLRVFf1Qtj2sOCvZCm8NFjwbPBMK6qtzjrLa6Js7jwWFbh0p9ktxpyvxYxLW7KNxglBgIOqYmseKnYwxYKSsYIEcV/ONnGi0wed2xF2EpjGSqhXDmLZOAQhsjuUneSICTkmK83bZrHWa+v9SHvi3Ypo5tnInE3jbuitS/8CUwke5e27mw0wce5CL/Be65Iv/k80gd8tcPucIMga7c0pqFbsQnlSA==" ],
- "priority" : [ "100" ],
- "algorithm" : [ "RSA-OAEP" ]
- }
- }, {
- "id" : "a4d499fc-aced-40b4-9102-255159578eec",
- "name" : "hmac-generated-hs512",
- "providerId" : "hmac-generated",
- "subComponents" : { },
- "config" : {
- "kid" : [ "097e5e0b-731e-4051-8125-e6f2a95d85dd" ],
- "secret" : [ "C5PmVP-PUiEhOvaR8pO91VirtTrFqejyQGJRJDiauQLTiK7M064pqVxeRhuTuPnlJwmykGD0LVHP1Hfk315rt9zR-cyBL3JaVgZsfeU4jHwcwJixFWtmXN0XjG5Ql-UpdMUoVWucvI_TUMNffWuSI2beLHU3ik2NkMUjAXwQ_eM" ],
- "priority" : [ "100" ],
- "algorithm" : [ "HS512" ]
- }
- }, {
- "id" : "f5dc3169-59a8-4790-a9e5-e0bf3c78805c",
- "name" : "aes-generated",
- "providerId" : "aes-generated",
- "subComponents" : { },
- "config" : {
- "kid" : [ "a6afd833-da03-4f08-a7f7-c4b8eb2a375f" ],
- "secret" : [ "aOC5gC8wSuQzdQfwtHqH1w" ],
- "priority" : [ "100" ]
- }
- }, {
- "id" : "762fc122-3966-4253-8ff2-681aef52798a",
- "name" : "rsa-generated",
- "providerId" : "rsa-generated",
- "subComponents" : { },
- "config" : {
- "privateKey" : [ "MIIEpAIBAAKCAQEAsXBlZ1S+NSOWhZ9nGebBTh7TjGPxl0LbcFgKnB8uLnQa7VSAWjmicCB+3bVXIP29l0MtstKJx6tZhGaiIazHGgjtK44hagr0NJwf+B+LwilmzgwcDyrOY4NUMkoFDkoaf4fTN75UKTW+ELKlIkm85mu7X1WySeQg23QvX3tCzPaUoUBP6HnWQwdcc0LeuPRd3rp2LAKPDug2ZWP7VzAWsLbimykd+C8BSJzTYDC0hn2B5t10VHhzOPgpZRbcHPY/xOLKN31oAymLfPH+OxAe7Px0mGZFuH4O6CcQEErsqIFRjsfByMBofjmQJbgOcse9EHLdPZh4vp3GBIdzfwyedQIDAQABAoIBACxBlmkxb8edOrvVSEfjkC9F7QnFG8rYeBcLPoo2FLStbNHpE6JtYaCJ2lq+Eh8+an1B2jIR5R+SE1+8oY+4omhR9aW5a4ghd0tv7WFbrOYeoW+fAZie2p9jcCqi36Pyw60vWXU72Y31w5QympF0xtLJ6BAdNbzMU42o6u/rtHueUpxs9EtckxNvIAKCGcfkHTtpZ7fZkQdv8umsz1IKHyRWlIwdWDCnYRAw5TdjAwRBGk2epzOh9NDxbPiy28MP4JpmRYfmpB6uc33Q5y3krZhfL9TctA3UOPCVcWyVFbAeG4wEcQlB+g6Hqbs5R/krgii3AmdOhB7mlIzhSEQ4joECgYEA19EMUHlI2z275AABP7Mh63njM83UYN6dKnvsxbSCE3mR+0WQHe57Huqt3sr+WsYLre54h25zfw2TbQQjKrJ6uOINfhjwJX0FDkrlw1BO+GRJpzt8kOYskyB8o2arCxakkmhHFyazIKEk1DTh9jLFLXKSaruNJJw9iEQvzX+hlJECgYEA0noSmLjxS3UFANWzoY9ch+N/6fyvJ0Aojgg6lahLlAtL+v59tp4dulfjiCuyzGd0g1WgdPWpnCOHGR+FhikeKMna98PGrIfLL3rV3qjKjioHdtFUsK26pyYxBIu0FHtIKERqeT3tx+cTylXWA2nVd4auABTWzz3NuPOmyGXUjaUCgYEApVZiOMSyLER/TY0zZ7m0otIeXfGyYwQpJAMMweooPQNF81q3rjal3GmuCqE5fBF9oSKw9BCKKywbZcllp7BUlI+aBqDUWeQNm4WFwLwlw+YRBy1roRa1z4Fz+zsMjtIqAoAg9nuPf8/0hx58fkEnDkpYIazN1N5dxad3d9fv0gECgYEApgxGVZQ6UNReAR2XHJNUZaRWSsvdhvK3y+20AlOGZKJQ7BAQL50oSNWDnO8UnOvVYLOR5hPVHmhs8aYLmh8gOv+crzEVsRFke+3FgmbZfjSsNNHKpaQ5iBq6OyLYC/yCnbnz4fi4eafU1iDHuWOqVCS9azUFjvPsM8iNQLYNbT0CgYBqg900PIveL5qsmgVopmg+kimU/w+S/gCV+Gy1aNdzrMAIyrwT0MV7jsYf7eg5kirvtbgNqobjZERVaDdOkbHOW69lE975NoranH3HzYK13Rv3xktnYu5JNYXCEVTV+maG1K0V5BDy5zxleGa7GqVONesNDN0nOh9p+U0WHnpLTA==" ],
- "certificate" : [ "MIICnTCCAYUCBgGVhuGkdTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdkZWZhdWx0MB4XDTI1MDMxMTIwMjMwOVoXDTM1MDMxMTIwMjQ0OVowEjEQMA4GA1UEAwwHZGVmYXVsdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALFwZWdUvjUjloWfZxnmwU4e04xj8ZdC23BYCpwfLi50Gu1UgFo5onAgft21VyD9vZdDLbLSicerWYRmoiGsxxoI7SuOIWoK9DScH/gfi8IpZs4MHA8qzmODVDJKBQ5KGn+H0ze+VCk1vhCypSJJvOZru19VsknkINt0L197Qsz2lKFAT+h51kMHXHNC3rj0Xd66diwCjw7oNmVj+1cwFrC24pspHfgvAUic02AwtIZ9gebddFR4czj4KWUW3Bz2P8Tiyjd9aAMpi3zx/jsQHuz8dJhmRbh+DugnEBBK7KiBUY7HwcjAaH45kCW4DnLHvRBy3T2YeL6dxgSHc38MnnUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAP8LumA7qBduayFtk67nbjI+sg7C6+auJJhclqXVP7qV2C3PpS4UXl1Hs4nuM6blhFMLZJWD+9FN7llsRw8HQg6pXg581QN/2pkYzr1uAP46/EyONYGbh0LkkLhKYHksJOABhkj6W7jRQ9/+1OMveREkbUMjlOOefdEoa6+zrT4sJPQAwNTXHcQrpjHQXEBysrbxO4TMqfdT1athivAMBAVikDVEI2uerarJ3CcM6tEFy4G3qdIEdXuIsx1bm0yPAri5WRIT4mhAFlOon1B6qqaYl6cdvw7L2s3qrCmMwkQbm8Kr7+7OleaCppLapi8Kt5v5XGjYV2y32Qj/zqDW2wA==" ],
- "priority" : [ "100" ]
- }
- } ]
- },
- "internationalizationEnabled" : false,
- "supportedLocales" : [ ],
- "authenticationFlows" : [ {
- "id" : "ee04c896-46c6-46e5-8f5f-2c3f5f5e982f",
- "alias" : "Account verification options",
- "description" : "Method with which to verity the existing account",
- "providerId" : "basic-flow",
- "topLevel" : false,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "idp-email-verification",
- "authenticatorFlow" : false,
- "requirement" : "ALTERNATIVE",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticatorFlow" : true,
- "requirement" : "ALTERNATIVE",
- "priority" : 20,
- "autheticatorFlow" : true,
- "flowAlias" : "Verify Existing Account by Re-authentication",
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "78e019d7-5a2f-44f3-81d0-6fa566f21a32",
- "alias" : "Browser - Conditional OTP",
- "description" : "Flow to determine if the OTP is required for the authentication",
- "providerId" : "basic-flow",
- "topLevel" : false,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "conditional-user-configured",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "auth-otp-form",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 20,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "3482050b-0ea3-4aee-8200-bcc78dfda38d",
- "alias" : "Direct Grant - Conditional OTP",
- "description" : "Flow to determine if the OTP is required for the authentication",
- "providerId" : "basic-flow",
- "topLevel" : false,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "conditional-user-configured",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "direct-grant-validate-otp",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 20,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "64987dfc-4f74-4abe-a9fa-47028f056258",
- "alias" : "First broker login - Conditional OTP",
- "description" : "Flow to determine if the OTP is required for the authentication",
- "providerId" : "basic-flow",
- "topLevel" : false,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "conditional-user-configured",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "auth-otp-form",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 20,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "32144fde-d5b5-4a21-bdd5-fb8d92f3c435",
- "alias" : "Handle Existing Account",
- "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider",
- "providerId" : "basic-flow",
- "topLevel" : false,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "idp-confirm-link",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticatorFlow" : true,
- "requirement" : "REQUIRED",
- "priority" : 20,
- "autheticatorFlow" : true,
- "flowAlias" : "Account verification options",
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "53e1602d-6fb0-4dcb-81a8-e0e549165a2c",
- "alias" : "Reset - Conditional OTP",
- "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.",
- "providerId" : "basic-flow",
- "topLevel" : false,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "conditional-user-configured",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "reset-otp",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 20,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "4819cc94-f5e5-47ca-99ca-35abb57f80e4",
- "alias" : "User creation or linking",
- "description" : "Flow for the existing/non-existing user alternatives",
- "providerId" : "basic-flow",
- "topLevel" : false,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticatorConfig" : "create unique user config",
- "authenticator" : "idp-create-user-if-unique",
- "authenticatorFlow" : false,
- "requirement" : "ALTERNATIVE",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticatorFlow" : true,
- "requirement" : "ALTERNATIVE",
- "priority" : 20,
- "autheticatorFlow" : true,
- "flowAlias" : "Handle Existing Account",
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "5942a1e4-f029-40cd-ae92-830898bf97dc",
- "alias" : "Verify Existing Account by Re-authentication",
- "description" : "Reauthentication of existing account",
- "providerId" : "basic-flow",
- "topLevel" : false,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "idp-username-password-form",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticatorFlow" : true,
- "requirement" : "CONDITIONAL",
- "priority" : 20,
- "autheticatorFlow" : true,
- "flowAlias" : "First broker login - Conditional OTP",
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "92632d82-47d5-4dd6-8a7e-8bf3e0ecd02f",
- "alias" : "browser",
- "description" : "browser based authentication",
- "providerId" : "basic-flow",
- "topLevel" : true,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "auth-cookie",
- "authenticatorFlow" : false,
- "requirement" : "ALTERNATIVE",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "auth-spnego",
- "authenticatorFlow" : false,
- "requirement" : "DISABLED",
- "priority" : 20,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticatorConfig" : "oidc",
- "authenticator" : "identity-provider-redirector",
- "authenticatorFlow" : false,
- "requirement" : "ALTERNATIVE",
- "priority" : 25,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticatorFlow" : true,
- "requirement" : "ALTERNATIVE",
- "priority" : 30,
- "autheticatorFlow" : true,
- "flowAlias" : "forms",
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "dfedc4d5-a50e-4622-bba6-7e5ee9b2065c",
- "alias" : "clients",
- "description" : "Base authentication for clients",
- "providerId" : "client-flow",
- "topLevel" : true,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "client-secret",
- "authenticatorFlow" : false,
- "requirement" : "ALTERNATIVE",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "client-jwt",
- "authenticatorFlow" : false,
- "requirement" : "ALTERNATIVE",
- "priority" : 20,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "client-secret-jwt",
- "authenticatorFlow" : false,
- "requirement" : "ALTERNATIVE",
- "priority" : 30,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "client-x509",
- "authenticatorFlow" : false,
- "requirement" : "ALTERNATIVE",
- "priority" : 40,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "25ac344e-2d5e-4c5c-8f86-2b8ba2e3dfde",
- "alias" : "direct grant",
- "description" : "OpenID Connect Resource Owner Grant",
- "providerId" : "basic-flow",
- "topLevel" : true,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "direct-grant-validate-username",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "direct-grant-validate-password",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 20,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticatorFlow" : true,
- "requirement" : "CONDITIONAL",
- "priority" : 30,
- "autheticatorFlow" : true,
- "flowAlias" : "Direct Grant - Conditional OTP",
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "fc07ca16-4d7d-4132-a6d6-352a566d19da",
- "alias" : "docker auth",
- "description" : "Used by Docker clients to authenticate against the IDP",
- "providerId" : "basic-flow",
- "topLevel" : true,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "docker-http-basic-authenticator",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "eb9f31b1-f136-47bc-93d1-9fc8c62de3c5",
- "alias" : "first broker login",
- "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
- "providerId" : "basic-flow",
- "topLevel" : true,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticatorConfig" : "review profile config",
- "authenticator" : "idp-review-profile",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticatorFlow" : true,
- "requirement" : "REQUIRED",
- "priority" : 20,
- "autheticatorFlow" : true,
- "flowAlias" : "User creation or linking",
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "92f6f1d6-7684-4c3a-8623-1a1643cf1b07",
- "alias" : "forms",
- "description" : "Username, password, otp and other auth forms.",
- "providerId" : "basic-flow",
- "topLevel" : false,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "auth-username-password-form",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticatorFlow" : true,
- "requirement" : "CONDITIONAL",
- "priority" : 20,
- "autheticatorFlow" : true,
- "flowAlias" : "Browser - Conditional OTP",
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "4701b039-0e51-435c-a867-76e602446315",
- "alias" : "registration",
- "description" : "registration flow",
- "providerId" : "basic-flow",
- "topLevel" : true,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "registration-page-form",
- "authenticatorFlow" : true,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : true,
- "flowAlias" : "registration form",
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "4857befc-6fac-45f1-ae13-02830ab04646",
- "alias" : "registration form",
- "description" : "registration form",
- "providerId" : "form-flow",
- "topLevel" : false,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "registration-user-creation",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 20,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "registration-password-action",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 50,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "registration-recaptcha-action",
- "authenticatorFlow" : false,
- "requirement" : "DISABLED",
- "priority" : 60,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "registration-terms-and-conditions",
- "authenticatorFlow" : false,
- "requirement" : "DISABLED",
- "priority" : 70,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "72627447-0fbe-4017-96ab-b12eacc75a2d",
- "alias" : "reset credentials",
- "description" : "Reset credentials for a user if they forgot their password or something",
- "providerId" : "basic-flow",
- "topLevel" : true,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "reset-credentials-choose-user",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "reset-credential-email",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 20,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticator" : "reset-password",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 30,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- }, {
- "authenticatorFlow" : true,
- "requirement" : "CONDITIONAL",
- "priority" : 40,
- "autheticatorFlow" : true,
- "flowAlias" : "Reset - Conditional OTP",
- "userSetupAllowed" : false
- } ]
- }, {
- "id" : "d8bec40b-7015-40c9-b069-c1bb1b1cf09e",
- "alias" : "saml ecp",
- "description" : "SAML ECP Profile Authentication Flow",
- "providerId" : "basic-flow",
- "topLevel" : true,
- "builtIn" : true,
- "authenticationExecutions" : [ {
- "authenticator" : "http-basic-authenticator",
- "authenticatorFlow" : false,
- "requirement" : "REQUIRED",
- "priority" : 10,
- "autheticatorFlow" : false,
- "userSetupAllowed" : false
- } ]
- } ],
- "authenticatorConfig" : [ {
- "id" : "6ee3b9e1-c19b-43f5-bc30-4dde2953aa3f",
- "alias" : "create unique user config",
- "config" : {
- "require.password.update.after.registration" : "false"
- }
- }, {
- "id" : "420707ab-984e-4a95-a679-26cddf145323",
- "alias" : "oidc",
- "config" : {
- "default.reference.maxAge" : "10000",
- "default.reference.value" : "CILogon",
- "defaultProvider" : "oidc"
- }
- }, {
- "id" : "6bab8c26-0c25-4cf3-a5a7-634cdfcf9f41",
- "alias" : "review profile config",
- "config" : {
- "update.profile.on.first.login" : "missing"
- }
- } ],
- "requiredActions" : [ {
- "alias" : "CONFIGURE_TOTP",
- "name" : "Configure OTP",
- "providerId" : "CONFIGURE_TOTP",
- "enabled" : true,
- "defaultAction" : false,
- "priority" : 10,
- "config" : { }
- }, {
- "alias" : "TERMS_AND_CONDITIONS",
- "name" : "Terms and Conditions",
- "providerId" : "TERMS_AND_CONDITIONS",
- "enabled" : false,
- "defaultAction" : false,
- "priority" : 20,
- "config" : { }
- }, {
- "alias" : "UPDATE_PASSWORD",
- "name" : "Update Password",
- "providerId" : "UPDATE_PASSWORD",
- "enabled" : true,
- "defaultAction" : false,
- "priority" : 30,
- "config" : { }
- }, {
- "alias" : "UPDATE_PROFILE",
- "name" : "Update Profile",
- "providerId" : "UPDATE_PROFILE",
- "enabled" : true,
- "defaultAction" : false,
- "priority" : 40,
- "config" : { }
- }, {
- "alias" : "VERIFY_EMAIL",
- "name" : "Verify Email",
- "providerId" : "VERIFY_EMAIL",
- "enabled" : true,
- "defaultAction" : false,
- "priority" : 50,
- "config" : { }
- }, {
- "alias" : "delete_account",
- "name" : "Delete Account",
- "providerId" : "delete_account",
- "enabled" : false,
- "defaultAction" : false,
- "priority" : 60,
- "config" : { }
- }, {
- "alias" : "webauthn-register",
- "name" : "Webauthn Register",
- "providerId" : "webauthn-register",
- "enabled" : true,
- "defaultAction" : false,
- "priority" : 70,
- "config" : { }
- }, {
- "alias" : "webauthn-register-passwordless",
- "name" : "Webauthn Register Passwordless",
- "providerId" : "webauthn-register-passwordless",
- "enabled" : true,
- "defaultAction" : false,
- "priority" : 80,
- "config" : { }
- }, {
- "alias" : "VERIFY_PROFILE",
- "name" : "Verify Profile",
- "providerId" : "VERIFY_PROFILE",
- "enabled" : true,
- "defaultAction" : false,
- "priority" : 90,
- "config" : { }
- }, {
- "alias" : "update_user_locale",
- "name" : "Update User Locale",
- "providerId" : "update_user_locale",
- "enabled" : true,
- "defaultAction" : false,
- "priority" : 1000,
- "config" : { }
- } ],
- "browserFlow" : "browser",
- "registrationFlow" : "registration",
- "directGrantFlow" : "direct grant",
- "resetCredentialsFlow" : "reset credentials",
- "clientAuthenticationFlow" : "clients",
- "dockerAuthenticationFlow" : "docker auth",
- "firstBrokerLoginFlow" : "first broker login",
- "attributes" : {
- "cibaBackchannelTokenDeliveryMode" : "poll",
- "cibaAuthRequestedUserHint" : "login_hint",
- "clientOfflineSessionMaxLifespan" : "0",
- "oauth2DevicePollingInterval" : "5",
- "clientSessionIdleTimeout" : "0",
- "actionTokenGeneratedByUserLifespan.idp-verify-account-via-email" : "",
- "actionTokenGeneratedByUserLifespan.verify-email" : "",
- "clientOfflineSessionIdleTimeout" : "0",
- "actionTokenGeneratedByUserLifespan.execute-actions" : "",
- "cibaInterval" : "5",
- "realmReusableOtpCode" : "false",
- "cibaExpiresIn" : "120",
- "oauth2DeviceCodeLifespan" : "600",
- "parRequestUriLifespan" : "60",
- "clientSessionMaxLifespan" : "0",
- "frontendUrl" : "",
- "acr.loa.map" : "{}",
- "shortVerificationUri" : "",
- "actionTokenGeneratedByUserLifespan.reset-credentials" : ""
- },
- "keycloakVersion" : "24.0.0",
- "userManagedAccessAllowed" : false,
- "clientProfiles" : {
- "profiles" : [ ]
- },
- "clientPolicies" : {
- "policies" : [ ]
- }
-}
\ No newline at end of file
diff --git a/.devcontainer/portals/Dockerfile b/.devcontainer/portals/Dockerfile
deleted file mode 100644
index 94a9de0b06f..00000000000
--- a/.devcontainer/portals/Dockerfile
+++ /dev/null
@@ -1,33 +0,0 @@
-FROM php:8.1-apache
-
-# TODO put all 3 portals -- research portal (:5173), php gateway (:8008), and django portal (:8009)
-# TODO handle ssl termination before requests hit this container
-
-ARG PORTALS_REPO_URL=https://github.com/apache/airavata-portals/archive/refs/heads/main.tar.gz
-
-# set hostname
-RUN echo "ServerName airavata.host" >> /etc/apache2/apache2.conf
-
-# install deps
-RUN apt-get update && apt-get install -y wget git unzip && rm -rf /var/lib/apt/lists/*
-RUN wget -qO- https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
-
-# setup site config
-COPY pga-default.conf /etc/apache2/sites-available/000-default.conf
-
-RUN a2enmod rewrite && a2ensite 000-default.conf && apache2ctl configtest
-
-USER www-data:www-data
-
-WORKDIR /var/www/html/default
-
-# ensure config directory exists for mounting
-RUN mkdir -p config storage/views storage/sessions bootstrap/cache
-
-RUN wget -qO- $PORTALS_REPO_URL | tar xz --strip-components=2 -C . airavata-portals-main/airavata-php-gateway
-
-COPY pga_config.php /var/www/html/default/config/pga_config.php
-
-RUN composer install --no-dev --optimize-autoloader
-
-EXPOSE 5173 8008 8009
diff --git a/.devcontainer/portals/pga-default.conf b/.devcontainer/portals/pga-default.conf
deleted file mode 100644
index e3b250eac4b..00000000000
--- a/.devcontainer/portals/pga-default.conf
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- DocumentRoot /var/www/html/default/public
-
- AllowOverride All
-
- ErrorLog /var/log/apache2/default.error.log
- CustomLog /var/log/apache2/default.requests.log combined
-
-
\ No newline at end of file
diff --git a/.devcontainer/portals/pga_config.php b/.devcontainer/portals/pga_config.php
deleted file mode 100644
index 80fb112cec2..00000000000
--- a/.devcontainer/portals/pga_config.php
+++ /dev/null
@@ -1,324 +0,0 @@
- [
-
- /**
- * Admin Role Name
- */
- 'admin-role-name' => 'Internal/everyone',
-
- /**
- * Read only Admin Role Name
- */
- 'read-only-admin-role-name' => 'Internal/everyone',
-
- /**
- * Gateway user role
- */
- 'user-role-name' => 'Internal/everyone',
-
- /**
- * Initial user role. This is the initial user role assigned to a new
- * user. Set this to one of the three roles above to automatically
- * grant new users that role, or set to some other role ('user-pending')
- * to require admin approval before users have access.
- */
- 'initial-role-name' => 'user-pending',
-
- /**
- * Tenant Domain
- */
- 'tenant-domain' => 'default',
-
- /**
- * Tenant admin's username
- */
- 'admin-username' => 'default-admin',
-
- /**
- * Tenant admin's password
- */
- 'admin-password' => 'ade4#21242ftfd',
-
- /**
- * OAuth client key
- */
- 'oauth-client-key' => 'pga',
-
- /**
- * OAuth client secret
- */
- 'oauth-client-secret' => 'upCMVu2RZcAXUqpr9V7phAbz6hhF9cbl',
-
- /**
- * Authentication options
- */
- 'auth-options' => [
- // Example of password based login
- [
- 'oauth-grant-type' => 'password',
- 'name' => 'Airavata PHP Gateway',
- ],
- // Example of external identity provider login
- [
- 'oauth-grant-type' => 'authorization_code',
- 'name' => 'CILogon',
- // Optional
- // Note: kc_idp_hint is used to skip Keycloak login screen and redirect immediately to this identity provider
- // http://www.keycloak.org/docs/2.5/server_admin/topics/identity-broker/suggested.html
- 'oauth-authorize-url-extra-params' => 'kc_idp_hint=oidc',
- // Optional
- 'logo' => '/assets/path_to_image.png'
- ],
- ],
-
- /**
- * OAuth Grant Type (password or authorization_code)
- */
- 'oauth-grant-type' => 'authorization_code',
-
- /**
- * OAuth call back url (only if the grant type is authorization_code)
- */
- 'oauth-callback-url' => 'http://localhost:8000/callback-url',
-
- /**
- * For OIDC servers that support the discovery protocol.
- */
- 'openid-connect-discovery-url' => 'http://airavata.host:18080/realms/default/.well-known/openid-configuration',
-
- /**
- * Identity server url
- */
- 'service-url' => 'http://airavata.host:18080',
-
- /**
- * Enable HTTPS server verification
- */
- 'verify-peer' => false,
-
- /**
- * Path to the server certificate file
- */
- 'cafile-path' => app_path() . '/resources/security/incommon_rsa_server_ca.pem',
- ],
-
-
- /**
- * *****************************************************************
- * Airavata Client Configurations
- * *****************************************************************
- */
- 'airavata' => [
-
- /**
- * Airavata API server location. Use tls:// as the protocol to
- * connect TLS enabled Airavata
- */
- 'airavata-server' => 'airavata.host',
-
- /**
- * Airavata API server port
- */
- 'airavata-port' => '8930',
-
- /**
- * Airavata Profile Service server location. Use tls:// as the protocol to
- * connect over TLS
- */
- 'airavata-profile-service-server' => 'airavata.host',
-
- /**
- * Airavata Profile Service port
- */
- 'airavata-profile-service-port' => '8962',
-
- /**
- * Airavata API server thrift communication timeout
- */
- 'airavata-timeout' => '1000000',
-
- /**
- * PGA Gateway ID
- */
- 'gateway-id' => 'default',
-
- /**
- * absolute path of the data dir
- */
- 'experiment-data-absolute-path' => '/var/www/experimentData',
-
- /**
- * Advanced experiments options
- */
- 'advanced-experiment-options' => '',
-
- /**
- * Default queue name
- */
- 'queue-name' => 'long',
-
- /**
- * Default node count
- */
- 'node-count' => '1',
-
- /**
- * Default total core count
- */
- 'total-cpu-count' => '16',
-
- /**
- * Default wall time limit
- */
- 'wall-time-limit' => '30',
-
- /**
- * Max node count
- */
- 'max-node-count' => '4',
-
- /**
- * Max total core count
- */
- 'max-total-cpu-count' => '96',
-
- /**
- * Max wall time limit
- */
- 'max-wall-time-limit' => '120',
-
- /**
- * Enable app-catalog cache
- */
- 'enable-app-catalog-cache' => true,
-
- /**
- * Life time of app catalog data cache in minutes
- */
- 'app-catalog-cache-duration' => 5,
-
- /**
- * Gateway data store resource id
- */
- 'gateway-data-store-resource-id' => '',
-
- /**
- * Data Sharing enabled
- */
- 'data-sharing-enabled' => false,
-
- /**
- * Group Resource Profile ID to use when submitting experiments
- */
- 'group-resource-profile-id' => '',
- ],
-
- /**
- * *****************************************************************
- * Portal Related Configurations
- * *****************************************************************
- */
- 'portal' => [
- /**
- * Whether this portal is the SciGaP admin portal
- */
- 'super-admin-portal' => true,
-
- /**
- * Set the name of theme in use here
- */
- 'theme' => 'base',
-
- /**
- * Portal title
- */
- 'portal-title' => 'Airavata PHP Gateway',
-
- /**
- * Email address of the portal admin. Portal admin well get email notifications for events
- * such as new user creation
- */
- 'admin-emails' => ['xxx@xxx.com','yyy@yyy.com'],
-
- /**
- * Email account that the portal should login to send emails
- */
- 'portal-email-username' => 'xyz@abc.com',
-
- /**
- * Password for the portal's email account
- */
- 'portal-email-password' => 'xxxxxxxxxxx',
-
- /**
- * SMTP server on which the portal should connect
- */
- 'portal-smtp-server-host' => 'smtp.gmail.com',
-
- /**
- * SMTP server port on which the portal should connect
- */
- 'portal-smtp-server-port' => '587',
-
- /**
- * Email verification code valid time interval in minutes
- */
- 'email-verify-code-valid-time' => 360,
-
- /**
- * Set this to true if theme has set links to login
- */
- 'theme-based-login-links-configured' => false,
-
- /**
- * Set the URL of the new Django portal for re-routing
- */
- 'airavata-django-url' => "http://airavata.host:8009/",
-
- /**
- * Set to true to enable reroute to the new Django portal
- */
- 'reroute-to-django' => true,
-
- /**
- * Set JIRA Issue Collector scripts here.
- */
- 'jira-help' =>
- [
- /**
- * Report Issue Script issued for your app by Atlassian JIRA
- */
- 'report-issue-script' => '',
- /**
- * Collector id at the end of the above script
- */
- 'report-issue-collector-id' => '',
- /**
- * Create Report Script issued for your app by Atlassian JIRA
- */
- 'request-feature-script' => '',
- /**
- * Collector id at the end of the above script
- */
- 'request-feature-collector-id' => ''
- ],
-
- /**
- * Set Google Analytics Id here. ID format that generates from
- * creating tracker object should be
- *
- * UA-XXXXX-Y
- *
- * for it to be working correctly. Currently it is only set for
- * sending pageviews.
- */
- 'google-analytics-id' => ''
- ]
-);
diff --git a/.devcontainer/proxy/http.conf b/.devcontainer/proxy/http.conf
deleted file mode 100644
index 19ebd0c9c51..00000000000
--- a/.devcontainer/proxy/http.conf
+++ /dev/null
@@ -1,104 +0,0 @@
-# research portal (5173)
-server {
- listen 5173 ssl;
- if ($scheme != "https") {
- return 301 https://$host$request_uri;
- }
- http2 on;
- server_name airavata.host;
- ssl_certificate /vault/server.crt;
- ssl_certificate_key /vault/server.key;
- location / {
- proxy_pass http://portals:5173;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Forwarded-Port $server_port;
- proxy_redirect default;
- }
-}
-
-# php portal (8008)
-server {
- listen 8008 ssl;
- if ($scheme != "https") {
- return 301 https://$host$request_uri;
- }
- http2 on;
- server_name airavata.host;
- ssl_certificate /vault/server.crt;
- ssl_certificate_key /vault/server.key;
- location / {
- proxy_pass http://portals:8008;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Forwarded-Port $server_port;
- proxy_redirect default;
- }
-}
-
-# django portal (8009)
-server {
- listen 8009 ssl;
- if ($scheme != "https") {
- return 301 https://$host$request_uri;
- }
- http2 on;
- server_name airavata.host;
- ssl_certificate /vault/server.crt;
- ssl_certificate_key /vault/server.key;
- location / {
- proxy_pass http://portals:8009;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Forwarded-Port $server_port;
- proxy_redirect default;
- }
-}
-
-# keycloak (8443)
-server {
- listen 8443 ssl;
- if ($scheme != "https") {
- return 301 https://$host$request_uri;
- }
- http2 on;
- server_name airavata.host;
- ssl_certificate /vault/server.crt;
- ssl_certificate_key /vault/server.key;
- location / {
- proxy_pass http://keycloak:18080;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Forwarded-Port $server_port;
- proxy_redirect default;
- }
-}
-
-# jupyterhub (20000)
-server {
- listen 20000 ssl;
- if ($scheme != "https") {
- return 301 https://$host$request_uri;
- }
- http2 on;
- server_name airavata.host;
- ssl_certificate /vault/server.crt;
- ssl_certificate_key /vault/server.key;
- location / {
- proxy_pass http://jupyterhub:20000;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Forwarded-Port $server_port;
- proxy_redirect default;
- }
-}
diff --git a/.devcontainer/proxy/nginx.conf b/.devcontainer/proxy/nginx.conf
deleted file mode 100644
index b9536d0aaa6..00000000000
--- a/.devcontainer/proxy/nginx.conf
+++ /dev/null
@@ -1,15 +0,0 @@
-worker_processes auto;
-error_log /var/log/nginx/error.log warn;
-pid /var/run/nginx.pid;
-
-events {
- worker_connections 1024;
-}
-
-http {
- include /etc/nginx/conf.d/http.conf;
-}
-
-stream {
- include /etc/nginx/conf.d/stream.conf;
-}
\ No newline at end of file
diff --git a/.devcontainer/proxy/stream.conf b/.devcontainer/proxy/stream.conf
deleted file mode 100644
index 782fbfc51d0..00000000000
--- a/.devcontainer/proxy/stream.conf
+++ /dev/null
@@ -1,18 +0,0 @@
-# Thrift server (9930 -> 8930)
-server {
- listen 9930 ssl; # accept TLS on port 9930
- proxy_pass 192.168.100.9:8930; # forward decrypted traffic
-
- # point to your cert+key
- ssl_certificate /vault/server.crt;
- ssl_certificate_key /vault/server.key;
-
- # (optional) enforce modern TLS only
- ssl_protocols TLSv1.2 TLSv1.3;
- ssl_ciphers HIGH:!aNULL:!MD5;
- ssl_prefer_server_ciphers on;
-
- # tuning
- proxy_connect_timeout 5s;
- proxy_timeout 5s;
-}
diff --git a/.devcontainer/test-infra/sftp/Dockerfile b/.devcontainer/test-infra/sftp/Dockerfile
new file mode 100644
index 00000000000..8115880e1ff
--- /dev/null
+++ b/.devcontainer/test-infra/sftp/Dockerfile
@@ -0,0 +1,47 @@
+# Multi-arch SFTP test server
+FROM --platform=$BUILDPLATFORM ubuntu:22.04
+
+ARG TARGETPLATFORM
+ARG BUILDPLATFORM
+
+ENV DEBIAN_FRONTEND=noninteractive
+
+# Install OpenSSH server
+RUN apt-get update && apt-get install -y \
+ openssh-server \
+ netcat-openbsd \
+ && rm -rf /var/lib/apt/lists/*
+
+# Create test user
+RUN useradd -m -s /bin/bash testuser && \
+ echo "testuser:testpass" | chpasswd
+
+# Configure SSH for SFTP
+RUN mkdir -p /var/run/sshd && \
+ ssh-keygen -A && \
+ mkdir -p /home/testuser/.ssh && \
+ mkdir -p /home/testuser/data && \
+ chmod 700 /home/testuser/.ssh && \
+ chmod 755 /home/testuser/data && \
+ chown -R testuser:testuser /home/testuser
+
+# Configure SSH daemon for SFTP
+RUN sed -i 's/#Subsystem sftp.*/Subsystem sftp internal-sftp/' /etc/ssh/sshd_config && \
+ echo "Port 22" >> /etc/ssh/sshd_config && \
+ echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \
+ echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config && \
+ echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config && \
+ echo "Match User testuser" >> /etc/ssh/sshd_config && \
+ echo " ChrootDirectory /home/testuser" >> /etc/ssh/sshd_config && \
+ echo " ForceCommand internal-sftp" >> /etc/ssh/sshd_config && \
+ echo " AllowTcpForwarding no" >> /etc/ssh/sshd_config && \
+ echo " X11Forwarding no" >> /etc/ssh/sshd_config
+
+# Expose SSH port
+EXPOSE 22
+
+# Start SSH daemon
+COPY entrypoint.sh /entrypoint.sh
+RUN chmod +x /entrypoint.sh
+
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/.devcontainer/test-infra/sftp/entrypoint.sh b/.devcontainer/test-infra/sftp/entrypoint.sh
new file mode 100755
index 00000000000..525a6d3fd44
--- /dev/null
+++ b/.devcontainer/test-infra/sftp/entrypoint.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+set -e
+
+# Create data directory
+mkdir -p /home/testuser/data
+chown testuser:testuser /home/testuser/data
+
+# Start SSH daemon in foreground
+exec /usr/sbin/sshd -D
diff --git a/.devcontainer/test-infra/slurm/Dockerfile b/.devcontainer/test-infra/slurm/Dockerfile
new file mode 100644
index 00000000000..9ecd75c8da1
--- /dev/null
+++ b/.devcontainer/test-infra/slurm/Dockerfile
@@ -0,0 +1,47 @@
+# Multi-arch SLURM test cluster
+FROM --platform=$BUILDPLATFORM ubuntu:22.04
+
+ARG TARGETPLATFORM
+ARG BUILDPLATFORM
+
+ENV DEBIAN_FRONTEND=noninteractive
+
+# Install SLURM and dependencies
+RUN apt-get update && apt-get install -y \
+ slurm-wlm \
+ slurm-wlm-basic-plugins \
+ slurmctld \
+ slurmd \
+ openssh-server \
+ sudo \
+ netcat-openbsd \
+ && rm -rf /var/lib/apt/lists/*
+
+# Create test user
+RUN useradd -m -s /bin/bash testuser && \
+ echo "testuser:testpass" | chpasswd && \
+ echo "testuser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
+
+# Configure SSH
+RUN mkdir -p /var/run/sshd && \
+ ssh-keygen -A && \
+ mkdir -p /home/testuser/.ssh && \
+ chmod 700 /home/testuser/.ssh
+
+# Copy SLURM configuration
+COPY slurm.conf /etc/slurm/slurm.conf
+RUN chmod 644 /etc/slurm/slurm.conf && \
+ mkdir -p /var/spool/slurmctld && \
+ mkdir -p /var/spool/slurmd && \
+ chown -R slurm:slurm /var/spool/slurm* && \
+ mkdir -p /var/log/slurm && \
+ chown -R slurm:slurm /var/log/slurm
+
+# Expose ports
+EXPOSE 22 6817 6818
+
+# Start services
+COPY entrypoint.sh /entrypoint.sh
+RUN chmod +x /entrypoint.sh
+
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/.devcontainer/test-infra/slurm/entrypoint.sh b/.devcontainer/test-infra/slurm/entrypoint.sh
new file mode 100755
index 00000000000..e7e2eabd278
--- /dev/null
+++ b/.devcontainer/test-infra/slurm/entrypoint.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+set -e
+
+# Start munge authentication
+service munge start
+
+# Start SSH daemon
+service ssh start
+
+# Initialize SLURM database
+if [ ! -f /var/spool/slurmctld/state ]; then
+ touch /var/spool/slurmctld/state
+ chown slurm:slurm /var/spool/slurmctld/state
+fi
+
+# Start SLURM controller
+slurmctld -D &
+
+# Wait for controller to be ready
+sleep 5
+
+# Start SLURM daemon
+slurmd -D &
+
+# Keep container running
+tail -f /var/log/slurm/*.log
diff --git a/.devcontainer/test-infra/slurm/slurm.conf b/.devcontainer/test-infra/slurm/slurm.conf
new file mode 100644
index 00000000000..c50c40cc804
--- /dev/null
+++ b/.devcontainer/test-infra/slurm/slurm.conf
@@ -0,0 +1,31 @@
+# SLURM configuration for test cluster
+ClusterName=test-cluster
+ControlMachine=slurm-test-cluster
+ControlAddr=localhost
+SlurmUser=slurm
+SlurmctldPort=6817
+SlurmdPort=6818
+AuthType=auth/munge
+StateSaveLocation=/var/spool/slurmctld
+SlurmdSpoolDir=/var/spool/slurmd
+SwitchType=switch/none
+MpiDefault=none
+SlurmctldPidFile=/var/run/slurmctld.pid
+SlurmdPidFile=/var/run/slurmd.pid
+SlurmctldLogFile=/var/log/slurm/slurmctld.log
+SlurmdLogFile=/var/log/slurm/slurmd.log
+
+# Node configuration
+NodeName=slurm-test-cluster NodeAddr=localhost CPUs=4 RealMemory=8192 State=UNKNOWN
+PartitionName=normal Nodes=slurm-test-cluster Default=YES MaxTime=INFINITE State=UP
+
+# Scheduling
+SchedulerType=sched/backfill
+SelectType=select/cons_res
+SelectTypeParameters=CR_CPU
+
+# Proctrack
+ProctrackType=proctrack/linuxproc
+
+# Job accounting (disabled for testing)
+JobAcctGatherType=jobacct_gather/none
diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index 43fb6a53e7f..00000000000
--- a/.dockerignore
+++ /dev/null
@@ -1,5 +0,0 @@
-.devcontainer
-.github
-.idea
-.run
-.vscode
\ No newline at end of file
diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml
index 3557266f9bf..37c69c3ffd0 100644
--- a/.github/workflows/build-and-publish.yml
+++ b/.github/workflows/build-and-publish.yml
@@ -2,7 +2,7 @@ name: Build and Push Airavata Docker Image
on:
push:
- branches: [ main, master ]
+ branches: [ main, master, service-layer-improvements ]
tags: [ "v*" ]
workflow_dispatch:
@@ -22,28 +22,16 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- - name: Set up JDK 17
+ - name: Set up JDK 25
uses: actions/setup-java@v4
with:
distribution: "temurin"
- java-version: "17"
-
- - name: Install OS dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y build-essential automake bison flex libboost-all-dev libevent-dev libssl-dev libtool pkg-config
-
- - name: Set up Thrift 0.22.0
- run: |
- wget -q https://dlcdn.apache.org/thrift/0.22.0/thrift-0.22.0.tar.gz
- tar -xzf thrift-0.22.0.tar.gz
- cd thrift-0.22.0
- ./configure --without-rs --enable-libs=no --enable-tests=no
- make -j$(nproc)
- sudo make install
+ java-version: "25"
+ cache: "maven"
- name: Build with Maven (skip tests)
- run: mvn -B -DskipTests clean install
+ run: ./mvnw -B -DskipTests clean install
+ working-directory: airavata-api
- name: Set up QEMU (multi-arch)
uses: docker/setup-qemu-action@v3
@@ -74,8 +62,8 @@ jobs:
id: buildpush
uses: docker/build-push-action@v5
with:
- context: .
- file: ./Dockerfile
+ context: airavata-api/modules/distribution/target/
+ file: airavata-api/modules/distribution/src/main/docker/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml
index 1b69f61d1b9..f89663d8978 100644
--- a/.github/workflows/maven-build.yml
+++ b/.github/workflows/maven-build.yml
@@ -4,33 +4,24 @@ on:
pull_request:
branches:
- master
+ - service-layer-improvements
push:
branches:
- master
+ - service-layer-improvements
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-latest
steps:
- - name: Set up OS dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y build-essential automake bison flex libboost-all-dev libevent-dev libssl-dev libtool pkg-config
- - name: Set up Thrift 0.22.0
- run: |
- wget -q https://dlcdn.apache.org/thrift/0.22.0/thrift-0.22.0.tar.gz
- tar -xzf thrift-0.22.0.tar.gz
- cd thrift-0.22.0
- ./configure --without-rs --enable-libs=no --enable-tests=no
- make -j$(nproc)
- sudo make install
- thrift --version
- - name: Set up JDK 17
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Set up JDK 25
uses: actions/setup-java@v4
with:
distribution: "temurin"
- java-version: "17"
- - name: Checkout code
- uses: actions/checkout@v4
- - name: Build with Maven (skip tests)
- run: mvn clean install -DskipTests
+ java-version: "25"
+ cache: "maven"
+ - name: Build and test with Maven
+ run: ./mvnw clean install
+ working-directory: airavata-api
diff --git a/.github/workflows/style-check.yml b/.github/workflows/style-check.yml
index 1d1300af686..bc8dacad407 100644
--- a/.github/workflows/style-check.yml
+++ b/.github/workflows/style-check.yml
@@ -4,26 +4,31 @@ on:
pull_request:
branches:
- master
+ - service-layer-improvements
push:
branches:
- master
+ - service-layer-improvements
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-latest
steps:
- - name: Set up JDK 17
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Set up JDK 25
uses: actions/setup-java@v4
with:
distribution: "temurin"
- java-version: "17"
- - name: Checkout code
- uses: actions/checkout@v4
+ java-version: "25"
+ cache: "maven"
- name: Run Spotless Check
- run: mvn spotless:check
+ run: ./mvnw spotless:check
+ working-directory: airavata-api
- name: Run Spotless Apply if Check Fails
if: failure()
- run: mvn spotless:apply
+ run: ./mvnw spotless:apply
+ working-directory: airavata-api
- name: Generate Spotless Patch
if: failure()
run: |
diff --git a/.gitignore b/.gitignore
index 347d0a12892..e581f2cb77d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,62 +1,143 @@
-# From https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore
-# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
+.devcontainer/.env
+.claude/
-## Directory-based project format
+# IDE and Editor files
.idea/
-# if you remove the above rule, at least ignore user-specific stuff:
-# .idea/workspace.xml
-# .idea/tasks.xml
-# and these sensitive or high-churn files:
-# .idea/dataSources.ids
-# .idea/dataSources.xml
-# .idea/sqlDataSources.xml
-# .idea/dynamic.xml
-
-## File-based project format
+!.idea/runConfigurations/
*.ipr
*.iws
*.iml
-*target
-## Additional for IntelliJ
-out/
-
-# generated by mpeltonen/sbt-idea plugin
+.vscode/
+!.vscode/launch.json
+!.vscode/tasks.json
+.project
+.settings/
+.classpath
+.factorypath
.idea_modules/
-
-# generated by JIRA plugin
atlassian-ide-plugin.xml
-
-# generated by Crashlytics plugin (for Android Studio and Intellij)
com_crashlytics_export_strings.xml
+# OS files
.DS_Store
-.project
-.vscode
-!.vscode/launch.json
-.settings
-.classpath
-.factorypath
+.AppleDouble
+.LSOverride
+Icon
+._*
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
-venv
-airavata_custos.egg-info
+# Python
+venv/
+ENV/
+env/
+.venv
*.pyc
-.tox
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+htmlcov/
+.ipynb_checkpoints/
+.mypy_cache/
+.dmypy.json
+dmypy.json
+.pyre/
+.pytype/
+cython_debug/
+
+# Java / Maven
+**/target/
+**/generated-sources/
+out/
+*.class
+.mvn/build-cache/
+airavata-api/distribution/
-airavata-local-agent/node_modules
-airavata-local-agent/app
-airavata-local-agent/proxy/config.txt
-airavata-local-agent/proxy/websockify
-airavata-local-agent/renderer/.next
-airavata-local-agent/dist
-airavata-local-agent/build.sh
+# Node.js
+node_modules/
+.npm
+.node_repl_history
+*.tgz
+.yarn-integrity
+.pnp.*
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
-modules/research-framework/compose
+# Go
+vendor/
+*.test
+*.out
+*.prof
+# Logs and temporary files
logs/
+*.log
+output*.log
+test_chunks.txt*
+chunk*.txt
-# Ignore Maven build output
-**/target/
-# Ignore generated sources
-**/generated-sources/
-/distribution
-/vault*
+# Test artifacts and reports
+TEST_*.md
+PRODUCTION_READINESS.md
+*.retry
+
+# Generated keystores and credentials
+**/keystores/*.p12
+**/keystores/*.p12.tmp
+**/keystores/*.key
+**/keystores/*.crt
+**/keystores/*.jks
+**/keystores/*.pem
+
+# Editor swap / backup files
+*.swp
+*.swo
+*~
+\#*\#
+.#*
+
+# Terraform (if used for infra)
+*.tfstate
+*.tfstate.backup
+.terraform/
diff --git a/.run/APIServerStarter.run.xml b/.run/APIServerStarter.run.xml
deleted file mode 100644
index 9b77ef4b3b4..00000000000
--- a/.run/APIServerStarter.run.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.run/AgentServiceApplication.run.xml b/.run/AgentServiceApplication.run.xml
deleted file mode 100644
index c220f66317c..00000000000
--- a/.run/AgentServiceApplication.run.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.run/FileServerApplication.run.xml b/.run/FileServerApplication.run.xml
deleted file mode 100644
index dd328679703..00000000000
--- a/.run/FileServerApplication.run.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.run/JobEngineStarter.run.xml b/.run/JobEngineStarter.run.xml
deleted file mode 100644
index 540bbd195bb..00000000000
--- a/.run/JobEngineStarter.run.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.run/JobMonitorStarter.run.xml b/.run/JobMonitorStarter.run.xml
deleted file mode 100644
index 4d25f476a6b..00000000000
--- a/.run/JobMonitorStarter.run.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.run/ResearchServiceApplication.run.xml b/.run/ResearchServiceApplication.run.xml
deleted file mode 100644
index dbec1478021..00000000000
--- a/.run/ResearchServiceApplication.run.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
deleted file mode 100644
index 9a39c30cdcc..00000000000
--- a/.vscode/launch.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "configurations": [
- {
- "type": "java",
- "name": "Start API Server",
- "request": "launch",
- "cwd": "${workspaceFolder}/modules/ide-integration",
- "mainClass": "org.apache.airavata.ide.integration.APIServerStarter",
- "vmArgs": "-javaagent:/home/vscode/.m2/repository/org/apache/openjpa/openjpa/4.0.1/openjpa-4.0.1.jar",
- },
- {
- "type": "java",
- "name": "Start Job Engine",
- "request": "launch",
- "cwd": "${workspaceFolder}/modules/ide-integration",
- "mainClass": "org.apache.airavata.ide.integration.JobEngineStarter"
- },
- {
- "type": "java",
- "name": "Start Job Monitor",
- "request": "launch",
- "cwd": "${workspaceFolder}/modules/ide-integration",
- "mainClass": "org.apache.airavata.ide.integration.JobMonitorStarter"
- }
- ],
-}
\ No newline at end of file
diff --git a/DEPLOY b/DEPLOY
deleted file mode 100644
index 57add712862..00000000000
--- a/DEPLOY
+++ /dev/null
@@ -1,33 +0,0 @@
-## Build Server
-
-```bash
-
-# build from source
-mvn clean install -DskipTests
-
-# prepare the distribution directory for upload
-cp -r dev-tools/deployment-scripts/ distribution
-cp -r vault/ distribution/vault
-
-# define hostname and basepath
-HOSTNAME=exouser@api.dev.cybershuttle.org
-BASEPATH="~/airavata-deployment/airavata-services-v2"
-
-# upload the distribution directory
-scp -r distribution/* $HOSTNAME:$BASEPATH
-
-```
-
-## Deployment Server
-```bash
-
-BASEPATH=$HOME/airavata-deployment/airavata-services-v2
-cd $BASEPATH
-
-./services_down.sh
-./distribution_update.sh
-./services_up.sh
-
-multitail apache-airavata-*/logs/*.log
-
-```
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 35b7e320631..00000000000
--- a/Dockerfile
+++ /dev/null
@@ -1,110 +0,0 @@
-# =============================================================================
-# Combines all Airavata services into one monolithic container
-# =============================================================================
-
-FROM eclipse-temurin:17-jre-jammy
-
-# Install necessary packages including multitail for log monitoring
-RUN apt-get update && apt-get install -y \
- curl \
- netcat-openbsd \
- multitail \
- && rm -rf /var/lib/apt/lists/*
-
-# Create non-root user for security
-RUN groupadd -r airavata && useradd -r -g airavata -d /opt/airavata airavata
-
-# Set working directory
-WORKDIR /opt/airavata
-
-# Copy all distribution files
-COPY distribution/apache-airavata-api-server-*.tar.gz ./
-COPY distribution/apache-airavata-agent-service-*.tar.gz ./
-COPY distribution/apache-airavata-research-service-*.tar.gz ./
-COPY distribution/apache-airavata-file-server-*.tar.gz ./
-
-# Extract all services
-RUN tar -xzf apache-airavata-api-server-*.tar.gz && \
- tar -xzf apache-airavata-agent-service-*.tar.gz && \
- tar -xzf apache-airavata-research-service-*.tar.gz && \
- tar -xzf apache-airavata-file-server-*.tar.gz && \
- rm *.tar.gz
-
-# Rename directories for consistency
-RUN mv apache-airavata-api-server-* apache-airavata-api-server && \
- mv apache-airavata-agent-service-* apache-airavata-agent-service && \
- mv apache-airavata-research-service-* apache-airavata-research-service && \
- mv apache-airavata-file-server-* apache-airavata-file-server
-
-# Create necessary directories
-RUN mkdir -p apache-airavata-api-server/conf \
- apache-airavata-api-server/logs \
- apache-airavata-api-server/temp \
- apache-airavata-api-server/data \
- apache-airavata-api-server/keystores
-
-# Set environment variables
-ENV AIRAVATA_HOME=/opt/airavata/apache-airavata-api-server
-ENV AIRAVATA_AGENT_HOME=/opt/airavata/apache-airavata-agent-service
-ENV AIRAVATA_RESEARCH_HOME=/opt/airavata/apache-airavata-research-service
-ENV AIRAVATA_FILE_HOME=/opt/airavata/apache-airavata-file-server
-ENV JAVA_HOME=/usr/lib/jvm/temurin-17-jre
-ENV PATH="${JAVA_HOME}/bin:${PATH}"
-
-# JVM tuning for production
-ENV JAVA_OPTS="-server \
- -Xms1g \
- -Xmx4g \
- -XX:+UseG1GC \
- -XX:MaxGCPauseMillis=200 \
- -XX:+HeapDumpOnOutOfMemoryError \
- -XX:HeapDumpPath=/opt/airavata/apache-airavata-api-server/logs \
- -Djava.security.egd=file:/dev/./urandom \
- -Dfile.encoding=UTF-8 \
- -Duser.timezone=UTC"
-
-# Health check
-HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
- CMD curl -f http://localhost:8930/ || exit 1
-
-# sharing registry service
-EXPOSE 7878
-# tunnel service
-EXPOSE 8000
-# tunnel service (ingress)
-EXPOSE 17000
-# file service
-EXPOSE 8050
-# api service
-EXPOSE 8930
-# cred store service
-EXPOSE 8960
-# profile service
-EXPOSE 8962
-# registry service
-EXPOSE 8970
-# agent service (http)
-EXPOSE 18800
-# agent service (gRPC)
-EXPOSE 19900
-# research service (http)
-EXPOSE 18889
-# research service (gRPC)
-EXPOSE 19908
-# monitoring
-EXPOSE 9097
-# rest proxy (commented out as restproxy distribution is not available)
-# EXPOSE 8082
-
-# Copy startup script
-COPY dev-tools/deployment-scripts/docker-startup.sh /opt/airavata/start.sh
-
-# Set ownership
-RUN chown -R airavata:airavata /opt/airavata && \
- chmod +x /opt/airavata/start.sh
-
-# Switch to non-root user
-USER airavata
-
-# Set entrypoint
-ENTRYPOINT ["/opt/airavata/start.sh"]
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index 40ed287e87d..00000000000
--- a/INSTALL
+++ /dev/null
@@ -1,32 +0,0 @@
-For detailed installation and further instructions please refer http://airavata.apache.org/:
-Documentation section in left hand panel. The website lists step by step instructions
-
-Installing Apache Airavata 0.17
-------------------------------------------------
-
-Prerequisites
--------------
-Java 1.8 or later
-Maven 3 or later
-
-Build Apache Airavata from Source
----------------------------------
-* Unzip/untar the source file or clone from git.
-* cd to project folder and type
- $ mvn clean install
- Note: in order to skip tests use the command
- $ mvn clean install -Dmaven.test.skip=true
-* Alternatively, all compressed binary distributions can be found at /modules/distribution/release/target/release-artifacts
-
-Running Tests
--------------
-* Unit tests & integrations tests will run while Apache Airavata is built from source (without "-Dmaven.test.skip=true").
-* To run the test samples
- - You can find the binary distributions at /modules/distribution/release/target/release-artifacts or from
- the Apache Airavata download site.
- - Extract the binary distributions and once the binary is unzipped, instructions to run the tests should be followed
- from README files found within.
-
-Tutorials
-----------
-The airavata documentation has instructions for basic tutorials at https://airavata.readthedocs.io/en/latest/
\ No newline at end of file
diff --git a/NOTICE b/NOTICE
index cc1e90f6fd2..8fb4aecba86 100644
--- a/NOTICE
+++ b/NOTICE
@@ -4,233 +4,3 @@ This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
Copyright 2019 The Apache Software Foundation
-
-========================================================================================================================
- COPYRIGHT NOTICES FOR DEPENDENCIES BUNDLED WITH THE APACHE AIRAVATA DISTRIBUTION
-========================================================================================================================
-
-Dom4j Notice (dom4j-1.6.1.jar):
-
-- Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
-
- Redistribution and use of this software and associated documentation
- ("Software"), with or without modification, are permitted provided
- that the following conditions are met:
-
- 1. Redistributions of source code must retain copyright
- statements and notices. Redistributions must also contain a
- copy of this document.
-
- 2. Redistributions in binary form must reproduce the
- above copyright notice, this list of conditions and the
- following disclaimer in the documentation and/or other
- materials provided with the distribution.
-
- 3. The name "DOM4J" must not be used to endorse or promote
- products derived from this Software without prior written
- permission of MetaStuff, Ltd. For written permission,
- please contact dom4j-info@metastuff.com.
-
- 4. Products derived from this Software may not be called "DOM4J"
- nor may "DOM4J" appear in their names without prior written
- permission of MetaStuff, Ltd. DOM4J is a registered
- trademark of MetaStuff, Ltd.
-
- 5. Due credit should be given to the DOM4J Project -
- http://www.dom4j.org
-
- THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
- ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
- NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- OF THE POSSIBILITY OF SUCH DAMAGE.
-
-========================================================================================================================
-
-Ganymed SSH-2 for Java Notice (ganymed-ssh2-262.jar):
-
-- Copyright (c) 2006 - 2013 Christian Plattner. All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- a.) Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- b.) Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- c.) Neither the name of Christian Plattner nor the names of its contributors may
- be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-
-========================================================================================================================
-
-Hamcrest Notcie (hamcrest-all-1.1.jar, hamcrest-core-1.3.jar):
-
-- BSD License
-
- Copyright (c) 2000-2006, www.hamcrest.org
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- Redistributions of source code must retain the above copyright notice, this list of
- conditions and the following disclaimer. Redistributions in binary form must reproduce
- the above copyright notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the distribution.
-
- Neither the name of Hamcrest nor the names of its contributors may be used to endorse
- or promote products derived from this software without specific prior written
- permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
- DAMAGE.
-
-========================================================================================================================
-
-The Jaxen XPath Engine for Java Notice (jaxen-1.1.1.jar):
-
-- Copyright 2003-2006 The Werken Company. All Rights Reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the Jaxen Project nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-========================================================================================================================
-
-JDOM Notice (jdom-1.0.jar):
-
-- Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions, and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions, and the disclaimer that follows
- these conditions in the documentation and/or other materials
- provided with the distribution.
-
- 3. The name "JDOM" must not be used to endorse or promote products
- derived from this software without prior written permission. For
- written permission, please contact .
-
- 4. Products derived from this software may not be called "JDOM", nor
- may "JDOM" appear in their name, without prior written permission
- from the JDOM Project Management .
-
- In addition, we request (but do not require) that you include in the
- end-user documentation provided with the redistribution and/or in the
- software itself an acknowledgement equivalent to the following:
- "This product includes software developed by the
- JDOM Project (http://www.jdom.org/)."
- Alternatively, the acknowledgment may be graphical using the logos
- available at http://www.jdom.org/images/logos.
-
- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
- This software consists of voluntary contributions made by many
- individuals on behalf of the JDOM Project and was originally
- created by Jason Hunter and
- Brett McLaughlin . For more information
- on the JDOM Project, please see .
-
-
-========================================================================================================================
-
-Stax2 API Notice (stax2-api-3.1.1.jar):
-
-- Copyright (c) 2004-2010, Woodstox Project (http://woodstox.codehaus.org/)
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- 3. Neither the name of the Woodstox XML Processor nor the names
- of its contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-
-========================================================================================================================
\ No newline at end of file
diff --git a/README.md b/README.md
index 15358409444..f5037b5b7d2 100644
--- a/README.md
+++ b/README.md
@@ -5,336 +5,632 @@
[](https://opensource.org/licenses/Apache-2.0)
[](https://github.com/apache/airavata/graphs/contributors)
-Apache Airavata is a software framework for executing and managing computational jobs on distributed computing resources including local clusters, supercomputers, national grids, academic and commercial clouds. Airavata builds on general concepts of service oriented computing, distributed messaging, and workflow composition and orchestration. Airavata bundles a server package with an API, client software development Kits and a general purpose reference UI implementation.
+Airavata runs and manages computational jobs and workflows on clusters, supercomputers, grids, and clouds. Submit jobs, monitor processes, automate workflows, transfer data, schedule tasks, and retrieve outputs. Use the Airavata Portal, the Python SDK, or REST and gRPC APIs.
+
+## Components
+
+| Component | Description | Details |
+|-----------|-------------|---------|
+| [**airavata-api**](airavata-api/) | Java/Spring Boot API server — REST, gRPC, orchestration, Temporal workflows, credential store, IAM. All services in a single JVM. | [README](airavata-api/README.md) |
+| [**airavata-portal**](airavata-portal/) | Next.js 14 web portal — dashboard, experiments, applications, storage, credentials, administration. | [README](airavata-portal/README.md) |
+| [**airavata-agent**](airavata-agent/) | Go binary deployed to remote compute resources. Executes workloads, manages Jupyter sessions, sets up TCP tunnels. The API server SCPs this binary to remote servers on demand. | [README](airavata-agent/README.md) |
+| [**airavata-python-sdk**](airavata-python-sdk/) | Python SDK for programmatic experiment submission, data management, and Jupyter magic annotations. | [README](airavata-python-sdk/README.md) |
## Key Features
-- 🔧 Service-oriented architecture with distributed messaging
-- 🔄 Fully-managed task lifecycle (environment setup, data staging, execution, and output retrieval)
-- ☁️ Multi-cloud and hybrid cloud support
-- 🖥️ Comprehensive API and SDK ecosystem
-- 🌐 [Reference UI Implementations](https://github.com/apache/airavata-portals)
+- Task lifecycle: setup env, stage data, run, retrieve output
+- Multi-cloud and hybrid cloud (SLURM, AWS EC2, local)
+- REST and gRPC APIs; Python SDK
+- Web portal for experiment management
+- Remote agent for workload execution on HPC clusters
-## Using Airavata
+## Repository Structure
-If you’re a researcher, Airavata offers several ways to streamline your workflows:
+```
+airavata/
+├── airavata-api/ # Java/Spring Boot API server (4 Maven modules)
+├── airavata-agent/ # Go binary for remote workload execution
+├── airavata-portal/ # Next.js web portal
+├── airavata-python-sdk/ # Python SDK
+├── proto/ # Protocol buffer definitions (gRPC)
+├── conf/ # Shared configuration (DB init, Keycloak setup)
+├── deployment/
+│ ├── ansible/ # Ansible playbooks and roles
+│ ├── k8s/ # Kubernetes manifests (Kustomize)
+│ └── scripts/ # Deployment scripts
+├── .devcontainer/ # Docker Compose for local development
+├── LICENSE
+├── NOTICE
+└── RELEASE_NOTES
+```
-1. Submit Batch Jobs via the **Airavata Application Portal** -- [Example](https://admin.cybershuttle.org)
-2. Launch Interactive Experiments through the **Airavata Research Portal** -- [Example](https://cybershuttle.org)
-3. Explore and run published experiments from the **Airavata Research Catalog** -- [Example](https://cybershuttle.org/resources?resourceTypes=REPOSITORY)
-4. Run interactive computational jobs directly from your IDE using **Airavata Python SDK** -- [PyPI](https://pypi.org/project/airavata-python-sdk/)
+---
+## Quick Start
-## 🧱 The Airavata Ecosystem
+### Prerequisites
-Apache Airavata is composed of modular components spanning core services, data management, user interfaces, and developer tooling.
+| Requirement | Version | Verify |
+|-------------|---------|--------|
+| **Java JDK** | 25+ | `java --version` |
+| **Apache Maven** | 3.8+ | `mvn -v` |
+| **Git** | Latest | `git --version` |
+| **Docker** | 20.10+ | `docker --version` |
+| **Docker Compose** | 2.0+ | `docker compose version` |
-### 🔧 Core Services
-- [`airavata`](https://github.com/apache/airavata) – Main resource management and task orchestration middleware
-- [`airavata-custos`](https://github.com/apache/airavata-custos) – Identity and access management framework
-- [`airavata-mft`](https://github.com/apache/airavata-mft) – Managed file transfer services
-- [`airavata-portals`](https://github.com/apache/airavata-portals) – All frontends for airavata
+Minimum 8GB RAM, 10GB disk. Unix (Linux/macOS) or Windows with WSL2.
-### 📦 Data & Metadata Services
-- [`airavata-data-lake`](https://github.com/apache/airavata-data-lake) – Data lake and storage backend
-- [`airavata-data-catalog`](https://github.com/apache/airavata-data-catalog) – Metadata and search services
+**One-click:** [Dev Container](#dev-container) (Docker only; no local Java/Maven).
-### 📚 Documentation & Branding
-- [`airavata-docs`](https://github.com/apache/airavata-docs) – Developer documentation
-- [`airavata-user-docs`](https://github.com/apache/airavata-user-docs) – End-user guides
-- [`airavata-admin-user-docs`](https://github.com/apache/airavata-admin-user-docs) – Admin-focused documentation
-- [`airavata-custos-docs`](https://github.com/apache/airavata-custos-docs) – Custos documentation
-- [`airavata-site`](https://github.com/apache/airavata-site) – Project website
+### Build and run
-### 🧪 Experimental & Research
-- [`airavata-sandbox`](https://github.com/apache/airavata-sandbox) – Prototypes and early-stage work
-- [`airavata-labs`](https://github.com/apache/airavata-labs) – Experimental projects
-- [`airavata-jupyter-kernel`](https://github.com/apache/airavata-jupyter-kernel) – Jupyter integration
-- [`airavata-cerebrum`](https://github.com/apache/airavata-cerebrum) – Airavata for Neuroscience
+```bash
+# Clone and enter repo
+git clone https://github.com/apache/airavata.git
+cd airavata/airavata-api
+# Build (runs tests)
+mvn clean install
+# Skip tests: mvn clean install -DskipTests
+# Or: ./scripts/build.sh
+
+# Run (builds if needed, inits Docker+Keycloak+DB, starts server; first run ~5–10 min)
+./scripts/run.sh
+# Credentials: default-admin / admin123
+
+# --- Airavata Portal (optional) ---
+cd ../airavata-portal
+npm install
+cp .env.example .env.local
+# Edit .env.local: KEYCLOAK_CLIENT_SECRET (from ../.devcontainer/dev.env.defaults), API_URL, KEYCLOAK_ISSUER, NEXTAUTH_*
+npm run dev
+
+# --- Alternatives ---
+# Manual init then start:
+./scripts/init.sh [--clean] [--run]
+./scripts/dev.sh serve
+# Cold start / full reset (tear down, bring up stack, start):
+./scripts/init.sh --clean --run
+# Tear down only:
+docker compose -f ../.devcontainer/compose.yml down -v
+```
-## 🔄 How Airavata Works
+### Dev Container
-Airavata is composed as 4 top-level services that work together to facilitate the full lifecycle of computational jobs.
+VS Code/Cursor: "Reopen in Container." Run `./scripts/run.sh` inside.
-
+### Troubleshooting
-### 1. Airavata API Server `(apache-airavata-api-server)`
+- **Database init failed:** Ensure `./scripts/dev.sh init` runs (check Flyway logs).
+- **Keycloak login 400/409:** Run `./scripts/init.sh --clean` to reset.
+- **Port in use:** See [Troubleshooting](#troubleshooting) below.
-The Airavata API Server bootstraps the services needed to run/monitor computational jobs, access/share results of computational runs, and manage fine-grained access to computational resources.
+---
+## Reference
-#### Orchestrator
-> Class Name: `org.apache.airavata.server.ServerMain`
-> Command: `bin/orchestrator.sh`
+### CLI commands
-The Orchestrator spins up 7 servers (of type `org.apache.airavata.common.utils.IServer`) for external clients to run computational jobs from.
+From `airavata-api/`:
-- **API** - public-facing API consumed by Airavata SDKs and dashboards. It bridges external clients and internal services, and is served over Thrift.
- (`org.apache.airavata.api.server.AiravataAPIServer`)
-- **DB Event Manager** - Monitors task execution events (launch, transitions, completion/failure) and syncs them to the Airavata DB via pub/sub hooks.
- (`org.apache.airavata.db.event.manager.DBEventManagerRunner`)
-- **Registry** - Manages metadata and definitions for executable tasks and applications.
- (`org.apache.airavata.registry.api.service.RegistryAPIServer`)
-- **Credential Store** - Manages secure storage and retrieval of credentials for accessing registered compute resources.
- (`org.apache.airavata.credential.store.server.CredentialStoreServer`)
-- **Sharing Registry** - Handles sharing and permissioning of Airavata resources between users and groups.
- (`org.apache.airavata.sharing.registry.server.SharingRegistryServer`)
-- **Orchestrator** - Constructs workflow DAGs, assigns unique IDs to tasks, and hands them off to the workflow manager.
- (`org.apache.airavata.orchestrator.server.OrchestratorServer`)
-- **Profile** - Manages users, tenants, compute resources, and group profiles.
- (`org.apache.airavata.service.profile.server.ProfileServiceServer`)
+```bash
+./scripts/dev.sh [options] # dev mode (hot reload, optional --debug)
+./scripts/jar.sh [options] # JAR mode (default command: serve)
+```
-#### Controller
-> Class Name: `org.apache.airavata.helix.impl.controller.HelixController`
-> Command: `bin/controller.sh`
+Examples: `./scripts/dev.sh serve`, `./scripts/dev.sh --debug serve`, `./scripts/jar.sh serve`, `./scripts/dev.sh --help`.
-The Controller manages the step-by-step transition of task state on *helix-side*. It uses Apache Helix to track step start, completion, and failure paths, ensuring the next step starts upon successful completion or retrying the current step on failure.
+Or from `airavata-api/modules/distribution`: `mvn exec:java -Dexec.args=" [options]"`, or `./scripts/dev.sh serve`.
-
+When using the distribution bundle (tarball or fat JAR):
-#### Participant
-> Class Name: `org.apache.airavata.helix.impl.participant.GlobalParticipant`
-> Command: `bin/participant.sh`
+```bash
+./bin/airavata.sh [options]
+# Or: java -Dairavata.home=... -Dairavata.config.dir=... -jar airavata-*.jar [options]
+```
-The participant synchronizes the *helix-side* state transition of a task with its concrete execution at *airavata-side*. The currently registered steps are: `EnvSetupTask`, `InputDataStagingTask`, `OutputDataStagingTask`, `JobVerificationTask`, `CompletingTask`, `ForkJobSubmissionTask`, `DefaultJobSubmissionTask`, `LocalJobSubmissionTask`, `ArchiveTask`, `WorkflowCancellationTask`, `RemoteJobCancellationTask`, `CancelCompletingTask`, `DataParsingTask`, `ParsingTriggeringTask`, and `MockTask`.
+### Main Commands
+
+| Command | Description |
+|---------|-------------|
+| `--help` | Show help |
+| `--version` | Show version |
+| `init` | Initialize databases |
+| `init --clean` | Drop and recreate all databases |
+| `serve` | Start in foreground (default) |
+| `serve -d` | Start in background (daemon) |
+| `serve --dev` | Start in foreground with hot-reload (DevTools) |
+
+### Management Commands
+
+| Command | Description |
+|---------|-------------|
+| `account --help` | Manage accounts |
+| `project --help` | Manage projects |
+| `compute --help` | Manage compute resources |
+| `storage --help` | Manage storage resources |
+| `group --help` | Manage groups |
+| `application --help` | Manage applications |
+| `service status` | Check service status |
+| `test run` | Run tests |
+
+### Server ports
+
+| Server | Port | Config property |
+|--------|------|-----------------|
+| HTTP (REST, File, Agent, Research API) | 8090 | `server.port` |
+| gRPC (agent/research streams) | 9090 | `spring.grpc.server.port` |
+| Temporal (workflow engine) | 7233 | `spring.temporal.connection.target` |
-#### Email Monitor
-> Class Name: `org.apache.airavata.monitor.email.EmailBasedMonitor`
-> Command: `bin/email-monitor.sh`
+---
-The email monitor periodically checks an email inbox for job status updates sent via email. If it reads a new email with a job status update, it relays that state-change to the internal MQ (KafkaProducer).
+## Architecture
+
+All services run in one JVM (`AiravataServer`) and handle the full job lifecycle.
+
+```mermaid
+flowchart TB
+ subgraph Clients["External Clients"]
+ Portals["Airavata Portal / SDKs"]
+ Agents["Agents"]
+ end
+
+ subgraph Server["Airavata Server (single JVM)"]
+ HTTP["HTTP Server :8090 REST API + File API"]
+ gRPC["gRPC Server :9090 Agent Streams"]
+ Orch["OrchestratorService"]
+ PA["ProcessActivity (Pre/Post/Cancel/Parsing workflows)"]
+ end
+
+ subgraph Temporal["Temporal :7233"]
+ Workflows["Durable Workflows (Pre/Post/Cancel/Parsing)"]
+ Activities["Activities (EnvSetup, InputStaging, JobSubmission, OutputStaging)"]
+ end
+
+ subgraph External["Infrastructure"]
+ DB[(MariaDB)]
+ KC[Keycloak]
+ Compute["Compute Clusters (SSH/SSHJ)"]
+ Storage["Storage Resources"]
+ end
+
+ Portals --> HTTP
+ Agents --> gRPC
+ HTTP --> Orch
+ Orch --> PA
+ PA --> Workflows
+ Workflows --> Activities
+ Activities -->|SSH/SSHJ| Compute
+ Activities -->|SSH/SSHJ| Storage
+ Server --> DB
+ Server --> KC
+```
-#### Realtime Monitor
-> Class Name: `org.apache.airavata.monitor.realtime.RealtimeMonitor`
-> Command: `bin/realtime-monitor.sh`
+### System overview
+
+```mermaid
+flowchart LR
+ subgraph Clients["External Clients"]
+ direction TB
+ C["Airavata Portal, SDKs, Agents, Monitoring"]
+ end
+
+ subgraph Servers["API Servers"]
+ direction TB
+ HTTP["HTTP :8090"]
+ gRPC["gRPC :9090"]
+ end
+
+ subgraph App["Internal Services (single JVM)"]
+ direction LR
+ O["Orchestrator"]
+ Ex["Execution (ProcessActivity)"]
+ Res["Research"]
+ S["IAM/Sharing"]
+ Cr["Credential Store"]
+ end
+
+ subgraph Infra["Infrastructure"]
+ direction TB
+ MariaDB[(MariaDB)]
+ Temporal[Temporal]
+ Keycloak[Keycloak]
+ end
+
+ Clients --> Servers
+ Servers --> App
+ App --> Infra
+```
-The realtime monitor listens to incoming state-change messages on the internal MQ (KafkaConsumer), and relays that state-change to the internal MQ (KafkaProducer). When a task is completed at the compute resource, the realtime monitor is notified of this.
+### API and workflows
-#### Pre Workflow Manager
-> Class Name: `org.apache.airavata.helix.impl.workflow.PreWorkflowManager`
-> Command: `bin/pre-wm.sh`
+- **HTTP (8090):** REST at `/api/v1/`, File API at `/api/v1/files/`, Agent at `/api/v1/agents/`, Research at `/api/v1/research/`.
+- **gRPC (9090):** Bidirectional streaming for agents and research.
+- **Temporal workflows:** ProcessActivity with PreWf, PostWf, CancelWf, ParsingWf (orchestration and activities via ProcessDAGEngine). In-process event delivery for status changes.
+- **Internal:** Orchestrator, Execution Engine, Research Services, Credential Store, IAM/Sharing, Workflow Managers. Schema and entities: see [airavata-api/README.md](airavata-api/README.md#database-erd).
-The pre-workflow manager listens on the internal MQ (KafkaConsumer) to inbound tasks at **pre-execution** phase. When a task DAG is received, it handles the environment setup and data staging phases of the DAG in a robust manner, which includes fault-handling. All these happen BEFORE the task DAG is submitted to the controller, and subsequently to the participant.
+---
-#### Post Workflow Manager
-> Class Name: `org.apache.airavata.helix.impl.workflow.PostWorkflowManager`
-> Command: `bin/post-wm.sh`
+## Internal Code Architecture
+
+The core API (`airavata-api/modules/airavata-api`) is layered.
+
+### Package structure
+
+Domain-first layout where each domain owns its entity, repository, mapper, service, and model:
+
+```text
+org.apache.airavata/
+├── execution/ # Orchestration, scheduling, DAG execution, Temporal activities
+│ ├── activity/ # Temporal workflows (Pre/Post/Cancel) and ActivitiesImpl
+│ ├── orchestration/ # OrchestratorService, ProcessResourceResolver, ValidationService
+│ ├── scheduling/ # ProcessScheduler, ScheduledTaskManager
+│ ├── dag/ # ProcessDAGEngine, TaskNode, interceptors
+│ ├── monitoring/ # JobStatusEventPublisher, email monitoring
+│ ├── state/ # StateValidators, StateModel
+│ ├── service/ # ProcessService interface + DefaultProcessService
+│ ├── entity/ # ProcessEntity, ComputeSubmissionTrackingEntity
+│ ├── repository/ # ProcessRepository
+│ ├── mapper/ # ProcessMapper
+│ ├── model/ # ProcessModel
+│ ├── event/ # LocalStatusEvent
+│ └── task/ # TaskContext
+├── research/ # Research domain grouping
+│ ├── experiment/ # Experiments: entity/repo/mapper/service/model/util
+│ ├── application/ # App catalog: entity/repo/mapper/service/model/adapter
+│ ├── artifact/ # Research artifacts: entity/repo/service/model/dto
+│ ├── project/ # Research projects: entity/repo/service/dto
+│ └── session/ # User sessions: entity/repo/service/model/dto
+├── compute/ # Compute resources and job execution
+│ ├── resource/ # Resource models/services/entities/repos + job submission infra
+│ └── provider/ # Backend impls: slurm/, local/, aws/ (each has a ComputeProvider)
+├── storage/ # Data staging
+│ ├── resource/ # Storage models (enums, POJOs)
+│ └── client/ # StorageClient interface + SftpStorageClient (sftp/)
+├── status/ # Event and status management
+│ ├── entity/ # EventEntity
+│ ├── repository/ # StatusRepository
+│ ├── mapper/ # StatusMapper
+│ ├── service/ # StatusService
+│ └── model/ # Status models
+├── protocol/ # Transport adapters (SSH, streaming)
+├── iam/ # Keycloak, sharing, authorization
+├── gateway/ # Multi-tenant gateway management
+├── credential/ # Credential store
+├── accounting/ # Allocation project accounting
+├── workflow/ # Workflow DAG definitions (not execution)
+├── core/ # Cross-cutting: EntityMapper, CrudService, exceptions, utils
+│ ├── mapper/ # EntityMapper generic contract
+│ ├── service/ # CrudService, AbstractCrudService
+│ ├── exception/ # Common exceptions
+│ ├── model/ # Shared model types
+│ ├── util/ # IdGenerator, PaginationUtil
+│ └── telemetry/ # Observability
+└── config/ # Spring configuration
+```
-The post-workflow listens on the internal MQ (KafkaConsumer) to inbound tasks at **post-execution** phase. Once a task is received, it handles the cleanup and output fetching phases of the task DAG in a robust manner, which includes fault-handling. Once the main task completes executing, this is announced to the realtime monitor, upon which the post-workflow phase is triggered. Once triggered, it submits this state change to the controller.
+### Data layer
+
+Domain-driven data layer where each package owns its entity, repository, mapper, and service:
+
+```mermaid
+flowchart LR
+ subgraph Service["Service Layer"]
+ direction TB
+ S1["ExperimentService, ProcessService, ResourceService, etc."]
+ S2["Domain services with @Transactional support"]
+ S3["AbstractCrudService eliminates per-domain boilerplate"]
+ S1 --> S2 --> S3
+ end
+
+ subgraph Mapper["Mapper Layer"]
+ direction TB
+ M1["EntityMapper<E,M> generic contract (core/mapper/)"]
+ M2["MapStruct mappers (Resource, Application, Project, etc.)"]
+ M3["Hand-written mappers (Experiment, Process)"]
+ M1 --> M2 & M3
+ end
+
+ subgraph Repo["Repository Layer"]
+ direction TB
+ R1["Spring Data JpaRepository per domain"]
+ R2["Domain-owned: execution/repository/, research/experiment/repository/, etc."]
+ R1 --> R2
+ end
+
+ subgraph Entity["Entity Layer"]
+ direction TB
+ E1["JPA entities per domain package"]
+ E2["execution/entity/, research/experiment/entity/, status/entity/, etc."]
+ E1 --> E2
+ end
+
+ subgraph DB["Unified Database (MariaDB)"]
+ D1["Single schema, Flyway migrations"]
+ end
+
+ Service --> Mapper --> Repo --> Entity --> DB
+```
-
+**Schema and services:** Each domain package owns its entities, repositories, mappers, and services (e.g. `research/experiment/entity/`, `execution/entity/`, `status/entity/`). Generic DRY infrastructure in `core/` (`EntityMapper`, `CrudService`, `AbstractCrudService`) eliminates boilerplate. MapStruct maps entities to domain models. Schema: see [airavata-api/README.md](airavata-api/README.md#database-erd).
+---
-### 2. Airavata File Server `(apache-airavata-file-server)`
-> Class Name: `org.apache.airavata.file.server.FileServerApplication`
-> Command: `bin/
+## Configuration
-The Airavata File Server is a lightweight SFTP wrapper running on storage nodes integrated with Airavata. It lets users securely access storage via SFTP, using Airavata authentication tokens as ephemeral passwords.
+- **Paths:** Home via `--home` or `AIRAVATA_HOME`; config via `--config-dir` or `AIRAVATA_CONFIG_DIR` (default `{home}/conf`); logs under home.
+- **Files:** `application.properties`, `keystores/airavata.sym.p12`, `logback.xml` in config dir.
+- **Agent tunnel (optional):** `airavata.services.agent.tunnelserver.host`, `.port`, `.url`, `.token` — remote tunnel server; Airavata does not start it.
+---
-### 3. Airavata Agent Service `(apache-airavata-agent-service)` [NEW]
-> Class Name: `org.apache.airavata.agent.connection.service.AgentServiceApplication`
+## Deployment
-The Airavata Agent Service is the backend for launching **interactive** jobs using Airavata.
-It provide constructs to launch a custom "Agent" on a compute resource, that connects back to the Agent Service through a bi-directional gRPC channel.
-The Airavata Python SDK primarily utilizes the Agent Service (gRPC) and the Airavata API (Thrift) to submit and execute interactive jobs, spawn subprocesses, and create network tunnels to subprocesses, even if they are behind NAT.
+Bundle layout:
+```
+airavata-0.21-SNAPSHOT/
+├── bin/airavata.sh
+├── lib/*.jar
+├── conf/ (keystores, *.properties, *.yml, templates/)
+├── logs/
+├── LICENSE, NOTICE, RELEASE_NOTES
+```
-### 4. Airavata Research Service `(apache-airavata-research-service)` [NEW]
-> Class Name: `org.apache.airavata.research.service.ResearchServiceApplication`
+### Tarball or Fat JAR
-The Airavata Research Service is the backend for the **research catalog** in Airavata. It provides the API to add, list, modify, and publish notebooks, repositories, datasets, and computational models in cybershuttle, and launch interactive remote sessions to utilize them in a research setting.
+**From tarball:**
+```bash
+cd distribution
+tar -xzf airavata-0.21-SNAPSHOT.tar.gz
+cd airavata-0.21-SNAPSHOT
+
+# Copy configuration files into conf/
+cp ../../conf/application.properties conf/
+cp ../../conf/airavata.sym.p12 conf/keystores/
+cp ../../conf/*.yml conf/
+cp ../../conf/logback.xml conf/
+
+# Start (default: AIRAVATA_HOME = parent of bin/, config = AIRAVATA_HOME/conf)
+./bin/airavata.sh -d start # daemon mode
+./bin/airavata.sh # foreground mode
+
+# Or pass home and config dir as arguments
+./bin/airavata.sh --home /opt/apache-airavata --config-dir /etc/airavata/conf -d start
+./bin/airavata.sh --home /opt/apache-airavata --config-dir /etc/airavata/conf
+```
-## 🏗️ Getting Started
+**Fat JAR:** Put the JAR in a directory. Use `bin/airavata.sh` (sets `-Dairavata.home` and `-Dairavata.config.dir` from `--home`/`--config-dir` or env) or run Java directly:
+```bash
+# Script sets home/config for you
+./bin/airavata.sh --home /path/to/install --config-dir /path/to/conf -d start
-### Option 1 - Build from Source
+# Or run Java directly (set airavata.home and airavata.config.dir)
+java -Dairavata.home=/path/to/install -Dairavata.config.dir=/path/to/conf -jar airavata-0.21-SNAPSHOT.jar serve
+```
-Before setting up Apache Airavata, ensure that you have:
+**Stop/Restart:** same script with `-d stop` / `-d restart` (use the same `--home`/`--config-dir` or env if you used them to start).
-| Requirement | Version | Check Using |
-|-------------|---------|-------|
-| **Java SDK** | 17+ | `java --version` |
-| **Apache Maven** | 3.8+ | `mvn -v` |
-| **Git** | Latest | `git -v` |
+**Paths:** Home = install root (`--home` or `AIRAVATA_HOME`). Config dir = `application.properties`, `logback.xml`, etc. (`--config-dir` or `AIRAVATA_CONFIG_DIR`; default `{home}/conf`). Logs under home.
+
+### Shared Configuration
+
+Shared config lives in `conf/` at the repo root — used by Docker, Ansible, and K8s:
-First, clone the project repository from GitHub.
-```bash
-git clone https://github.com/apache/airavata.git
-cd airavata
```
+conf/
+├── init-db/01-create-databases.sql # DB init (airavata + keycloak schemas)
+└── keycloak/
+ ├── keycloak.conf # Keycloak server config
+ └── setup-keycloak.sh # Realm/client provisioning script
+```
+
+### Service Hostnames (local deployment)
+
+| Service | Hostname | Port |
+|---------|----------|------|
+| Portal | `airavata.localhost` | 3000 |
+| API Server | `api.airavata.localhost` | 8090 |
+| Keycloak | `auth.airavata.localhost` | 8080 |
+| Temporal UI | `temporal.airavata.localhost` | 8233 |
+| MariaDB | `db.airavata.localhost` | 3306 |
+| gRPC | `grpc.airavata.localhost` | 9090 |
+
+### Docker
-Next, build the project using Maven.
```bash
-# with tests (slower, but safer)
-mvn clean install
-# OR without tests (faster)
-mvn clean install -DskipTests
+# Build API server distribution + Docker image (includes agent binary)
+cd airavata-api && mvn clean install -DskipTests && cd ..
+cd airavata-agent && GOOS=linux GOARCH=amd64 go build -o ../airavata-api/modules/distribution/target/airavata-agent && cd ..
+docker build -t airavata:latest -f airavata-api/modules/distribution/src/main/docker/Dockerfile airavata-api/modules/distribution/target
+
+docker run -p 8090:8090 -p 9090:9090 airavata:latest
```
-Once the project is built, four `tar.gz` bundles will be generated in the `./distributions` folder.
+### Kubernetes
+
```bash
-├── apache-airavata-agent-service-0.21-SNAPSHOT.tar.gz
-├── apache-airavata-api-server-0.21-SNAPSHOT.tar.gz
-├── apache-airavata-file-server-0.21-SNAPSHOT.tar.gz
-└── apache-airavata-research-service-0.21-SNAPSHOT.tar.gz
+cd deployment/k8s
+
+# Apply all manifests
+kubectl apply -k .
+
+# Or apply individually
+kubectl apply -f namespace.yaml
+kubectl apply -f configmap.yaml -f secrets.yaml
+kubectl apply -f mariadb.yaml -f temporal.yaml -f keycloak.yaml
+kubectl apply -f apiserver.yaml -f portal.yaml
+kubectl apply -f ingress.yaml
-1 directory, 4 files
+# Access at: http://airavata.localhost (portal), http://api.airavata.localhost (API)
```
-Next, copy the deployment scripts and configurations into the `./distributions` folder.
+Requires nginx ingress controller and `/etc/hosts` entries. See [deployment/k8s/](deployment/k8s/).
+
+### Ansible (production)
```bash
-cp -r dev-tools/deployment-scripts/ distribution
-cp -r vault/ distribution/vault
-
-tree ./distribution
-distribution
-├── apache-airavata-agent-service-0.21-SNAPSHOT.tar.gz
-├── apache-airavata-api-server-0.21-SNAPSHOT.tar.gz
-├── apache-airavata-file-server-0.21-SNAPSHOT.tar.gz
-├── apache-airavata-research-service-0.21-SNAPSHOT.tar.gz
-├── distribution_backup.sh
-├── distribution_update.sh
-├── services_down.sh
-├── services_up.sh
-└── vault
- ├── airavata-server.properties
- ├── airavata.sym.p12
- ├── application-agent-service.yml
- ├── application-research-service.yml
- ├── email-config.yml
- └── log4j2.xml
-
-2 directories, 16 files
+cd deployment/ansible
+
+# Local deployment (all services on one machine)
+ansible-playbook -i inventories/local deploy.yml
+
+# Production: copy and customize inventory
+cp -r inventories/template inventories/my-deployment
+# Edit hosts and group_vars/all/vars.yml
+ansible-playbook -i inventories/my-deployment deploy.yml
+
+# Deploy specific services
+ansible-playbook -i inventories/my-deployment deploy.yml --tags database
+ansible-playbook -i inventories/my-deployment deploy.yml --tags temporal
+ansible-playbook -i inventories/my-deployment deploy.yml --tags apiserver
+ansible-playbook -i inventories/my-deployment deploy.yml --tags keycloak
+ansible-playbook -i inventories/my-deployment deploy.yml --tags portal
```
-**What's in the vault?**
+See [deployment/ansible/README.md](deployment/ansible/README.md).
+
+---
+
+## Development
+
+### IDE Setup
+
+Import as Maven project. Java 25+, annotation processing on.
+
+**VS Code:** [launch.json](airavata-api/.vscode/launch.json) (Serve, Serve Debug) and [tasks.json](airavata-api/.vscode/tasks.json) (Init, Build, Test). Run `./scripts/init.sh --clean` from `airavata-api/` before first Serve.
+
+**IntelliJ:** Import as Maven project from `airavata-api/`. Create Run Configurations for Serve and Serve Debug.
-* `airavata.sym.p12` - contains the symmetric key used to secure stored credentials.
-* `airavata-server.properties` - config file for the airavata api server.
-* `application-agent-service.yml` - config file for the airavata agent service.
-* `application-file-server.yml` - config file for the airavata file server.
-* `application-research-service.yml` - config file for the airavata research service.
-* `application-restproxy.properties` - config file for the airavata rest proxy.
-* `email-config.yml` - contains the email addresses observed by the email monitor.
-* `log4j2.xml` - contains the Log4j configuration for all airavata services.
+### Running Tests
-Next, start the services using the deployment scripts.
+With DB, Keycloak, and Temporal up (for integration tests), run:
```bash
-cd distribution
-./distribution_update.sh
-./services_up.sh
+./scripts/init.sh # ensure db, Keycloak, Temporal up (no --clean)
+mvn test -Dskip.slurm.tests=true # all tests, skip SLURM if no cluster
```
-Voila, you are now running Airavata! You can now tail the server logs using `multitail` (all logs) or `tail` (specific logs).
+Without pre-starting services (many tests use Testcontainers):
```bash
-multitail apache-airavata-*/logs/*.log
-
+mvn test # All tests
+mvn test -pl modules/airavata-api # Specific module (from airavata-api/)
+mvn test -Dtest=SomeTestClass # Specific test
```
-### 🐳 Option 2 - Run with Docker (Experimental)
+**Test categories:**
+- **Unit and repository:** Testcontainers (MariaDB). Many pass with `mvn test`; some fail in certain environments.
+- **Workflow/state-machine:** Need Temporal. In CI without Temporal, tests can stay CREATED/SUBMITTED and fail; expected when workflow runtime is off.
+- **SLURM:** Need Docker + SLURM Testcontainer (`csniper/slurm-lab`). Skip: `mvn test -Dskip.slurm.tests=true`. Enable all: `mvn test -P test`.
+- **Credential store:** Need backend (e.g. Vault); may skip or fail if missing.
+- Build without tests: `mvn clean install -DskipTests`.
-> ⚠️ **Note:** Docker deployment is experimental and not recommended for production use.
+### Schema migrations (contributors)
-Before setting up Apache Airavata, ensure that you have:
+Schema: single Flyway baseline at `src/main/resources/conf/db/migration/airavata/V1__Baseline_schema.sql`. JPA via package scanning (no `persistence.xml`). To change schema: (1) Add or update the entity. (2) Add a versioned migration (e.g. `V2__Description.sql`) so DB matches entity; use `IF NOT EXISTS`/`IF EXISTS`. Drop column/table must be done manually. Tarball uses these Flyway scripts only.
-| Requirement | Version | Check Using |
-|-------------|---------|-------|
-| **Java SDK** | 17+ | `java --version` |
-| **Apache Maven** | 3.8+ | `mvn -v` |
-| **Git** | Latest | `git -v` |
-| **Docker Engine** | 20.10+ | `docker -v` |
-| **Docker Compose** | 2.0+ | `docker compose version` |
+**Simplifications:** Child tables merged into JSON (e.g. batch queues → `RESOURCE.capabilities`). TASK table removed (tasks are transient DAG nodes, not persisted). JOB table remains for HPC job tracking with FK to PROCESS. See [airavata-api/README.md](airavata-api/README.md#database-erd).
-In your `/etc/hosts`, point `airavata.host` to `127.0.0.1`:
-```
-127.0.0.1 airavata.host
+---
+
+## Troubleshooting
+
+### Port already in use
+
+```bash
+lsof -i :8090
+kill -9
```
-First, clone the project repository from GitHub.
+### Database connection refused
+
+From the repo root:
+
```bash
-git clone https://github.com/apache/airavata.git
-cd airavata
+docker compose -f .devcontainer/compose.yml ps db
+docker compose -f .devcontainer/compose.yml logs db
+docker compose -f .devcontainer/compose.yml restart db
```
-Next, build the project distribution using Maven.
+### Maven build failures
```bash
-# with tests (slower, but safer)
-mvn clean install
-# OR without tests (faster)
-mvn clean install -DskipTests
+rm -rf ~/.m2/repository/org/apache/airavata
+mvn clean install -DskipTests -U
```
-Next, build the containers and start them through compose.
+### Out of memory
```bash
+export MAVEN_OPTS="-Xmx4g -XX:MaxMetaspaceSize=512m"
+mvn clean install -DskipTests
+```
-# build the containers
-mvn docker:build -pl modules/distribution
+### Logs
-# start containers via compose
-docker-compose \
- -f modules/distribution/src/main/docker/docker-compose.yml \
- up -d
+- Dev: console. Bundle: `airavata-0.21-SNAPSHOT/logs/airavata.log`. Docker: `docker compose logs `
-# check whether services are running
-docker-compose ps
-```
+---
-**Service Endpoints:**
-- **API Server:** `airavata.host:8960`
-- **Profile Service:** `airavata.host:8962`
-- **Keycloak:** `airavata.host:8443`
+## The Airavata Ecosystem
-**Stop Services:**
-```bash
-docker-compose \
- -f modules/ide-integration/src/main/containers/docker-compose.yml \
- -f modules/distribution/src/main/docker/docker-compose.yml \
- down
-```
+### Core
+- [`airavata`](https://github.com/apache/airavata) – Orchestrate resources and tasks
+- [`airavata-custos`](https://github.com/apache/airavata-custos) – IAM
+- [`airavata-mft`](https://github.com/apache/airavata-mft) – File transfer
+- [`airavata-portals`](https://github.com/apache/airavata-portals) – Airavata Portal (Next.js)
+### Data
+- [`airavata-data-lake`](https://github.com/apache/airavata-data-lake) – Data lake and storage
+- [`airavata-data-catalog`](https://github.com/apache/airavata-data-catalog) – Metadata and search
-## 🤝 Contributing
+### Docs
+- [`airavata-docs`](https://github.com/apache/airavata-docs) – Developer docs
+- [`airavata-user-docs`](https://github.com/apache/airavata-user-docs) – User guides
+- [`airavata-site`](https://github.com/apache/airavata-site) – Project site
-We welcome contributions from the community! Here's how you can help:
+### Powered by Airavata
-1. **🍴 Fork the repository**
-2. **🌿 Create a feature branch**
-3. **✨ Make your changes**
-4. **🧪 Add tests if applicable**
-5. **📝 Submit a pull request**
+- [Cybershuttle](https://cybershuttle.org/) – Research computing platform
+- [Bio-realistic multiscale simulations of cortical circuits](https://github.com/cyber-shuttle/allenai-v1)
+- [Computational neuroscience models from brain atlases](https://github.com/cyber-shuttle/airavata-cerebrum)
+- [Large-scale brain model simulations](https://github.com/cyber-shuttle/whole-brain-public)
+- [Neural data analysis with torch_brain](https://github.com/cyber-shuttle/neurodata25_torchbrain_notebooks)
+- [NAMD Workshop examples](https://github.com/cyber-shuttle/namd-workshop-2024)
+- [OpenFold Attention Visualization](https://github.com/vizfold/attention-viz-demo)
-**Learn More:**
-- [Contributing Guidelines](http://airavata.apache.org/get-involved.html)
-- [Code of Conduct](https://www.apache.org/foundation/policies/conduct.html)
-- [Developer Resources](https://cwiki.apache.org/confluence/display/AIRAVATA)
+See more projects at [cybershuttle.org/resources](https://cybershuttle.org/resources).
+
+---
-### Setting up your IDE
+## Contributing
-The easiest way to setup a development environment is to follow the instructions in the [ide-integration README](./modules/ide-integration/README.md).Those instructions will guide you on setting up a development environment with IntelliJ IDEA.
+1. Fork the repo
+2. Create a feature branch
+3. Make your changes
+4. Add tests if applicable
+5. Submit a pull request
-### Additional Tools
+**Resources:**
+- [Contributing Guidelines](http://airavata.apache.org/get-involved.html)
+- [Code of Conduct](https://www.apache.org/foundation/policies/conduct.html)
+- [Developer Wiki](https://cwiki.apache.org/confluence/display/AIRAVATA)
-* `org.apache.airavata.sharing.registry.migrator.airavata.AiravataDataMigrator`
-* `modules/deployment-scripts`
-* `modules/load-client`
+---
+## Community & Support
-## 💬 Community & Support
+- **User Mailing List:** [users@airavata.apache.org](mailto:users@airavata.apache.org)
+- **Developer Mailing List:** [dev@airavata.apache.org](mailto:dev@airavata.apache.org)
+- **All Mailing Lists:** [airavata.apache.org/mailing-list](https://airavata.apache.org/mailing-list.html)
+- **GitHub Issues:** [github.com/apache/airavata/issues](https://github.com/apache/airavata/issues)
-**Get Help:**
-- 📧 **User Mailing List:** [users@airavata.apache.org](mailto:users@airavata.apache.org)
-- 👨💻 **Developer Mailing List:** [dev@airavata.apache.org](mailto:dev@airavata.apache.org)
-- 🔗 **All Mailing Lists:** [airavata.apache.org/mailing-list](https://airavata.apache.org/mailing-list.html)
+---
-## 📄 License
+## License
```
Licensed to the Apache Software Foundation (ASF) under one or more contributor
@@ -353,10 +649,4 @@ CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
```
-See the [LICENSE](LICENSE) file for complete license details.
-
----
-
-
- Made with ❤️ by the Apache Airavata Community
-
+See [LICENSE](LICENSE).
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 38fc99ac0d8..6a408673620 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -3,6 +3,28 @@ Apache Airavata Release Notes
See http://issues.apache.org/jira/browse/AIRAVATA-* (where * is the number of the issue below)
+Release Notes - Airavata - Version 1.0.0-SNAPSHOT (in development)
+
+** Architecture and data layer
+ * Unified distribution: single Spring Boot application with Thrift, HTTP, and gRPC endpoints
+ * Credential-centric access model: credentials linked to compute resources via CredentialComputeConfig; ApplicationDeployment bound to credential configs
+ * Unified database schema: single MariaDB schema with STATUS/ERROR and resource management entities
+ * Entity simplifications: Project and workflow entities no longer hold @OneToMany to resource accounts or status/error; status and error accessed via StatusRepository/ErrorRepository
+ * User profile: minimal research-context profile; demographics and dashboard data managed by Keycloak
+
+** Testing and distribution
+ * Integration test context: mock CredentialStoreService provided so registry tests load without full security stack
+ * Repository tests: ResourceAccessGrantRepositoryTest satisfies CREDENTIALS FK before inserting grants
+ * Documentation: ERD (docs/ERD.md), README quick start, and deployment options updated for distribution-ready use
+
+** Distribution and scripts (0.21)
+ * Distribution: tarball and fat JAR only; GraalVM/native image removed
+ * Launcher: single bin/airavata.sh with -d start|stop|restart; --home and --config-dir (or AIRAVATA_HOME, AIRAVATA_CONFIG_DIR)
+ * Cleanup: unused native-image resources and duplicate slurminfo.sh removed; INSTALL removed from tarball assembly
+ * Scripts: dev-tools/deployment-scripts updated to airavata-0.21-SNAPSHOT and bin/airavata.sh; docs/README.md and scripts/README.md document script and resource locations
+ * Integration test README: start-integration-services.sh removed; use scripts/full-init.sh or docker compose for db/redis
+
+
Release Notes - Airavata - Version 0.17
** Epic
diff --git a/airavata-agent/.dockerignore b/airavata-agent/.dockerignore
new file mode 100644
index 00000000000..e3d17e9e168
--- /dev/null
+++ b/airavata-agent/.dockerignore
@@ -0,0 +1,16 @@
+.git
+.github
+.gitignore
+.claude
+.DS_Store
+*.md
+*.test
+*.prof
+vendor/
+protos/
+application/
+jupyter/
+__pycache__/
+*.pyc
+*.egg-info
+*.sif
diff --git a/airavata-agent/.gitignore b/airavata-agent/.gitignore
new file mode 100644
index 00000000000..0bb0a408594
--- /dev/null
+++ b/airavata-agent/.gitignore
@@ -0,0 +1,21 @@
+# Built binary
+/airavata-agent*
+
+# Go
+vendor/
+*.test
+*.out
+*.prof
+
+# Jupyter / Python
+/jupyter/
+*.egg-info
+*.ipynb_checkpoints
+*.pyc
+__pycache__/
+
+# Singularity
+*.sif
+
+# Docker
+.dockerignore.bak
diff --git a/modules/agent-framework/airavata-agent/Dockerfile b/airavata-agent/Dockerfile
similarity index 100%
rename from modules/agent-framework/airavata-agent/Dockerfile
rename to airavata-agent/Dockerfile
diff --git a/airavata-agent/Makefile b/airavata-agent/Makefile
new file mode 100644
index 00000000000..a932b30e382
--- /dev/null
+++ b/airavata-agent/Makefile
@@ -0,0 +1,159 @@
+# 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.
+
+IMAGE_NAME=cybershuttle/remote-agent-base
+AGENT_SVC_URL=localhost:19900
+AGENT_ID=test
+
+build-multiarch:
+ GOOS=linux GOARCH=amd64 go build -o airavata-agent-linux-amd64; \
+ GOOS=linux GOARCH=arm64 go build -o airavata-agent-linux-arm64; \
+ GOOS=darwin GOARCH=amd64 go build -o airavata-agent-osx-amd64; \
+ GOOS=darwin GOARCH=arm64 go build -o airavata-agent-osx-arm64
+
+build-container:
+ GOOS=linux GOARCH=amd64 go build -o airavata-agent && \
+ docker build --platform linux/amd64 -t $(IMAGE_NAME) . && \
+ docker push $(IMAGE_NAME)
+
+run-container:
+ docker run -it $(IMAGE_NAME) /opt/airavata-agent $(AGENT_SVC_URL) $(AGENT_ID)
+
+deploy-anvil-scigap:
+ ssh x-scigap@anvil \
+ "srun -N1 -n1 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-anvil-waterhub:
+ ssh x-waterhub@anvil \
+ "srun -N1 -n1 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-anvil-gcommunityus:
+ ssh x-gcommunityus@anvil \
+ "srun -N1 -n1 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-bridges2-gcommuni:
+ ssh gcommuni@bridges2 \
+ "srun -N1 -n1 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-delta-svcscigapgwuser:
+ ssh svcscigapgwuser@delta \
+ "srun -N1 -n1 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-expanse-scigap:
+ ssh scigap@expanse \
+ "module load singularitypro && srun -N1 -n1 -A ind123 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-expanse-gridchem:
+ ssh gridchem@expanse \
+ "module load singularitypro && srun -N1 -n1 -A ind123 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-pace-ice-ideas-cybershuttle:
+ ssh ideas-cybershuttle@pace-ice \
+ "srun -N1 -n1 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-pace-phoenix-ideas-cybershuttle:
+ ssh ideas-cybershuttle@pace-phoenix \
+ "srun -N1 -n1 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-stampede3-ccguser:
+ ssh ccguser@stampede3 \
+ "srun -N1 -n1 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-vc-airavata-cpu-exouser:
+ ssh exouser@vc-airavata-cpu \
+ "module load singularity && srun -p cloud -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-vc-airavata-gpu-exouser:
+ ssh exouser@vc-airavata-gpu \
+ "module load singularity && srun -p cloud -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-vc-gkeyll-exouser:
+ ssh exouser@vc-gkeyll \
+ "srun -N1 -n1 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-vc-gkeyll-gateway-user:
+ ssh gateway-user@vc-gkeyll \
+ "srun -N1 -n1 -p shared --mem 4G -t 30 \
+ singularity pull --disable-cache --force \
+ cybershuttle/container/remote-agent-base.sif \
+ docker://$(IMAGE_NAME)"
+
+deploy-bootstrap:
+ scp agent.sh x-scigap@anvil:~/cybershuttle/agent.sh; \
+ scp agent.sh x-waterhub@anvil:~/cybershuttle/agent.sh; \
+ scp agent.sh x-gcommunityus@anvil:~/cybershuttle/agent.sh; \
+ scp agent.sh gcommuni@bridges2:~/cybershuttle/agent.sh; \
+ scp agent.sh svcscigapgwuser@delta:~/cybershuttle/agent.sh; \
+ scp agent.sh scigap@expanse:~/cybershuttle/agent.sh; \
+ scp agent.sh gridchem@expanse:~/cybershuttle/agent.sh; \
+ scp agent.sh ideas-cybershuttle@pace-ice:~/cybershuttle/agent.sh; \
+ scp agent.sh ideas-cybershuttle@pace-phoenix:~/cybershuttle/agent.sh; \
+ scp agent.sh ccguser@stampede3:~/cybershuttle/agent.sh; \
+ scp agent.sh exouser@vc-airavata-cpu:~/cybershuttle/agent.sh; \
+ scp agent.sh exouser@vc-airavata-gpu:~/cybershuttle/agent.sh; \
+ scp agent.sh exouser@vc-gkeyll:~/cybershuttle/agent.sh; \
+ scp agent.sh gateway-user@vc-gkeyll:~/cybershuttle/agent.sh;
+
+deploy-anvil: deploy-anvil-scigap deploy-anvil-waterhub deploy-anvil-gcommunityus
+deploy-bridges2: deploy-bridges2-gcommuni
+deploy-delta: deploy-delta-svcscigapgwuser
+deploy-expanse: deploy-expanse-scigap deploy-expanse-gridchem
+deploy-pace-ice: deploy-pace-ice-ideas-cybershuttle
+deploy-pace-phoenix: deploy-pace-phoenix-ideas-cybershuttle
+deploy-stampede3: deploy-stampede3-ccguser deploy-stampede3-exouser
+deploy-vc-airavata-cpu: deploy-vc-airavata-cpu-exouser
+deploy-vc-airavata-gpu: deploy-vc-airavata-gpu-exouser
+deploy-vc-gkeyll: deploy-vc-gkeyll-exouser deploy-vc-gkeyll-gateway-user
diff --git a/airavata-agent/README.md b/airavata-agent/README.md
new file mode 100644
index 00000000000..b1caf7eda42
--- /dev/null
+++ b/airavata-agent/README.md
@@ -0,0 +1,235 @@
+
+
+# The Agent for orchestrating Airavata job workloads
+This agent is part of the Apache Airavata platform and is responsible for executing remote workloads, managing Jupyter sessions, and setting up tunnels on behalf of users.
+
+## Download and Set Up micromamba
+
+### Download the binary
+Linux
+```
+wget https://github.com/mamba-org/micromamba-releases/releases/latest/download/micromamba-linux-64 -O micromamba
+```
+
+macOS (Intel)
+```
+wget https://github.com/mamba-org/micromamba-releases/releases/latest/download/micromamba-osx-64 -O micromamba
+```
+
+macOS (Apple Silicon)
+```
+wget https://github.com/mamba-org/micromamba-releases/releases/latest/download/micromamba-osx-arm64 -O micromamba
+```
+
+### Make the binary executable
+```
+chmod +x micromamba
+```
+
+### Create required directories
+```
+mkdir -p $HOME/cybershuttle/scratch/tmp
+```
+
+### Export environment variables
+```
+export MAMBA_ROOT_PREFIX=$HOME/cybershuttle/scratch
+export TMPDIR=$HOME/cybershuttle/scratch/tmp
+export PATH=$PWD:$PATH
+```
+
+## Running the Agent (Using Prebuilt Binary)
+
+### Download prebuilt agent binary
+```
+wget https://github.com/cyber-shuttle/binaries/releases/download/1.0.1/airavata-agent-linux-amd64 -O airavata-agent
+chmod +x airavata-agent
+```
+
+### Run the agent
+```
+./airavata-agent \
+ --server :9090 \
+ --agent \
+ --environ \
+ --lib "python=3.10,pip," \
+ --pip ""
+```
+Replace placeholders with your actual configuration values.
+
+#### Example:
+```
+./airavata-agent
+--server localhost:9090
+--agent agent_dd9667fe-78d1-4ffa-a0d2-19074e41dd45
+--environ base
+--lib "python=3.10,pip,mattersim,torchmetrics,numpy"
+--pip "git+https://github.com/cyber-shuttle/mattertune.git"
+```
+
+## Running the Agent (Development Mode)
+
+### Set up the go environment
+```
+go mod tidy
+```
+
+### Building proto files
+
+```
+go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
+go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
+export PATH=$PATH:$HOME/go/bin
+protoc --go_out=. --go-grpc_out=. agent-communication.proto
+```
+
+### Run the agent
+```
+go install
+go run agent.go --agent --environ
+```
+
+#### Example
+```
+go run agent.go --server localhost:9090 --agent agent1 --environ base
+```
+
+## Build the agent
+
+### Build for the current platform
+```
+go build
+```
+
+### Build for a specific platform
+```
+env GOOS=linux GOARCH=amd64 go build
+```
+
+## Sample Requests
+
+Execute a Shell Command
+```
+POST http://localhost:8090/api/v1/agents/execute/shell
+
+{
+ "agentId": "agent1",
+ "workingDir": "",
+ "arguments": ["docker", "ps", "-a"],
+ "envName": "base"
+}
+
+Response
+
+{
+ "executionId": "78fe66aa-4895-4768-8701-5ef50367732e",
+ "error": null
+}
+```
+
+To extract the result, pass the executionId
+```
+GET http://localhost:8090/api/v1/agents/execute/shell/78fe66aa-4895-4768-8701-5ef50367732e
+```
+
+Execute Jupyter Code
+```
+http://localhost:8090/api/v1/agents/execute/jupyter
+
+{
+ "sessionId": "session1",
+ "keepAlive": true,
+ "code": "print(4 + 5)",
+ "agentId": "agent3"
+}
+
+Response
+
+{
+ "executionId": "22f02087-87cc-4e90-bc3b-3b969179c31b",
+ "error": null
+}
+```
+```
+http://localhost:8090/api/v1/agents/execute/jupyter/22f02087-87cc-4e90-bc3b-3b969179c31b
+
+Response
+
+{
+ "executionId": "93d82c06-31e5-477f-a73a-760908a7a482",
+ "sessionId": null,
+ "responseString": "{\"result\":\"9\\n\"}\n",
+ "available": true
+}
+```
+
+Set Up a tcp Tunnel
+```
+POST http://localhost:8090/api/v1/agents/setup/tunnel
+
+{
+ "agentId": "agent1",
+ "localPort": 7000,
+ "localBindHost": "localhost"
+}
+
+Response
+
+{
+ "executionId": "f2f1c982-5a8b-4813-b048-8a71cdfc9578",
+ "status": 0,
+ "error": null
+}
+
+```
+Get tunnel info
+
+```
+GET http://localhost:8090/api/v1/agents/setup/tunnel/
+
+Response
+
+{
+ "executionId": "f2f1c982-5a8b-4813-b048-8a71cdfc9578",
+ "tunnelId": "ff2516f6-d5a4-426c-adf4-cd988c66bfc2",
+ "poxyPort": 10000,
+ "proxyHost": "poxy-host",
+ "status": "OK"
+}
+```
+
+Terminate tunnel
+
+```
+POST http://localhost:8090/api/v1/agents/terminate/tunnel
+
+{
+ "agentId": "agent1",
+ "tunnelId": "0c03281d-a713-4361-9a34-ad833b98dc4f"
+}
+
+Response
+
+{
+ "executionId": "fa88ccd8-515d-4c31-bc5a-71471c23d189",
+ "status": 0,
+ "error": null
+}
+```
diff --git a/modules/agent-framework/airavata-agent/agent.go b/airavata-agent/agent.go
similarity index 100%
rename from modules/agent-framework/airavata-agent/agent.go
rename to airavata-agent/agent.go
diff --git a/modules/agent-framework/airavata-agent/agent.sh b/airavata-agent/agent.sh
similarity index 97%
rename from modules/agent-framework/airavata-agent/agent.sh
rename to airavata-agent/agent.sh
index 23b7a10c86b..c7599f7776b 100755
--- a/modules/agent-framework/airavata-agent/agent.sh
+++ b/airavata-agent/agent.sh
@@ -116,4 +116,4 @@ export TMPDIR=$CS_HOME/scratch/tmp
export PATH=$PWD:$PATH
# run agent
-airavata-agent --server "$SERVER:19900" --agent "$AGENT" --environ "$ENVIRON" --lib "$LIBRARIES" --pip "$PIP"
+airavata-agent --server "$SERVER:9090" --agent "$AGENT" --environ "$ENVIRON" --lib "$LIBRARIES" --pip "$PIP"
diff --git a/modules/agent-framework/airavata-agent/application/README.md b/airavata-agent/application/README.md
similarity index 100%
rename from modules/agent-framework/airavata-agent/application/README.md
rename to airavata-agent/application/README.md
diff --git a/modules/agent-framework/airavata-agent/application/alphafold2.sh b/airavata-agent/application/alphafold2.sh
similarity index 100%
rename from modules/agent-framework/airavata-agent/application/alphafold2.sh
rename to airavata-agent/application/alphafold2.sh
diff --git a/modules/agent-framework/airavata-agent/application/gaussian16.sh b/airavata-agent/application/gaussian16.sh
similarity index 100%
rename from modules/agent-framework/airavata-agent/application/gaussian16.sh
rename to airavata-agent/application/gaussian16.sh
diff --git a/modules/agent-framework/airavata-agent/application/gromacs.sh b/airavata-agent/application/gromacs.sh
similarity index 100%
rename from modules/agent-framework/airavata-agent/application/gromacs.sh
rename to airavata-agent/application/gromacs.sh
diff --git a/modules/agent-framework/airavata-agent/application/namd.sh b/airavata-agent/application/namd.sh
similarity index 97%
rename from modules/agent-framework/airavata-agent/application/namd.sh
rename to airavata-agent/application/namd.sh
index 240cc2ad10f..8ad9476bad8 100644
--- a/modules/agent-framework/airavata-agent/application/namd.sh
+++ b/airavata-agent/application/namd.sh
@@ -94,7 +94,7 @@ wget -q https://github.com/cyber-shuttle/binaries/releases/download/1.0.1/airava
wget -q https://github.com/cyber-shuttle/binaries/releases/download/1.0.1/kernel.py -O $WORKDIR/kernel.py
wget -q https://github.com/mamba-org/micromamba-releases/releases/download/2.3.0-1/micromamba-linux-64 -O $WORKDIR/micromamba
chmod +x $WORKDIR/airavata-agent $WORKDIR/micromamba
-$WORKDIR/airavata-agent --server "$AGENT_SERVER:19900" --agent "$AGENT_ID" --environ "$AGENT_ID" --lib "" --pip "" &
+$WORKDIR/airavata-agent --server "$AGENT_SERVER:9090" --agent "$AGENT_ID" --environ "$AGENT_ID" --lib "" --pip "" &
AGENT_PID=$!
trap 'kill -TERM $AGENT_PID' EXIT
echo "Agent started with PID $AGENT_PID"
diff --git a/modules/agent-framework/airavata-agent/application/pmemd_cuda.sh b/airavata-agent/application/pmemd_cuda.sh
similarity index 100%
rename from modules/agent-framework/airavata-agent/application/pmemd_cuda.sh
rename to airavata-agent/application/pmemd_cuda.sh
diff --git a/modules/agent-framework/airavata-agent/application/pmemd_mpi.sh b/airavata-agent/application/pmemd_mpi.sh
similarity index 100%
rename from modules/agent-framework/airavata-agent/application/pmemd_mpi.sh
rename to airavata-agent/application/pmemd_mpi.sh
diff --git a/modules/agent-framework/airavata-agent/application/psi4.sh b/airavata-agent/application/psi4.sh
similarity index 100%
rename from modules/agent-framework/airavata-agent/application/psi4.sh
rename to airavata-agent/application/psi4.sh
diff --git a/modules/agent-framework/airavata-agent/go.mod b/airavata-agent/go.mod
similarity index 100%
rename from modules/agent-framework/airavata-agent/go.mod
rename to airavata-agent/go.mod
diff --git a/airavata-agent/go.sum b/airavata-agent/go.sum
new file mode 100644
index 00000000000..e8bbbd92870
--- /dev/null
+++ b/airavata-agent/go.sum
@@ -0,0 +1,280 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
+github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
+github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
+github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cyber-shuttle/cybershuttle-tunnels v0.0.0-20250505072337-4314f6e407c4 h1:wNrW6M+J4HQJg2jEHdgCgdixbY0zdpdREe+zXxAun+Q=
+github.com/cyber-shuttle/cybershuttle-tunnels v0.0.0-20250505072337-4314f6e407c4/go.mod h1://7D+8FO4p1dDoyb4Ml1Sgtyh4MtUMBV1KfFZcVCe70=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatedier/frp v0.62.1 h1:Xufo5KRaLUz+ZGyUD7rVsSBWv0el+ffPYgC6gGbB/f0=
+github.com/fatedier/frp v0.62.1/go.mod h1:Kfctx9dDnV+8vr2BYfwmKIbNPSbgyvIcWsVSkHWq2jM=
+github.com/fatedier/golib v0.5.1 h1:hcKAnaw5mdI/1KWRGejxR+i1Hn/NvbY5UsMKDr7o13M=
+github.com/fatedier/golib v0.5.1/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ=
+github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
+github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
+github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/pprof v0.0.0-20241206021119-61a79c692802 h1:US08AXzP0bLurpzFUV3Poa9ZijrRdd1zAIOVtoHEiS8=
+github.com/google/pprof v0.0.0-20241206021119-61a79c692802/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
+github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
+github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
+github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/klauspost/reedsolomon v1.12.0 h1:I5FEp3xSwVCcEh3F5A7dofEfhXdF/bWhQWPH+XwBFno=
+github.com/klauspost/reedsolomon v1.12.0/go.mod h1:EPLZJeh4l27pUGC3aXOjheaoh1I9yut7xTURiW3LQ9Y=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
+github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
+github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
+github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
+github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
+github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
+github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
+github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
+github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
+github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM=
+github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
+github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
+github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
+github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
+github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
+github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
+github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
+github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
+github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
+github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
+github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
+github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
+github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
+github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
+github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/templexxx/cpu v0.1.1 h1:isxHaxBXpYFWnk2DReuKkigaZyrjs2+9ypIdGP4h+HI=
+github.com/templexxx/cpu v0.1.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
+github.com/templexxx/xorsimd v0.4.3 h1:9AQTFHd7Bhk3dIT7Al2XeBX5DWOvsUPZCuhyAtNbHjU=
+github.com/templexxx/xorsimd v0.4.3/go.mod h1:oZQcD6RFDisW2Am58dSAGwwL6rHjbzrlu25VDqfWkQg=
+github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
+github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
+github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
+github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
+github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
+github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
+github.com/xtaci/kcp-go/v5 v5.6.13 h1:FEjtz9+D4p8t2x4WjciGt/jsIuhlWjjgPCCWjrVR4Hk=
+github.com/xtaci/kcp-go/v5 v5.6.13/go.mod h1:75S1AKYYzNUSXIv30h+jPKJYZUwqpfvLshu63nCNSOM=
+github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
+github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
+go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
+golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
+golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
+golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
+golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
+golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
+golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
+golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
+golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
+golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
+golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
+golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
+golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
+golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
+golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
+golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
+golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
+google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
+google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
+gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+k8s.io/apimachinery v0.28.8 h1:hi/nrxHwk4QLV+W/SHve1bypTE59HCDorLY1stBIxKQ=
+k8s.io/apimachinery v0.28.8/go.mod h1:cBnwIM3fXoRo28SqbV/Ihxf/iviw85KyXOrzxvZQ83U=
+k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
+k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/modules/agent-framework/airavata-agent/kernel.py b/airavata-agent/kernel.py
similarity index 100%
rename from modules/agent-framework/airavata-agent/kernel.py
rename to airavata-agent/kernel.py
diff --git a/modules/agent-framework/airavata-agent/pkg/jupyter.go b/airavata-agent/pkg/jupyter.go
similarity index 100%
rename from modules/agent-framework/airavata-agent/pkg/jupyter.go
rename to airavata-agent/pkg/jupyter.go
diff --git a/modules/agent-framework/airavata-agent/pkg/python.go b/airavata-agent/pkg/python.go
similarity index 100%
rename from modules/agent-framework/airavata-agent/pkg/python.go
rename to airavata-agent/pkg/python.go
diff --git a/modules/agent-framework/airavata-agent/pkg/shell.go b/airavata-agent/pkg/shell.go
similarity index 100%
rename from modules/agent-framework/airavata-agent/pkg/shell.go
rename to airavata-agent/pkg/shell.go
diff --git a/modules/agent-framework/airavata-agent/pkg/tunnel.go b/airavata-agent/pkg/tunnel.go
similarity index 100%
rename from modules/agent-framework/airavata-agent/pkg/tunnel.go
rename to airavata-agent/pkg/tunnel.go
diff --git a/modules/agent-framework/airavata-agent/pkg/types.go b/airavata-agent/pkg/types.go
similarity index 100%
rename from modules/agent-framework/airavata-agent/pkg/types.go
rename to airavata-agent/pkg/types.go
diff --git a/airavata-agent/proto-shared b/airavata-agent/proto-shared
new file mode 120000
index 00000000000..5c8d3525370
--- /dev/null
+++ b/airavata-agent/proto-shared
@@ -0,0 +1 @@
+../proto
\ No newline at end of file
diff --git a/airavata-agent/protos/.gitignore b/airavata-agent/protos/.gitignore
new file mode 100644
index 00000000000..441e2dc2f1a
--- /dev/null
+++ b/airavata-agent/protos/.gitignore
@@ -0,0 +1,2 @@
+# Auto-generated protobuf Go code — regenerate from .proto files
+*.pb.go
diff --git a/airavata-api/.dockerignore b/airavata-api/.dockerignore
new file mode 100644
index 00000000000..f2ccd93286f
--- /dev/null
+++ b/airavata-api/.dockerignore
@@ -0,0 +1,27 @@
+# Docker build context: typically modules/distribution/target/
+# This file applies when building from the airavata-api/ directory.
+.git
+.github
+.gitignore
+.gitattributes
+.idea
+.run
+.vscode
+.claude
+.DS_Store
+.env
+.env.*
+
+# Source and docs (not needed in image)
+docs/
+scripts/
+examples/
+*.iml
+*.md
+
+# Build output (selectively include via Dockerfile COPY)
+**/target
+**/node_modules
+
+# Logs
+*.log
diff --git a/airavata-api/.mvn/extensions.xml b/airavata-api/.mvn/extensions.xml
new file mode 100644
index 00000000000..e739711bb0a
--- /dev/null
+++ b/airavata-api/.mvn/extensions.xml
@@ -0,0 +1,8 @@
+
+
+
+ org.apache.maven.extensions
+ maven-build-cache-extension
+ 1.2.1
+
+
diff --git a/airavata-api/.mvn/jvm.config b/airavata-api/.mvn/jvm.config
new file mode 100644
index 00000000000..4e8a98e8f43
--- /dev/null
+++ b/airavata-api/.mvn/jvm.config
@@ -0,0 +1,2 @@
+--enable-native-access=ALL-UNNAMED
+--add-opens jdk.unsupported/sun.misc=ALL-UNNAMED
diff --git a/airavata-api/.mvn/maven-build-cache-config.xml b/airavata-api/.mvn/maven-build-cache-config.xml
new file mode 100644
index 00000000000..7bb071d23a5
--- /dev/null
+++ b/airavata-api/.mvn/maven-build-cache-config.xml
@@ -0,0 +1,14 @@
+
+
+
+ false
+ XX
+
+
+
+ {*.java,*.xml,*.properties,*.proto,*.yml,*.yaml,*.json,*.sql,*.sh,*.py}
+
+
+
diff --git a/airavata-api/.mvn/wrapper/maven-wrapper.properties b/airavata-api/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000000..ffcab66aa28
--- /dev/null
+++ b/airavata-api/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,3 @@
+wrapperVersion=3.3.4
+distributionType=only-script
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
diff --git a/airavata-api/README.md b/airavata-api/README.md
new file mode 100644
index 00000000000..81d4763f89c
--- /dev/null
+++ b/airavata-api/README.md
@@ -0,0 +1,428 @@
+# Airavata API Server
+
+The core backend for Apache Airavata. A unified Spring Boot application that runs all services in a single JVM — REST API, gRPC, orchestration, Temporal workflows, credential store, IAM integration, and job execution.
+
+## Quick Start
+
+```bash
+# Prerequisites: Java 25+, Maven 3.8+, Docker 20.10+
+
+# One command: build + init infrastructure + serve
+./scripts/run.sh
+# Credentials: default-admin / admin123
+
+# --- Or step by step ---
+mvn clean install -DskipTests # build
+./scripts/init.sh --clean # init infrastructure (Docker, Keycloak, DB)
+./scripts/dev.sh serve # start server
+```
+
+Swagger UI: http://localhost:8090/swagger-ui/index.html
+
+## Modules
+
+| Module | Path | Description |
+|--------|------|-------------|
+| **airavata-api** | `modules/airavata-api/` | Core domain logic, entities, repositories, services |
+| **grpc-api** | `modules/grpc-api/` | gRPC protocol definitions and codegen (agent, research streams) |
+| **rest-api** | `modules/rest-api/` | REST controllers, security filters, OpenAPI docs |
+| **distribution** | `modules/distribution/` | Spring Boot entry point, packaging, Docker image |
+
+Build order: `airavata-api` -> `grpc-api` -> `rest-api` -> `distribution`
+
+## Server Ports
+
+| Server | Port | Config Property |
+|--------|------|-----------------|
+| HTTP (REST, File, Agent, Research API) | 8090 | `server.port` |
+| gRPC (agent/research streams) | 9090 | `spring.grpc.server.port` |
+| Temporal (workflow engine) | 7233 | `spring.temporal.connection.target` |
+
+## Scripts
+
+From `airavata-api/`:
+
+| Script | Purpose |
+|--------|---------|
+| `./scripts/build.sh` | Build: `mvn clean install`. Use `--skip-tests` for compile-only. |
+| `./scripts/run.sh` | **One command:** build (if needed) + init --clean + serve |
+| `./scripts/init.sh` | Init infra (Keycloak + DB). `--clean` = reset. `--run` = then serve |
+| `./scripts/dev.sh` | Dev mode: `serve`, `init`, etc. `--debug` for jdwp |
+| `./scripts/jar.sh` | Run from built JAR |
+| `./scripts/setup-echo-experiment.sh` | End-to-end smoke test (18 steps) |
+
+**Cold start:** `./scripts/init.sh --clean --run` tears down containers/volumes, brings up infra, runs Keycloak setup and DB migrations, then serves. Teardown only: `docker compose -f ../.devcontainer/compose.yml down -v`.
+
+## REST API Endpoints
+
+All endpoints are prefixed with `/api/v1/` and follow RESTful conventions.
+
+| Endpoint Category | Controller | Base Path | Description |
+|-------------------|------------|-----------|-------------|
+| Auth | `AuthController` | `/api/v1/auth` | Logout and federated logout |
+| System | `SystemController` | `/api/v1` | Health check, public config |
+| Experiments | `ExperimentController` | `/api/v1/experiments` | Experiment lifecycle management |
+| Processes | `ProcessController` | `/api/v1/processes` | Process execution and monitoring |
+| Jobs | `JobController` | `/api/v1/jobs` | Job status and management |
+| Applications | `ApplicationController` | `/api/v1/applications` | Application CRUD |
+| Installations | `ApplicationInstallationController` | `/api/v1/installations` | Application installation on resources |
+| Resources | `ResourceController` | `/api/v1/resources` | Unified compute/storage resource management |
+| Resource Bindings | `ResourceBindingController` | `/api/v1/bindings` | Credential-resource binding management |
+| Projects | `ProjectController` | `/api/v1/projects` | Project management |
+| Allocation Projects | `AllocationProjectController` | `/api/v1/allocation-projects` | HPC allocation project management |
+| Gateways | `GatewayController` | `/api/v1/gateways` | Gateway CRUD |
+| Users | `UserController` | `/api/v1/users` | User management |
+| Groups | `GroupController` | `/api/v1/groups` | Group management and membership |
+| Credentials | `CredentialController` | `/api/v1` | SSH/password credential CRUD |
+| Workflows | `WorkflowController` | `/api/v1/workflows` | Workflow definitions |
+| Workflow Runs | `WorkflowRunController` | `/api/v1/workflow-runs` | Workflow run execution and status |
+| Notices | `NoticeController` | `/api/v1/notices` | Notification management |
+| Statistics | `StatisticsController` | `/api/v1/statistics` | Experiment and system statistics |
+| SSH Keys | `SSHKeyController` | `/api/v1/ssh-keygen` | SSH key pair generation |
+| Connectivity Test | `ConnectivityTestController` | `/api/v1/connectivity-test` | SSH/SFTP/SLURM connectivity validation |
+| Monitoring | `MonitoringJobStatusController` | `/api/v1/monitoring` | Job status callback endpoint |
+| Research Artifacts | `ResearchArtifactController` | `/api/v1/research/artifacts` | Research artifact CRUD |
+| Research Projects | `ResearchProjectController` | `/api/v1/research/artifacts/projects` | Research project management |
+| Research Sessions | `ResearchSessionController` | `/api/v1/research-hub/sessions` | Research session management |
+
+OpenAPI docs: `http://localhost:8090/swagger-ui/index.html` | `http://localhost:8090/v3/api-docs`
+
+## Package Structure
+
+Domain-first layout where each domain owns its entity, repository, mapper, service, and model:
+
+```text
+org.apache.airavata/
+├── execution/ # Orchestration, scheduling, DAG execution, Temporal activities
+│ ├── activity/ # Temporal workflows (Pre/Post/Cancel) and ActivitiesImpl
+│ ├── orchestration/ # OrchestratorService, ProcessResourceResolver
+│ ├── scheduling/ # ProcessScheduler, ScheduledTaskManager
+│ ├── dag/ # ProcessDAGEngine, TaskNode, interceptors
+│ ├── monitoring/ # JobStatusEventPublisher, email monitoring
+│ └── state/ # StateValidators, StateModel
+├── research/ # Research domain grouping
+│ ├── experiment/ # Experiments: entity/repo/mapper/service/model
+│ ├── application/ # App catalog: entity/repo/mapper/service/model
+│ ├── artifact/ # Research artifacts
+│ ├── project/ # Research projects
+│ └── session/ # User sessions
+├── compute/ # Compute resources and job execution
+│ ├── resource/ # Resource models/services/entities + job submission infra
+│ └── provider/ # Backend impls: slurm/, local/, aws/
+├── storage/ # Data staging
+│ ├── resource/ # Storage models
+│ └── client/ # StorageClient interface + SftpStorageClient
+├── status/ # Event and status management
+├── protocol/ # SSH/SCP/SFTP transport adapters
+├── iam/ # Keycloak, sharing, authorization
+├── gateway/ # Multi-tenant gateway management
+├── credential/ # Credential store
+├── accounting/ # Allocation project accounting
+├── workflow/ # Workflow DAG definitions
+├── core/ # Cross-cutting: EntityMapper, CrudService, exceptions, utils
+└── config/ # Spring configuration
+```
+
+## Configuration
+
+Main config: `modules/distribution/src/main/resources/application.properties`
+
+| Category | Key Properties |
+|----------|---------------|
+| Database | `spring.datasource.url`, `spring.datasource.username`, `spring.datasource.password` |
+| Keycloak | `airavata.security.iam.server-url`, `airavata.security.iam.realm` |
+| Temporal | `spring.temporal.connection.target` |
+| Agent | `airavata.services.agent.enabled`, `airavata.services.agent.storage.path` |
+| gRPC | `spring.grpc.server.port` |
+| Monitoring | `airavata.services.monitor.compute.job-status-callback-url` |
+
+Paths: Home via `--home` or `AIRAVATA_HOME`; config via `--config-dir` or `AIRAVATA_CONFIG_DIR` (default `{home}/conf`).
+
+## Schema Migrations
+
+Single Flyway baseline at `modules/distribution/src/main/resources/conf/db/migration/airavata/V1__Baseline_schema.sql`. To change schema: add a versioned migration (e.g. `V2__Description.sql`) with `IF NOT EXISTS`/`IF EXISTS`.
+
+## Database ERD
+
+Entity-Relationship Diagram for the Airavata database schema.
+
+### Schema Summary
+
+- **38 tables** in the Flyway V1 baseline migration (`V1__Baseline_schema.sql`)
+- **No views** — `EXPERIMENT_SUMMARY` is a denormalized table
+- **Engine**: InnoDB, charset `utf8mb4`, collation `utf8mb4_unicode_ci`
+- **Requires**: MariaDB 10.2+ (JSON column support)
+- **All table names use UPPER_CASE**
+
+### Table Origin
+
+All 38 tables are defined in the V1 Flyway baseline migration. There are no Hibernate `ddl-auto`-created tables.
+
+| Part | Tables |
+|------|--------|
+| Part 1 — Tenant, Identity & IAM (7) | GATEWAY, USER, NOTIFICATION, TAG, USER_GROUP, GROUP_MEMBERSHIP, SHARING_PERMISSION |
+| Part 2 — Compute & Credentials (7) | RESOURCE, CREDENTIAL, RESOURCE_BINDING, RESOURCE_PREFERENCE, ALLOCATION_PROJECT, CREDENTIAL_ALLOCATION_PROJECT, COMPUTE_SUBMISSION_TRACKING |
+| Part 3 — Application & Experiment (10) | APPLICATION, APPLICATION_INSTALLATION, PROJECT, PROJECT_DATASET, EXPERIMENT, EXPERIMENT_INPUT, EXPERIMENT_OUTPUT, PROCESS, EVENT, JOB |
+| Part 4 — Research Platform (11) | RESEARCH_ARTIFACT, RESEARCH_MODEL_ARTIFACT, RESEARCH_NOTEBOOK_ARTIFACT, RESEARCH_REPOSITORY_ARTIFACT, RESEARCH_DATASET_ARTIFACT, RESEARCH_ARTIFACT_AUTHORS, RESEARCH_ARTIFACT_TAGS, ARTIFACT_STAR, RESEARCH_PROJECT, RESEARCH_PROJECT_DATASET, RESEARCH_SESSION |
+| Part 5 — Workflow & Tracking (3) | WORKFLOW, WORKFLOW_RUN, EXPERIMENT_SUMMARY |
+
+### Part 1 — Tenant, Identity & IAM
+
+```mermaid
+%%{init: { "er": { "layoutDirection": "TB", "fontSize": 16 } }}%%
+erDiagram
+ GATEWAY {
+ varchar gateway_id PK
+ varchar gateway_name UK
+ varchar gateway_domain
+ varchar email_address
+ timestamp created_at
+ timestamp updated_at
+ }
+ USER {
+ varchar user_id PK
+ varchar sub
+ varchar gateway_id FK
+ varchar first_name
+ varchar last_name
+ varchar email
+ timestamp created_at
+ }
+ USER_GROUP {
+ varchar group_id PK
+ varchar gateway_id PK
+ varchar name
+ varchar owner_id
+ varchar group_type
+ boolean is_personal_group
+ }
+ GROUP_MEMBERSHIP {
+ varchar membership_id PK
+ varchar group_id
+ varchar user_id
+ varchar role
+ }
+ SHARING_PERMISSION {
+ varchar permission_id PK
+ varchar resource_type
+ varchar resource_id
+ varchar grantee_type
+ varchar grantee_id
+ varchar permission
+ }
+
+ GATEWAY ||--o{ USER : "has users"
+ GATEWAY ||--o{ USER_GROUP : "has groups"
+```
+
+### Part 2 — Compute & Credentials
+
+```mermaid
+%%{init: { "er": { "layoutDirection": "TB", "fontSize": 16 } }}%%
+erDiagram
+ RESOURCE {
+ varchar resource_id PK
+ varchar gateway_id FK
+ varchar name
+ varchar host_name
+ int port
+ varchar resource_type
+ json capabilities
+ }
+ CREDENTIAL {
+ varchar credential_id PK
+ varchar gateway_id FK
+ varchar type
+ longblob credential_data
+ varchar user_id
+ varchar name
+ }
+ RESOURCE_BINDING {
+ varchar binding_id PK
+ varchar credential_id FK
+ varchar resource_id FK
+ varchar login_username
+ json metadata
+ }
+ ALLOCATION_PROJECT {
+ varchar allocation_project_id PK
+ varchar project_code
+ varchar resource_id FK
+ }
+
+ RESOURCE_BINDING }o--|| CREDENTIAL : "uses credential"
+ RESOURCE_BINDING }o--|| RESOURCE : "binds to resource"
+ ALLOCATION_PROJECT }o--|| RESOURCE : "for resource"
+```
+
+### Part 3 — Application & Experiment Pipeline
+
+```mermaid
+%%{init: { "er": { "layoutDirection": "TB", "fontSize": 16 } }}%%
+erDiagram
+ APPLICATION {
+ varchar application_id PK
+ varchar gateway_id FK
+ varchar name
+ json inputs
+ json outputs
+ mediumtext run_script
+ }
+ EXPERIMENT {
+ varchar experiment_id PK
+ varchar project_id FK
+ varchar gateway_id FK
+ varchar application_id FK
+ varchar binding_id FK
+ varchar state
+ json scheduling
+ }
+ PROCESS {
+ varchar process_id PK
+ varchar experiment_id FK
+ json provider_context
+ }
+ JOB {
+ varchar job_id PK
+ varchar process_id FK
+ varchar job_name
+ mediumtext std_out
+ mediumtext std_err
+ int exit_code
+ }
+ EVENT {
+ varchar event_id PK
+ varchar parent_id
+ varchar parent_type
+ varchar event_kind
+ varchar state
+ mediumtext reason
+ }
+
+ EXPERIMENT ||--o{ PROCESS : "has processes"
+ EXPERIMENT }o--|| APPLICATION : "runs application"
+ PROCESS ||--o{ JOB : "has jobs"
+```
+
+### Part 4 — Research Platform
+
+```mermaid
+%%{init: { "er": { "layoutDirection": "TB", "fontSize": 16 } }}%%
+erDiagram
+ RESEARCH_ARTIFACT {
+ varchar id PK
+ varchar name
+ varchar status
+ varchar state
+ varchar privacy
+ }
+ RESEARCH_REPOSITORY_ARTIFACT {
+ varchar id PK
+ varchar repository_url
+ }
+ RESEARCH_PROJECT {
+ varchar id PK
+ varchar name
+ varchar owner_id
+ varchar repository_artifact_id FK
+ }
+ RESEARCH_SESSION {
+ varchar id PK
+ varchar session_name
+ varchar user_id
+ varchar project_id FK
+ }
+
+ RESEARCH_ARTIFACT ||--o| RESEARCH_REPOSITORY_ARTIFACT : "JOINED"
+ RESEARCH_PROJECT }o--|| RESEARCH_REPOSITORY_ARTIFACT : "has repo"
+ RESEARCH_SESSION }o--|| RESEARCH_PROJECT : "in project"
+```
+
+### Part 5 — Workflow & Tracking
+
+```mermaid
+%%{init: { "er": { "layoutDirection": "TB", "fontSize": 16 } }}%%
+erDiagram
+ WORKFLOW {
+ varchar workflow_id PK
+ varchar project_id FK
+ varchar workflow_name
+ json steps
+ json edges
+ }
+ WORKFLOW_RUN {
+ varchar run_id PK
+ varchar workflow_id FK
+ varchar status
+ json step_states
+ }
+
+ WORKFLOW_RUN }o--|| WORKFLOW : "run of"
+```
+
+### Complete Table Index
+
+| # | Table | PK | JPA Entity |
+|---|-------|-----|------------|
+| 1 | GATEWAY | gateway_id | GatewayEntity |
+| 2 | USER | user_id | UserEntity |
+| 3 | NOTIFICATION | notification_id | NotificationEntity |
+| 4 | TAG | id | TagEntity |
+| 5 | RESOURCE | resource_id | ComputeResourceEntity |
+| 6 | CREDENTIAL | credential_id | CredentialEntity |
+| 7 | RESOURCE_BINDING | binding_id | ResourceBindingEntity |
+| 8 | RESOURCE_PREFERENCE | preference_id | ResourcePreferenceEntity |
+| 9 | APPLICATION | application_id | ApplicationEntity |
+| 10 | APPLICATION_INSTALLATION | installation_id | ApplicationInstallationEntity |
+| 11 | ALLOCATION_PROJECT | allocation_project_id | AllocationProjectEntity |
+| 12 | CREDENTIAL_ALLOCATION_PROJECT | (credential_id, alloc_project_id) | CredentialAllocationProjectEntity |
+| 13-17 | RESEARCH_ARTIFACT + subtypes | id | ResearchArtifactEntity (abstract) |
+| 18-20 | RESEARCH_ARTIFACT_AUTHORS/TAGS, ARTIFACT_STAR | composite / id | @ElementCollection / @ManyToMany |
+| 21 | PROJECT | project_id | ProjectEntity |
+| 22 | RESEARCH_PROJECT | id | ResearchProjectEntity |
+| 23-24 | PROJECT_DATASET, RESEARCH_PROJECT_DATASET | composite | @ManyToMany join |
+| 25 | EXPERIMENT | experiment_id | ExperimentEntity |
+| 26-27 | EXPERIMENT_INPUT/OUTPUT | input_id / output_id | ExperimentInputEntity / ExperimentOutputEntity |
+| 28 | PROCESS | process_id | ProcessEntity |
+| 29 | EVENT | event_id | EventEntity |
+| 30 | JOB | job_id | JobEntity |
+| 31 | RESEARCH_SESSION | id | SessionEntity |
+| 32 | USER_GROUP | (group_id, gateway_id) | UserGroupEntity |
+| 33 | GROUP_MEMBERSHIP | membership_id | GroupMembershipEntity |
+| 34 | SHARING_PERMISSION | permission_id | SharingPermissionEntity |
+| 35 | WORKFLOW | workflow_id | WorkflowEntity |
+| 36 | WORKFLOW_RUN | run_id | WorkflowRunEntity |
+| 37 | COMPUTE_SUBMISSION_TRACKING | compute_resource_id | ComputeSubmissionTrackingEntity |
+| 38 | EXPERIMENT_SUMMARY | experiment_id | ExperimentSummaryEntity (@Immutable) |
+
+## Testing
+
+```bash
+# With infrastructure running (for integration tests)
+./scripts/init.sh # ensure DB, Keycloak, Temporal up
+mvn test -Dskip.slurm.tests=true # all tests, skip SLURM
+
+# Without pre-starting services (many tests use Testcontainers)
+mvn test # all tests
+mvn test -pl modules/airavata-api # specific module
+mvn test -Dtest=SomeTestClass # specific test
+
+# SLURM tests need Docker + SLURM Testcontainer
+docker compose -f ../.devcontainer/compose.yml --profile test up -d
+mvn test -P test
+```
+
+## Docker
+
+```bash
+# Build distribution + Docker image
+mvn clean install -DskipTests
+docker build -t airavata:latest -f modules/distribution/src/main/docker/Dockerfile modules/distribution/target
+
+# Run
+docker run -p 8090:8090 -p 9090:9090 airavata:latest
+```
+
+## License
+
+Licensed under the Apache License, Version 2.0. See [LICENSE](../LICENSE).
diff --git a/apache-license-header-java.txt b/airavata-api/apache-license-header-java.txt
similarity index 100%
rename from apache-license-header-java.txt
rename to airavata-api/apache-license-header-java.txt
diff --git a/apache-license-header-xml.txt b/airavata-api/apache-license-header-xml.txt
similarity index 100%
rename from apache-license-header-xml.txt
rename to airavata-api/apache-license-header-xml.txt
diff --git a/apache-license-header.txt b/airavata-api/apache-license-header.txt
similarity index 100%
rename from apache-license-header.txt
rename to airavata-api/apache-license-header.txt
diff --git a/airavata-api/examples/hello-world-experiment.sh b/airavata-api/examples/hello-world-experiment.sh
new file mode 100755
index 00000000000..1f08b71dd69
--- /dev/null
+++ b/airavata-api/examples/hello-world-experiment.sh
@@ -0,0 +1,223 @@
+#!/usr/bin/env bash
+# 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.
+
+# =============================================================================
+# Hello World Experiment — Airavata REST API Example
+# =============================================================================
+#
+# Demonstrates the full experiment lifecycle against the Airavata REST API:
+# 1. Create an application module
+# 2. Create an application interface
+# 3. Register a compute resource with SSH/SLURM job submission
+# 4. Add an SSH credential
+# 5. Create an application deployment
+# 6. Set up a group resource profile
+# 7. Create a project
+# 8. Create and launch an experiment
+# 9. Poll for terminal state
+#
+# Prerequisites:
+# - Airavata server running (./scripts/run.sh)
+# - curl and jq installed
+#
+# Usage:
+# ./examples/hello-world-experiment.sh [BASE_URL]
+#
+# Default BASE_URL: http://localhost:8090/api/v1
+# =============================================================================
+
+set -euo pipefail
+
+BASE_URL="${1:-http://localhost:8090/api/v1}"
+GATEWAY_ID="default"
+USERNAME="default-admin"
+
+echo "=== Airavata Hello World Experiment ==="
+echo "API: $BASE_URL"
+echo
+
+# ---------- helper ----------
+api() {
+ local method="$1" path="$2"; shift 2
+ curl -sf -X "$method" \
+ -H "Content-Type: application/json" \
+ -H "X-Gateway-ID: $GATEWAY_ID" \
+ -H "X-User-Name: $USERNAME" \
+ "$BASE_URL$path" "$@"
+}
+
+# ---------- 1. Application Module ----------
+echo "1. Creating application module..."
+MODULE_ID=$(api POST "/application-modules?gatewayId=$GATEWAY_ID" -d '{
+ "appModuleName": "HelloWorld",
+ "appModuleVersion": "1.0",
+ "appModuleDescription": "Simple echo application"
+}' | jq -r '.moduleId')
+echo " Module ID: $MODULE_ID"
+
+# ---------- 2. Application Interface ----------
+echo "2. Creating application interface..."
+APP_ID=$(api POST "/application-interfaces?gatewayId=$GATEWAY_ID" -d "{
+ \"applicationName\": \"HelloWorld\",
+ \"applicationDescription\": \"Echo application - reads message and writes to stdout\",
+ \"applicationModules\": [\"$MODULE_ID\"],
+ \"applicationInputs\": [{
+ \"name\": \"message\",
+ \"type\": \"STRING\",
+ \"userFriendlyDescription\": \"Message to echo\",
+ \"inputOrder\": 0,
+ \"isRequired\": true,
+ \"requiredToAddedToCommandLine\": true
+ }],
+ \"applicationOutputs\": []
+}" | jq -r '.applicationInterfaceId // .interfaceId')
+echo " Application ID: $APP_ID"
+
+# ---------- 3. Compute Resource ----------
+# Job submission interfaces are embedded in the compute resource JSON
+# (no separate endpoint for adding them).
+echo "3. Registering compute resource with SSH/SLURM job submission..."
+COMPUTE_ID=$(api POST /compute-resources -d '{
+ "hostName": "localhost",
+ "resourceDescription": "Local SLURM cluster",
+ "enabled": true,
+ "batchQueues": [{
+ "queueName": "normal",
+ "queueDescription": "Default queue",
+ "maxRunTime": 60,
+ "maxNodes": 1,
+ "maxProcessors": 4,
+ "defaultNodeCount": 1,
+ "defaultCPUCount": 1,
+ "defaultWalltime": 5,
+ "isDefaultQueue": true
+ }],
+ "jobSubmissionInterfaces": [{
+ "jobSubmissionProtocol": "SSH",
+ "priorityOrder": 0
+ }]
+}' | jq -r '.computeResourceId')
+echo " Compute Resource ID: $COMPUTE_ID"
+
+# ---------- 4. SSH Credential ----------
+echo "4. Creating SSH credential..."
+CRED_TOKEN=$(api POST /credentials/ssh -d "{
+ \"gatewayId\": \"$GATEWAY_ID\",
+ \"userId\": \"$USERNAME\",
+ \"description\": \"Hello-world demo credential\"
+}" | jq -r '.token')
+echo " Credential Token: $CRED_TOKEN"
+
+# ---------- 5. Application Deployment ----------
+echo "5. Creating application deployment..."
+DEPLOY_ID=$(api POST "/application-deployments?gatewayId=$GATEWAY_ID" -d "{
+ \"appModuleId\": \"$MODULE_ID\",
+ \"computeResourceId\": \"$COMPUTE_ID\",
+ \"executablePath\": \"/bin/echo\",
+ \"parallelism\": \"SERIAL\",
+ \"appDeploymentDescription\": \"HelloWorld on local SLURM\",
+ \"defaultQueueName\": \"normal\",
+ \"defaultNodeCount\": 1,
+ \"defaultCPUCount\": 1,
+ \"defaultWalltime\": 5
+}" | jq -r '.deploymentId')
+echo " Deployment ID: $DEPLOY_ID"
+
+# ---------- 6. Group Resource Profile ----------
+echo "6. Creating group resource profile..."
+PROFILE_ID=$(api POST /group-resource-profiles -d "{
+ \"gatewayId\": \"$GATEWAY_ID\",
+ \"groupResourceProfileName\": \"HelloWorld-Profile\",
+ \"computePreferences\": [{
+ \"computeResourceId\": \"$COMPUTE_ID\",
+ \"overridebyAiravata\": true,
+ \"loginUserName\": \"root\",
+ \"scratchLocation\": \"/tmp\",
+ \"resourceSpecificCredentialStoreToken\": \"$CRED_TOKEN\",
+ \"preferredJobSubmissionProtocol\": \"SSH\"
+ }]
+}" | jq -r '.groupResourceProfileId')
+echo " Profile ID: $PROFILE_ID"
+
+# ---------- 7. Create Project ----------
+echo "7. Creating project..."
+PROJECT_ID=$(api POST "/projects?gatewayId=$GATEWAY_ID" -d "{
+ \"name\": \"HelloWorld-Demo\",
+ \"gatewayId\": \"$GATEWAY_ID\",
+ \"owner\": \"$USERNAME\",
+ \"description\": \"Demo project for hello-world experiment\"
+}" | jq -r '.projectId')
+echo " Project ID: $PROJECT_ID"
+
+# ---------- 8. Create Experiment ----------
+echo "8. Creating experiment..."
+EXP_ID=$(api POST /experiments -d "{
+ \"experimentName\": \"HelloWorld-$(date +%s)\",
+ \"projectId\": \"$PROJECT_ID\",
+ \"gatewayId\": \"$GATEWAY_ID\",
+ \"userName\": \"$USERNAME\",
+ \"description\": \"Hello-world demo experiment\",
+ \"experimentType\": \"SINGLE_APPLICATION\",
+ \"executionId\": \"$APP_ID\",
+ \"experimentInputs\": [{
+ \"name\": \"message\",
+ \"value\": \"Hello from Airavata!\",
+ \"type\": \"STRING\"
+ }],
+ \"userConfigurationData\": {
+ \"computationalResourceScheduling\": {
+ \"resourceHostId\": \"$COMPUTE_ID\",
+ \"nodeCount\": 1,
+ \"totalCPUCount\": 1,
+ \"wallTimeLimit\": 5,
+ \"queueName\": \"normal\"
+ },
+ \"groupResourceProfileId\": \"$PROFILE_ID\",
+ \"airavataAutoSchedule\": false,
+ \"overrideManualScheduledParams\": false
+ }
+}" | jq -r '.experimentId')
+echo " Experiment ID: $EXP_ID"
+
+# ---------- 9. Launch Experiment ----------
+echo "9. Launching experiment..."
+api POST "/experiments/$EXP_ID/launch" > /dev/null
+echo " Launched."
+
+# ---------- 10. Poll for Completion ----------
+echo "10. Polling for terminal state (timeout 5 min)..."
+TIMEOUT=300
+ELAPSED=0
+while [ $ELAPSED -lt $TIMEOUT ]; do
+ # Status is embedded in the experiment — experimentStatus is a list, take last entry
+ STATE=$(api GET "/experiments/$EXP_ID" | jq -r '
+ (.experimentStatus // [])
+ | if length > 0 then last.state // "UNKNOWN" else "UNKNOWN" end
+ ')
+ echo " [$ELAPSED s] State: $STATE"
+ case "$STATE" in
+ COMPLETED|FAILED|CANCELED) break ;;
+ esac
+ sleep 5
+ ELAPSED=$((ELAPSED + 5))
+done
+
+echo
+echo "=== Done ==="
+echo "Final state: $STATE"
+echo "Experiment ID: $EXP_ID"
diff --git a/airavata-api/modules/airavata-api/pom.xml b/airavata-api/modules/airavata-api/pom.xml
new file mode 100644
index 00000000000..21a5d4c84b6
--- /dev/null
+++ b/airavata-api/modules/airavata-api/pom.xml
@@ -0,0 +1,585 @@
+
+
+
+ 4.0.0
+
+
+ org.apache.airavata
+ airavata
+ 0.21-SNAPSHOT
+ ../../pom.xml
+
+
+ airavata-api
+ jar
+ Airavata API
+ http://airavata.apache.org/
+
+
+
+ ${project.build.directory}/docker
+ ${project.basedir}/../distribution/src/main/docker
+ airavata-${project.version}
+
+
+
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+ jakarta.transaction
+ jakarta.transaction-api
+
+
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ provided
+
+
+ jakarta.jms
+ jakarta.jms-api
+ provided
+
+
+
+
+
+
+ org.mariadb.jdbc
+ mariadb-java-client
+
+
+
+
+
+ org.mapstruct
+ mapstruct
+
+
+ org.mapstruct
+ mapstruct-processor
+ provided
+
+
+ org.hibernate.validator
+ hibernate-validator
+
+
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+
+
+
+
+
+ com.hierynomus
+ sshj
+
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+
+
+ org.bouncycastle
+ bcpkix-jdk18on
+
+
+
+
+ org.flywaydb
+ flyway-core
+
+
+ org.flywaydb
+ flyway-mysql
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.testcontainers
+ testcontainers
+ test
+
+
+ org.testcontainers
+ junit-jupiter
+ test
+
+
+ org.testcontainers
+ mariadb
+ test
+
+
+ com.github.dasniko
+ testcontainers-keycloak
+ test
+
+
+
+ org.jboss
+ jandex
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+ org.slf4j
+ jcl-over-slf4j
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-ldap
+
+
+ org.springframework.security
+ spring-security-ldap
+
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
+
+
+ org.apache.directory.api
+ api-all
+
+
+
+ org.codehaus.groovy
+ groovy-templates
+
+
+
+ com.github.docker-java
+ docker-java
+
+
+ log4j
+ log4j
+
+
+ org.glassfish.jersey.core
+ jersey-client
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+
+
+ org.bouncycastle
+ bcpkix-jdk18on
+
+
+ org.glassfish.jersey.connectors
+ jersey-apache-connector
+
+
+ org.glassfish.jersey.core
+ jersey-common
+
+
+ org.glassfish.jersey.inject
+ jersey-hk2
+
+
+
+
+ com.github.docker-java
+ docker-java-transport-httpclient5
+
+
+
+
+
+ software.amazon.awssdk
+ ec2
+
+
+ software.amazon.awssdk
+ auth
+
+
+ software.amazon.awssdk
+ retries
+
+
+
+ org.apache.sshd
+ sshd-sftp
+ 2.12.1
+ test
+
+
+
+
+ io.temporal
+ temporal-spring-boot-starter
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.jboss.jandex
+ jandex
+
+
+
+
+
+ io.smallrye
+ jandex
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+ org.springframework.boot
+ spring-boot-starter-cache
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+
+
+ enforce-deps
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-template-file
+ generate-sources
+
+ copy-resources
+
+
+ ${project.build.directory}/generated-sources/templates/org/apache/airavata/config
+
+
+ ${project.basedir}/src/main/java/org/apache/airavata/config
+
+ DatabaseVersionConstants.java.template
+
+ true
+
+
+
+
+
+ copy-api-server-resources
+ package
+
+ copy-resources
+
+
+ ${docker.api.server.build.directory}
+
+
+ ${docker.api.server.image.src.root}
+ false
+
+
+ ${project.basedir}/../../deployment/scripts
+ false
+
+
+ ${session.executionRootDirectory}/distribution
+
+ ${api.server.dist.name}.tar.gz
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+ rename-template-file
+ generate-sources
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 25
+ true
+
+ -Xlint:-dangling-doc-comments
+
+
+
+ org.mapstruct
+ mapstruct-processor
+
+
+ ${project.build.directory}/generated-sources/annotations
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-generated-sources
+ generate-sources
+
+ add-source
+
+
+
+ ${project.build.directory}/generated-sources/annotations
+ ${project.build.directory}/generated-sources/templates
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ true
+
+ false
+
+
+ @{argLine} -Xmx2048m --add-opens java.base/java.lang=ALL-UNNAMED -XX:+EnableDynamicAgentLoading
+
+ **/*Test.java
+ **/*Tests.java
+
+
+
+ %regex[.*CredentialStoreServiceIntegrationTest.*]
+ **/DataMovementStateMachineIntegrationTest.java
+ **/TaskOutcomeStateTransitionIntegrationTest.java
+ **/JobSubmissionStateMachineIntegrationTest.java
+ **/ProcessExecutionStateMachineIntegrationTest.java
+ **/WorkflowStateTransitionEndToEndTest.java
+ **/WorkflowTaskExecutionIntegrationTest.java
+ **/ProcessStateTransitionComprehensiveTest.java
+ **/ExperimentLifecycleIntegrationTest.java
+ **/StateTransitionValidationIntegrationTest.java
+ **/ExperimentStateTransitionIntegrationTest.java
+ **/JobStatusRepositoryTest.java
+ **/ExperimentSummaryRepositoryTest.java
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+ report
+ test
+
+ report
+
+
+
+ check
+
+ check
+
+
+
+ false
+
+
+ PACKAGE
+
+
+ LINE
+ COVEREDRATIO
+
+ 0.20
+
+
+
+
+
+ org.apache.airavata.agents.*
+ org.apache.airavata.api.server.handler.*
+
+ org.apache.airavata.parser.*
+ org.apache.airavata.compute.model.Job*
+
+ org.apache.airavata.gfac.cloud.*
+ org.apache.airavata.cloud.*
+
+ org.apache.airavata.monitoring.*
+
+ org.apache.airavata.api.server.util.*
+
+
+
+
+
+
+
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+ true
+
+
+ airavata/api-server
+
+ ${docker.api.server.build.directory}/Dockerfile
+
+
+
+
+
+
+
+ ${project.basedir}/src/test/java
+ ${project.build.directory}/test-classes
+
+
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/entity/AllocationProjectEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/entity/AllocationProjectEntity.java
new file mode 100644
index 00000000000..c58f7ec2e9f
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/entity/AllocationProjectEntity.java
@@ -0,0 +1,127 @@
+/**
+*
+* 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.accounting.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import java.io.Serializable;
+import java.time.Instant;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+/**
+ * Entity representing an HPC allocation project that grants compute time on a resource.
+ *
+ *
Allocation projects (also known as charge accounts or project codes) are issued by
+ * HPC centres to control and track resource consumption. A project is tied to a specific
+ * {@code resourceId} and identified by its {@code projectCode} which is passed to the
+ * scheduler at job submission time.
+ *
+ *
Users are associated with allocation projects via {@link CredentialAllocationProjectEntity}.
+ */
+@Entity
+@Table(name = "allocation_project")
+@EntityListeners(AuditingEntityListener.class)
+public class AllocationProjectEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @Column(name = "allocation_project_id")
+ private String allocationProjectId;
+
+ @Column(name = "project_code", nullable = false)
+ private String projectCode;
+
+ @Column(name = "resource_id", nullable = false)
+ private String resourceId;
+
+ @Column(name = "description", columnDefinition = "TEXT")
+ private String description;
+
+ @Column(name = "gateway_id", nullable = false)
+ private String gatewayId;
+
+ @CreatedDate
+ @Column(name = "created_at", nullable = false, updatable = false)
+ private Instant createdAt;
+
+ public AllocationProjectEntity() {}
+
+ public String getAllocationProjectId() {
+ return allocationProjectId;
+ }
+
+ public void setAllocationProjectId(String allocationProjectId) {
+ this.allocationProjectId = allocationProjectId;
+ }
+
+ public String getProjectCode() {
+ return projectCode;
+ }
+
+ public void setProjectCode(String projectCode) {
+ this.projectCode = projectCode;
+ }
+
+ public String getResourceId() {
+ return resourceId;
+ }
+
+ public void setResourceId(String resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ @Override
+ public String toString() {
+ return "AllocationProjectEntity{"
+ + "allocationProjectId='" + allocationProjectId + '\''
+ + ", projectCode='" + projectCode + '\''
+ + ", resourceId='" + resourceId + '\''
+ + ", gatewayId='" + gatewayId + '\''
+ + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/entity/CredentialAllocationProjectEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/entity/CredentialAllocationProjectEntity.java
new file mode 100644
index 00000000000..9ce6f67b8c6
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/entity/CredentialAllocationProjectEntity.java
@@ -0,0 +1,91 @@
+/**
+*
+* 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.accounting.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.IdClass;
+import jakarta.persistence.Table;
+import java.io.Serializable;
+
+/**
+ * Entity linking a credential to an {@link AllocationProjectEntity}.
+ *
+ *
Records that a credential ({@code credentialId}) is a member of the given allocation
+ * project ({@code allocationProjectId}). The {@code bindingId} references the
+ * {@link ResourceBindingEntity} that provides the credential context under
+ * which jobs are submitted to the project's associated resource.
+ *
+ *
The composite primary key {@code (CREDENTIAL_ID, ALLOCATION_PROJECT_ID)} is declared
+ * via {@link CredentialAllocationProjectPK} and the {@code @IdClass} annotation.
+ */
+@Entity
+@Table(name = "credential_allocation_project")
+@IdClass(CredentialAllocationProjectPK.class)
+public class CredentialAllocationProjectEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @Column(name = "credential_id")
+ private String credentialId;
+
+ @Id
+ @Column(name = "allocation_project_id")
+ private String allocationProjectId;
+
+ @Column(name = "binding_id", nullable = false)
+ private String bindingId;
+
+ public CredentialAllocationProjectEntity() {}
+
+ public String getCredentialId() {
+ return credentialId;
+ }
+
+ public void setCredentialId(String credentialId) {
+ this.credentialId = credentialId;
+ }
+
+ public String getAllocationProjectId() {
+ return allocationProjectId;
+ }
+
+ public void setAllocationProjectId(String allocationProjectId) {
+ this.allocationProjectId = allocationProjectId;
+ }
+
+ public String getBindingId() {
+ return bindingId;
+ }
+
+ public void setBindingId(String bindingId) {
+ this.bindingId = bindingId;
+ }
+
+ @Override
+ public String toString() {
+ return "CredentialAllocationProjectEntity{"
+ + "credentialId='" + credentialId + '\''
+ + ", allocationProjectId='" + allocationProjectId + '\''
+ + ", bindingId='" + bindingId + '\''
+ + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/entity/CredentialAllocationProjectPK.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/entity/CredentialAllocationProjectPK.java
new file mode 100644
index 00000000000..2116dfa48ab
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/entity/CredentialAllocationProjectPK.java
@@ -0,0 +1,82 @@
+/**
+*
+* 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.accounting.entity;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Composite primary key for {@link CredentialAllocationProjectEntity}.
+ *
+ *
Combines {@code credentialId} and {@code allocationProjectId} to uniquely identify
+ * the membership of a credential in an allocation project. Used with {@code @IdClass} on
+ * the owning entity.
+ */
+public class CredentialAllocationProjectPK implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private String credentialId;
+ private String allocationProjectId;
+
+ public CredentialAllocationProjectPK() {}
+
+ public CredentialAllocationProjectPK(String credentialId, String allocationProjectId) {
+ this.credentialId = credentialId;
+ this.allocationProjectId = allocationProjectId;
+ }
+
+ public String getCredentialId() {
+ return credentialId;
+ }
+
+ public void setCredentialId(String credentialId) {
+ this.credentialId = credentialId;
+ }
+
+ public String getAllocationProjectId() {
+ return allocationProjectId;
+ }
+
+ public void setAllocationProjectId(String allocationProjectId) {
+ this.allocationProjectId = allocationProjectId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ CredentialAllocationProjectPK that = (CredentialAllocationProjectPK) obj;
+ return Objects.equals(credentialId, that.credentialId)
+ && Objects.equals(allocationProjectId, that.allocationProjectId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(credentialId, allocationProjectId);
+ }
+
+ @Override
+ public String toString() {
+ return "CredentialAllocationProjectPK{"
+ + "credentialId='" + credentialId + '\''
+ + ", allocationProjectId='" + allocationProjectId + '\''
+ + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/mapper/AllocationProjectMapper.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/mapper/AllocationProjectMapper.java
new file mode 100644
index 00000000000..89f04c638b2
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/mapper/AllocationProjectMapper.java
@@ -0,0 +1,36 @@
+/**
+*
+* 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.accounting.mapper;
+
+import org.apache.airavata.accounting.entity.AllocationProjectEntity;
+import org.apache.airavata.accounting.model.AllocationProject;
+import org.apache.airavata.config.EntityMapperConfiguration;
+import org.apache.airavata.core.mapper.EntityMapper;
+import org.mapstruct.Mapper;
+
+/**
+ * MapStruct mapper for converting between {@link AllocationProjectEntity} and {@link AllocationProject}.
+ *
+ *
All fields map 1:1 with no lazy associations. No explicit {@code @Mapping}
+ * annotations are required. MapStruct generates a structurally complete implementation
+ * using the shared {@link EntityMapperConfiguration}.
+ */
+@Mapper(componentModel = "spring", config = EntityMapperConfiguration.class)
+public interface AllocationProjectMapper extends EntityMapper {}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/model/AllocationProject.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/model/AllocationProject.java
new file mode 100644
index 00000000000..0366a65c217
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/model/AllocationProject.java
@@ -0,0 +1,116 @@
+/**
+*
+* 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.accounting.model;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * Domain model: AllocationProject
+ * Represents a compute allocation project (e.g., an HPC account or charge code) granted on
+ * a specific {@link Resource}. Users are linked to allocation projects via
+ * {@link CredentialAllocationProject}. The {@code projectCode} is the identifier recognized by
+ * the scheduler (e.g., a SLURM account name).
+ */
+public class AllocationProject {
+ private String allocationProjectId;
+ /** Scheduler-level account or charge code (e.g., {@code "abc123"} for SLURM {@code --account}). */
+ private String projectCode;
+
+ private String resourceId;
+ private String description;
+ private String gatewayId;
+ private Instant createdAt;
+
+ public AllocationProject() {}
+
+ public String getAllocationProjectId() {
+ return allocationProjectId;
+ }
+
+ public void setAllocationProjectId(String allocationProjectId) {
+ this.allocationProjectId = allocationProjectId;
+ }
+
+ public String getProjectCode() {
+ return projectCode;
+ }
+
+ public void setProjectCode(String projectCode) {
+ this.projectCode = projectCode;
+ }
+
+ public String getResourceId() {
+ return resourceId;
+ }
+
+ public void setResourceId(String resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AllocationProject that = (AllocationProject) o;
+ return Objects.equals(allocationProjectId, that.allocationProjectId)
+ && Objects.equals(projectCode, that.projectCode)
+ && Objects.equals(resourceId, that.resourceId)
+ && Objects.equals(description, that.description)
+ && Objects.equals(gatewayId, that.gatewayId)
+ && Objects.equals(createdAt, that.createdAt);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(allocationProjectId, projectCode, resourceId, description, gatewayId, createdAt);
+ }
+
+ @Override
+ public String toString() {
+ return "AllocationProject{" + "allocationProjectId=" + allocationProjectId + ", projectCode=" + projectCode
+ + ", resourceId=" + resourceId + ", description=" + description + ", gatewayId=" + gatewayId
+ + ", createdAt=" + createdAt + "}";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/model/CredentialAllocationProject.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/model/CredentialAllocationProject.java
new file mode 100644
index 00000000000..77db3ef017a
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/model/CredentialAllocationProject.java
@@ -0,0 +1,87 @@
+/**
+*
+* 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.accounting.model;
+
+import java.util.Objects;
+
+/**
+ * Domain model: CredentialAllocationProject
+ * Join record that grants a credential access to an {@link AllocationProject}.
+ * The {@code bindingId} links to the {@link ResourceBinding} that defines
+ * which credential is used when submitting jobs against this allocation.
+ */
+public class CredentialAllocationProject {
+ /** Credential ID being granted access to the allocation project. */
+ private String credentialId;
+
+ private String allocationProjectId;
+ /**
+ * ID of the {@link ResourceBinding} that provides authentication context
+ * (credential + resource + login username) for this credential's allocation access.
+ */
+ private String bindingId;
+
+ public CredentialAllocationProject() {}
+
+ public String getCredentialId() {
+ return credentialId;
+ }
+
+ public void setCredentialId(String credentialId) {
+ this.credentialId = credentialId;
+ }
+
+ public String getAllocationProjectId() {
+ return allocationProjectId;
+ }
+
+ public void setAllocationProjectId(String allocationProjectId) {
+ this.allocationProjectId = allocationProjectId;
+ }
+
+ public String getBindingId() {
+ return bindingId;
+ }
+
+ public void setBindingId(String bindingId) {
+ this.bindingId = bindingId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CredentialAllocationProject that = (CredentialAllocationProject) o;
+ return Objects.equals(credentialId, that.credentialId)
+ && Objects.equals(allocationProjectId, that.allocationProjectId)
+ && Objects.equals(bindingId, that.bindingId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(credentialId, allocationProjectId, bindingId);
+ }
+
+ @Override
+ public String toString() {
+ return "CredentialAllocationProject{" + "credentialId=" + credentialId + ", allocationProjectId="
+ + allocationProjectId + ", bindingId=" + bindingId + "}";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/repository/AllocationProjectRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/repository/AllocationProjectRepository.java
new file mode 100644
index 00000000000..852ffedda12
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/repository/AllocationProjectRepository.java
@@ -0,0 +1,65 @@
+/**
+*
+* 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.accounting.repository;
+
+import java.util.List;
+import java.util.Optional;
+import org.apache.airavata.accounting.entity.AllocationProjectEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Spring Data JPA repository for {@link AllocationProjectEntity}.
+ *
+ *
Provides query methods for HPC allocation projects (charge accounts) that grant
+ * compute time on specific resources within a gateway. All finder methods use Spring
+ * Data JPA method naming conventions for automatic query derivation.
+ */
+@Repository
+public interface AllocationProjectRepository extends JpaRepository {
+
+ /**
+ * Find all allocation projects belonging to a specific gateway.
+ *
+ * @param gatewayId the gateway identifier
+ * @return list of allocation projects for the gateway, empty list if none found
+ */
+ List findByGatewayId(String gatewayId);
+
+ /**
+ * Find all allocation projects associated with a specific compute resource.
+ *
+ * @param resourceId the resource identifier
+ * @return list of allocation projects for the resource, empty list if none found
+ */
+ List findByResourceId(String resourceId);
+
+ /**
+ * Find an allocation project by its scheduler-level project code on a specific resource.
+ *
+ *
The combination of {@code projectCode} and {@code resourceId} uniquely identifies
+ * a single allocation project (scheduler account code is unique per resource).
+ *
+ * @param projectCode the scheduler account or charge code (e.g. SLURM {@code --account})
+ * @param resourceId the resource identifier
+ * @return Optional containing the allocation project if found
+ */
+ Optional findByProjectCodeAndResourceId(String projectCode, String resourceId);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/repository/CredentialAllocationProjectRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/repository/CredentialAllocationProjectRepository.java
new file mode 100644
index 00000000000..8715d45856e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/repository/CredentialAllocationProjectRepository.java
@@ -0,0 +1,82 @@
+/**
+*
+* 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.accounting.repository;
+
+import java.util.List;
+import org.apache.airavata.accounting.entity.CredentialAllocationProjectEntity;
+import org.apache.airavata.accounting.entity.CredentialAllocationProjectPK;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Spring Data JPA repository for {@link CredentialAllocationProjectEntity}.
+ *
+ *
Manages the join records that grant credentials access to HPC allocation projects.
+ * The entity uses a composite primary key ({@link CredentialAllocationProjectPK}) composed of
+ * {@code credentialId} and {@code allocationProjectId}.
+ *
+ *
Simple lookups use Spring Data JPA method naming conventions. The
+ * {@link #deleteByBindingId(String)} method uses an explicit {@code @Query} because
+ * deletion by a non-PK field cannot be derived automatically.
+ */
+@Repository
+public interface CredentialAllocationProjectRepository
+ extends JpaRepository {
+
+ /**
+ * Find all credential-allocation-project memberships for a specific allocation project.
+ *
+ * @param allocationProjectId the allocation project identifier
+ * @return list of memberships for the allocation project, empty list if none found
+ */
+ List findByAllocationProjectId(String allocationProjectId);
+
+ /**
+ * Find all allocation projects a specific credential is a member of.
+ *
+ * @param credentialId the credential identifier
+ * @return list of memberships for the credential, empty list if none found
+ */
+ List findByCredentialId(String credentialId);
+
+ /**
+ * Find all credential-allocation-project memberships backed by a specific credential-resource binding.
+ *
+ * @param bindingId the credential-resource binding identifier
+ * @return list of memberships referencing the binding, empty list if none found
+ */
+ List findByBindingId(String bindingId);
+
+ /**
+ * Delete all credential-allocation-project memberships associated with a given
+ * credential-resource binding. Called when a binding is removed to cascade
+ * removal of orphaned allocation project memberships.
+ *
+ * @param bindingId the credential-resource binding identifier
+ */
+ @Modifying
+ @Transactional
+ @Query("DELETE FROM CredentialAllocationProjectEntity u WHERE u.bindingId = :bindingId")
+ void deleteByBindingId(@Param("bindingId") String bindingId);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/service/AllocationProjectService.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/service/AllocationProjectService.java
new file mode 100644
index 00000000000..d79445e8635
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/service/AllocationProjectService.java
@@ -0,0 +1,53 @@
+/**
+*
+* 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.accounting.service;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.airavata.accounting.model.AllocationProject;
+
+public interface AllocationProjectService {
+
+ AllocationProject getAllocationProject(String allocationProjectId);
+
+ List getAllocationProjects(String gatewayId);
+
+ List getAllocationProjectsByResource(String resourceId);
+
+ AllocationProject findByProjectCodeAndResource(String projectCode, String resourceId);
+
+ String createAllocationProject(AllocationProject project);
+
+ void deleteAllocationProject(String allocationProjectId);
+
+ void syncFromBinding(
+ String bindingId, String resourceId, String gatewayId, String credentialId, Map metadata);
+
+ void cleanupForBinding(String bindingId);
+
+ List getProjectCredentials(String allocationProjectId);
+
+ /**
+ * Alias for {@link #getProjectCredentials(String)} matching the REST controller method name.
+ */
+ default List getProjectMembers(String allocationProjectId) {
+ return getProjectCredentials(allocationProjectId);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/service/DefaultAllocationProjectService.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/service/DefaultAllocationProjectService.java
new file mode 100644
index 00000000000..3ff9aa5bbb3
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/accounting/service/DefaultAllocationProjectService.java
@@ -0,0 +1,250 @@
+/**
+*
+* 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.accounting.service;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.airavata.accounting.entity.AllocationProjectEntity;
+import org.apache.airavata.accounting.entity.CredentialAllocationProjectEntity;
+import org.apache.airavata.accounting.entity.CredentialAllocationProjectPK;
+import org.apache.airavata.accounting.mapper.AllocationProjectMapper;
+import org.apache.airavata.accounting.model.AllocationProject;
+import org.apache.airavata.accounting.repository.AllocationProjectRepository;
+import org.apache.airavata.accounting.repository.CredentialAllocationProjectRepository;
+import org.apache.airavata.core.util.IdGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Default implementation of {@link AllocationProjectService}.
+ *
+ *
Manages the lifecycle of HPC allocation projects (charge accounts) and the credential
+ * memberships that grant credentials access to those projects. The service is the single point
+ * of truth for the {@code allocation_project} and {@code credential_allocation_project} tables.
+ *
+ *
The key domain operations are:
+ *
+ *
{@link #syncFromBinding}: Called whenever a resource binding is created or updated.
+ * Extracts a project code from the binding metadata, finds or creates the corresponding
+ * allocation project, then upserts the credential membership record for that binding.
+ * Idempotent — safe to call on every binding save.
+ *
{@link #cleanupForBinding}: Called when a resource binding is deleted. Removes all
+ * credential-allocation-project membership records that were provisioned through the
+ * deleted binding.
+ *
{@link #getProjectCredentials}: Returns the credential IDs of all members of a given
+ * allocation project.
When a resource binding is created or updated the binding may carry an
+ * {@code allocationProjectNumber} in its metadata map. This method:
+ *
+ *
Extracts the project code from metadata; returns immediately if absent or blank.
+ *
Finds the existing {@link AllocationProjectEntity} for that
+ * {@code (projectCode, resourceId)} pair, or creates a new one.
+ *
Upserts the {@link CredentialAllocationProjectEntity} record that links
+ * {@code credentialId} to the allocation project via the given {@code bindingId}.
+ *
+ *
+ *
The operation is idempotent: calling it repeatedly with the same arguments produces
+ * the same result.
+ *
+ * @param bindingId the credential-resource binding identifier
+ * @param resourceId the compute resource identifier
+ * @param gatewayId the gateway owning the allocation project
+ * @param credentialId the credential being granted project membership
+ * @param metadata the binding metadata map; may be null
+ */
+ @Override
+ public void syncFromBinding(
+ String bindingId, String resourceId, String gatewayId, String credentialId, Map metadata) {
+
+ if (metadata == null) {
+ return;
+ }
+ Object raw = metadata.get(METADATA_KEY_PROJECT_CODE);
+ if (raw == null) {
+ return;
+ }
+ String projectCode = raw.toString().trim();
+ if (projectCode.isBlank()) {
+ return;
+ }
+
+ // Find or create the allocation project for this (projectCode, resourceId) pair.
+ AllocationProjectEntity project = allocationProjectRepository
+ .findByProjectCodeAndResourceId(projectCode, resourceId)
+ .orElseGet(() -> {
+ var newProject = new AllocationProjectEntity();
+ newProject.setAllocationProjectId(IdGenerator.ensureId(null));
+ newProject.setProjectCode(projectCode);
+ newProject.setResourceId(resourceId);
+ newProject.setGatewayId(gatewayId);
+ AllocationProjectEntity saved = allocationProjectRepository.save(newProject);
+ logger.info(
+ "Auto-created allocation project id={} code={} resource={}",
+ saved.getAllocationProjectId(),
+ projectCode,
+ resourceId);
+ return saved;
+ });
+
+ // Upsert the credential membership for this binding.
+ CredentialAllocationProjectPK pk =
+ new CredentialAllocationProjectPK(credentialId, project.getAllocationProjectId());
+
+ if (!credentialAllocationProjectRepository.existsById(pk)) {
+ var membership = new CredentialAllocationProjectEntity();
+ membership.setCredentialId(credentialId);
+ membership.setAllocationProjectId(project.getAllocationProjectId());
+ membership.setBindingId(bindingId);
+ credentialAllocationProjectRepository.save(membership);
+ logger.debug(
+ "Linked credential={} to allocation project={} via binding={}",
+ credentialId,
+ project.getAllocationProjectId(),
+ bindingId);
+ } else {
+ logger.debug(
+ "Credential={} already member of allocation project={} — skipping insert",
+ credentialId,
+ project.getAllocationProjectId());
+ }
+ }
+
+ /**
+ * Remove all credential-allocation-project memberships provisioned through the given binding.
+ *
+ *
Called when a resource binding is deleted so that orphaned membership records are
+ * cleaned up. Allocation project records themselves are not deleted — a project may still
+ * be referenced by other bindings.
+ *
+ * @param bindingId the credential-resource binding being removed
+ */
+ @Override
+ public void cleanupForBinding(String bindingId) {
+ credentialAllocationProjectRepository.deleteByBindingId(bindingId);
+ logger.debug("Removed credential allocation project memberships for binding={}", bindingId);
+ }
+
+ // -------------------------------------------------------------------------
+ // Credential membership queries
+ // -------------------------------------------------------------------------
+
+ /**
+ * Return the credential IDs of all members of the given allocation project.
+ *
+ * @param allocationProjectId the allocation project identifier
+ * @return list of credential ID strings; empty if the project has no members
+ */
+ @Transactional(readOnly = true)
+ @Override
+ public List getProjectCredentials(String allocationProjectId) {
+ return credentialAllocationProjectRepository.findByAllocationProjectId(allocationProjectId).stream()
+ .map(CredentialAllocationProjectEntity::getCredentialId)
+ .toList();
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/UserContext.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/UserContext.java
new file mode 100644
index 00000000000..b21eee3aafa
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/UserContext.java
@@ -0,0 +1,53 @@
+/**
+*
+* 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.agent;
+
+import java.util.Map;
+import org.apache.airavata.iam.model.AuthzToken;
+
+public class UserContext {
+
+ private static final ThreadLocal AUTHZ_TOKEN = new ThreadLocal<>();
+
+ public static AuthzToken authzToken() {
+ return AUTHZ_TOKEN.get();
+ }
+
+ public static void setAuthzToken(AuthzToken token) {
+ AUTHZ_TOKEN.set(token);
+ }
+
+ public static String username() {
+ return getClaim("userName");
+ }
+
+ public static String gatewayId() {
+ return getClaim("gatewayID");
+ }
+
+ private static String getClaim(String claimId) {
+ return AUTHZ_TOKEN.get().getClaimsMap().entrySet().stream()
+ .filter(entry -> entry.getKey().equalsIgnoreCase(claimId))
+ .map(Map.Entry::getValue)
+ .findFirst()
+ .orElseThrow(() ->
+ new IllegalArgumentException("Missing '" + claimId + "' claim in the authentication token"));
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/config/ClusterApplicationConfiguration.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/config/ClusterApplicationConfiguration.java
new file mode 100644
index 00000000000..d9c4230aafb
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/config/ClusterApplicationConfiguration.java
@@ -0,0 +1,40 @@
+/**
+*
+* 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.agent.config;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "airavata.services.agent.appinterface")
+@ConditionalOnProperty(name = "airavata.services.agent.enabled", havingValue = "true", matchIfMissing = true)
+public class ClusterApplicationConfiguration {
+
+ private String id;
+
+ public String getApplicationInterfaceId() {
+ return id;
+ }
+
+ public void setApplicationInterfaceId(String applicationInterfaceId) {
+ this.id = applicationInterfaceId;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/AgentDeploymentInfoEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/AgentDeploymentInfoEntity.java
new file mode 100644
index 00000000000..d9e1767a851
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/AgentDeploymentInfoEntity.java
@@ -0,0 +1,77 @@
+/**
+*
+* 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.agent.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import org.hibernate.annotations.UuidGenerator;
+
+@Entity
+@Table(name = "agent_deployment_info")
+public class AgentDeploymentInfoEntity {
+
+ @Id
+ @Column(name = "agent_deployment_info_id")
+ @UuidGenerator
+ private String id;
+
+ @Column(unique = true, name = "user_friendly_name")
+ private String userFriendlyName;
+
+ @Column(name = "compute_resource_id")
+ private String computeResourceId;
+
+ @Column(name = "agent_application_id")
+ private String agentApplicationId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUserFriendlyName() {
+ return userFriendlyName;
+ }
+
+ public void setUserFriendlyName(String userFriendlyName) {
+ this.userFriendlyName = userFriendlyName;
+ }
+
+ public String getComputeResourceId() {
+ return computeResourceId;
+ }
+
+ public void setComputeResourceId(String computeResourceId) {
+ this.computeResourceId = computeResourceId;
+ }
+
+ public String getAgentApplicationId() {
+ return agentApplicationId;
+ }
+
+ public void setAgentApplicationId(String agentApplicationId) {
+ this.agentApplicationId = agentApplicationId;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/AgentExecutionEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/AgentExecutionEntity.java
new file mode 100644
index 00000000000..541588ce56d
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/AgentExecutionEntity.java
@@ -0,0 +1,65 @@
+/**
+*
+* 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.agent.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import org.hibernate.annotations.UuidGenerator;
+
+@Entity
+@Table(name = "agent_execution")
+public class AgentExecutionEntity {
+ @Id
+ @UuidGenerator
+ @Column(name = "agent_execution_id")
+ private String id;
+
+ @Column(name = "agent_id")
+ private String agentId;
+
+ @Column(name = "airavata_experiment_id")
+ private String airavataExperimentId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getAgentId() {
+ return agentId;
+ }
+
+ public void setAgentId(String agentId) {
+ this.agentId = agentId;
+ }
+
+ public String getAiravataExperimentId() {
+ return airavataExperimentId;
+ }
+
+ public void setAiravataExperimentId(String airavataExperimentId) {
+ this.airavataExperimentId = airavataExperimentId;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/AgentExecutionStatusEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/AgentExecutionStatusEntity.java
new file mode 100644
index 00000000000..55feeaec52d
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/AgentExecutionStatusEntity.java
@@ -0,0 +1,100 @@
+/**
+*
+* 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.agent.entity;
+
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.Id;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import org.hibernate.annotations.UuidGenerator;
+
+@Entity
+@Table(name = "agent_execution_status")
+public class AgentExecutionStatusEntity {
+
+ public enum ExecutionStatus {
+ SUBMITTED_TO_CLUSTER,
+ FAILED,
+ CONNECTED,
+ CONNECTION_BROKEN,
+ TERMINATING,
+ TERMINATED,
+ }
+
+ @Id
+ @UuidGenerator
+ @Column(name = "agent_execution_status_id")
+ private String id;
+
+ @ManyToOne(targetEntity = AgentExecutionEntity.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+ private AgentExecutionEntity agentExecution;
+
+ @Column(name = "updated_at")
+ private long updateTime;
+
+ @Column(name = "status")
+ private ExecutionStatus status;
+
+ @Column(name = "additional_info", length = 2000)
+ private String additionalInfo;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public AgentExecutionEntity getAgentExecution() {
+ return agentExecution;
+ }
+
+ public void setAgentExecution(AgentExecutionEntity agentExecution) {
+ this.agentExecution = agentExecution;
+ }
+
+ public long getUpdateTime() {
+ return updateTime;
+ }
+
+ public void setUpdateTime(long updateTime) {
+ this.updateTime = updateTime;
+ }
+
+ public ExecutionStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(ExecutionStatus status) {
+ this.status = status;
+ }
+
+ public String getAdditionalInfo() {
+ return additionalInfo;
+ }
+
+ public void setAdditionalInfo(String additionalInfo) {
+ this.additionalInfo = additionalInfo;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/ExperimentStorageCacheEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/ExperimentStorageCacheEntity.java
new file mode 100644
index 00000000000..b3ef0b18e1f
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/ExperimentStorageCacheEntity.java
@@ -0,0 +1,96 @@
+/**
+*
+* 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.agent.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Index;
+import jakarta.persistence.Table;
+import java.io.Serializable;
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
+
+@Entity
+@Table(
+ name = "experiment_storage_cache",
+ indexes = {@Index(name = "idx_exp_storage_experiment_id", columnList = "experiment_id")})
+public class ExperimentStorageCacheEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @Column(name = "cache_key", nullable = false, length = 512)
+ private String cacheKey;
+
+ @Column(name = "experiment_id", nullable = false, length = 255)
+ private String experimentId;
+
+ @Column(name = "path", nullable = false, length = 1024)
+ private String path;
+
+ @JdbcTypeCode(SqlTypes.JSON)
+ @Column(name = "response_json", nullable = false, columnDefinition = "json")
+ private String responseJson;
+
+ @Column(name = "cached_at", nullable = false)
+ private long cachedAt;
+
+ public ExperimentStorageCacheEntity() {}
+
+ public String getCacheKey() {
+ return cacheKey;
+ }
+
+ public void setCacheKey(String cacheKey) {
+ this.cacheKey = cacheKey;
+ }
+
+ public String getExperimentId() {
+ return experimentId;
+ }
+
+ public void setExperimentId(String experimentId) {
+ this.experimentId = experimentId;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public String getResponseJson() {
+ return responseJson;
+ }
+
+ public void setResponseJson(String responseJson) {
+ this.responseJson = responseJson;
+ }
+
+ public long getCachedAt() {
+ return cachedAt;
+ }
+
+ public void setCachedAt(long cachedAt) {
+ this.cachedAt = cachedAt;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/PlanEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/PlanEntity.java
new file mode 100644
index 00000000000..0f59fdb2007
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/entity/PlanEntity.java
@@ -0,0 +1,75 @@
+/**
+*
+* 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.agent.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "plan")
+public class PlanEntity {
+
+ @Id
+ @Column(name = "plan_id", nullable = false)
+ private String id;
+
+ @Column(name = "user_id", nullable = false)
+ private String userId;
+
+ @Column(name = "gateway_id", nullable = false)
+ private String gatewayId;
+
+ @Column(name = "data", columnDefinition = "TEXT")
+ private String data;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+}
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandExecutionRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandExecutionRequest.java
similarity index 96%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandExecutionRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandExecutionRequest.java
index a907b3f0152..83c8fd1fdb4 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandExecutionRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandExecutionRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
import java.util.ArrayList;
import java.util.List;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandExecutionResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandExecutionResponse.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandExecutionResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandExecutionResponse.java
index 664e387015e..3bd3f53af63 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandExecutionResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandExecutionResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentAsyncCommandExecutionResponse {
private String executionId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandListRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandListRequest.java
similarity index 94%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandListRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandListRequest.java
index 98bab12698b..97892ad722f 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandListRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandListRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentAsyncCommandListRequest {
private String agentId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandListResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandListResponse.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandListResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandListResponse.java
index 67962fb21f7..5e92fe836a1 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandListResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandListResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
import java.util.List;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandTerminateRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandTerminateRequest.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandTerminateRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandTerminateRequest.java
index dd7fa21a267..2f9680491d5 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandTerminateRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandTerminateRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentAsyncCommandTerminateRequest {
private String agentId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandTerminateResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandTerminateResponse.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandTerminateResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandTerminateResponse.java
index dcc2fd0ee2b..82cdd035e7a 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentAsyncCommandTerminateResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentAsyncCommandTerminateResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentAsyncCommandTerminateResponse {
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentCommandExecutionAck.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentCommandExecutionAck.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentCommandExecutionAck.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentCommandExecutionAck.java
index fad8ee49b85..cae6c980de4 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentCommandExecutionAck.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentCommandExecutionAck.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentCommandExecutionAck {
private String executionId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentCommandExecutionRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentCommandExecutionRequest.java
similarity index 96%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentCommandExecutionRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentCommandExecutionRequest.java
index 69ccbe6073b..8f90b00140d 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentCommandExecutionRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentCommandExecutionRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
import java.util.ArrayList;
import java.util.List;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentCommandExecutionResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentCommandExecutionResponse.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentCommandExecutionResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentCommandExecutionResponse.java
index f3f319163d9..1c1d3ad9f99 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentCommandExecutionResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentCommandExecutionResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentCommandExecutionResponse {
private String executionId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentEnvSetupAck.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentEnvSetupAck.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentEnvSetupAck.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentEnvSetupAck.java
index b8b6a650516..242a8074a3c 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentEnvSetupAck.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentEnvSetupAck.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentEnvSetupAck {
private String executionId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentEnvSetupRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentEnvSetupRequest.java
similarity index 96%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentEnvSetupRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentEnvSetupRequest.java
index 53469cf8536..048de0224e8 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentEnvSetupRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentEnvSetupRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
import java.util.ArrayList;
import java.util.List;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentEnvSetupResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentEnvSetupResponse.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentEnvSetupResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentEnvSetupResponse.java
index 636c424bb22..7432095239e 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentEnvSetupResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentEnvSetupResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentEnvSetupResponse {
private String executionId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentInfoResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentInfoResponse.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentInfoResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentInfoResponse.java
index d0db52311dd..183e206d579 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentInfoResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentInfoResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentInfoResponse {
private String agentId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentJupyterExecutionAck.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentJupyterExecutionAck.java
similarity index 93%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentJupyterExecutionAck.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentJupyterExecutionAck.java
index f35b497bdf2..4f716efd7e2 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentJupyterExecutionAck.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentJupyterExecutionAck.java
@@ -17,6 +17,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentJupyterExecutionAck extends AgentCommandExecutionAck {}
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentJupyterExecutionRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentJupyterExecutionRequest.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentJupyterExecutionRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentJupyterExecutionRequest.java
index c4c60525f06..7fefb2fb959 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentJupyterExecutionRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentJupyterExecutionRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentJupyterExecutionRequest {
private String agentId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentJupyterExecutionResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentJupyterExecutionResponse.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentJupyterExecutionResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentJupyterExecutionResponse.java
index 548bd92f5be..ba5e1282074 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentJupyterExecutionResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentJupyterExecutionResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentJupyterExecutionResponse {
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentKernelRestartAck.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentKernelRestartAck.java
similarity index 93%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentKernelRestartAck.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentKernelRestartAck.java
index fe0cb3c5954..7d0c0600f73 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentKernelRestartAck.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentKernelRestartAck.java
@@ -17,6 +17,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentKernelRestartAck extends AgentCommandExecutionAck {}
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentKernelRestartRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentKernelRestartRequest.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentKernelRestartRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentKernelRestartRequest.java
index c790191f131..d3ebea8850f 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentKernelRestartRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentKernelRestartRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentKernelRestartRequest {
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentKernelRestartResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentKernelRestartResponse.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentKernelRestartResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentKernelRestartResponse.java
index e65acc00b28..05772890170 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentKernelRestartResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentKernelRestartResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentKernelRestartResponse {
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentLaunchRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentLaunchRequest.java
similarity index 98%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentLaunchRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentLaunchRequest.java
index 0e655c07f13..01176a26cd0 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentLaunchRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentLaunchRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
import java.util.ArrayList;
import java.util.List;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentLaunchResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentLaunchResponse.java
similarity index 96%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentLaunchResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentLaunchResponse.java
index d3c6a8077d0..461cab49176 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentLaunchResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentLaunchResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentLaunchResponse {
private String agentId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentPythonExecutionAck.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentPythonExecutionAck.java
similarity index 93%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentPythonExecutionAck.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentPythonExecutionAck.java
index 2c972e1aa09..63bb39051c9 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentPythonExecutionAck.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentPythonExecutionAck.java
@@ -17,6 +17,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentPythonExecutionAck extends AgentCommandExecutionAck {}
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentPythonExecutionRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentPythonExecutionRequest.java
similarity index 96%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentPythonExecutionRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentPythonExecutionRequest.java
index 22f3ee7fc25..79c036c0553 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentPythonExecutionRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentPythonExecutionRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentPythonExecutionRequest {
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentPythonExecutionResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentPythonExecutionResponse.java
similarity index 96%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentPythonExecutionResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentPythonExecutionResponse.java
index 9e42814c926..250404fcd55 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentPythonExecutionResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentPythonExecutionResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentPythonExecutionResponse {
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTerminateResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTerminateResponse.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTerminateResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTerminateResponse.java
index 584653dc59f..f07befcb242 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTerminateResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTerminateResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentTerminateResponse {
private String experimentId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelAck.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelAck.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelAck.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelAck.java
index 1c43d4f267a..bec27671aaa 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelAck.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelAck.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentTunnelAck {
private String executionId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelCreateRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelCreateRequest.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelCreateRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelCreateRequest.java
index 651d0593bd9..7471612c3eb 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelCreateRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelCreateRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentTunnelCreateRequest {
private String agentId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelCreateResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelCreateResponse.java
similarity index 96%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelCreateResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelCreateResponse.java
index 6cdf0599329..ffd5deb4bc3 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelCreateResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelCreateResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentTunnelCreateResponse {
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelTerminateRequest.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelTerminateRequest.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelTerminateRequest.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelTerminateRequest.java
index 71825d614bc..bce25e4b7d5 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AgentTunnelTerminateRequest.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AgentTunnelTerminateRequest.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class AgentTunnelTerminateRequest {
private String agentId;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AsyncCommand.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AsyncCommand.java
similarity index 95%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AsyncCommand.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AsyncCommand.java
index abf70b5c990..41fc4df380a 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/AsyncCommand.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/AsyncCommand.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
import java.util.List;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/DirectoryInfo.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/DirectoryInfo.java
similarity index 94%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/DirectoryInfo.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/DirectoryInfo.java
index c024498766b..6217f24a0d3 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/DirectoryInfo.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/DirectoryInfo.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
public class DirectoryInfo {
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/ExperimentStorageResponse.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/ExperimentStorageResponse.java
similarity index 96%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/ExperimentStorageResponse.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/ExperimentStorageResponse.java
index 54af1cbd656..92a17ce64b1 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/ExperimentStorageResponse.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/ExperimentStorageResponse.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
import java.util.List;
diff --git a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/FileInfo.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/FileInfo.java
similarity index 89%
rename from modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/FileInfo.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/FileInfo.java
index f5f2e06275b..c99036b8976 100644
--- a/modules/agent-framework/agent-service/src/main/java/org/apache/airavata/agent/connection/service/models/FileInfo.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/model/FileInfo.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.agent.connection.service.models;
+package org.apache.airavata.agent.model;
import java.time.Instant;
@@ -26,7 +26,7 @@ public class FileInfo {
private boolean userHasWriteAccess;
private String name;
private String downloadURL;
- private String dataProductURI;
+ private String artifactUri;
private Instant createdTime;
private Instant modifiedTime;
private String mimeType;
@@ -57,12 +57,12 @@ public void setDownloadURL(String downloadURL) {
this.downloadURL = downloadURL;
}
- public String getDataProductURI() {
- return dataProductURI;
+ public String getArtifactUri() {
+ return artifactUri;
}
- public void setDataProductURI(String dataProductURI) {
- this.dataProductURI = dataProductURI;
+ public void setArtifactUri(String artifactUri) {
+ this.artifactUri = artifactUri;
}
public Instant getCreatedTime() {
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/AgentDeploymentInfoRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/AgentDeploymentInfoRepository.java
new file mode 100644
index 00000000000..cd84b1c374f
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/AgentDeploymentInfoRepository.java
@@ -0,0 +1,27 @@
+/**
+*
+* 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.agent.repository;
+
+import org.apache.airavata.agent.entity.AgentDeploymentInfoEntity;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface AgentDeploymentInfoRepository extends CrudRepository {}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/AgentExecutionRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/AgentExecutionRepository.java
new file mode 100644
index 00000000000..90a8f0b6f10
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/AgentExecutionRepository.java
@@ -0,0 +1,27 @@
+/**
+*
+* 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.agent.repository;
+
+import org.apache.airavata.agent.entity.AgentExecutionEntity;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface AgentExecutionRepository extends CrudRepository {}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/AgentExecutionStatusRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/AgentExecutionStatusRepository.java
new file mode 100644
index 00000000000..1a8f732e6be
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/AgentExecutionStatusRepository.java
@@ -0,0 +1,27 @@
+/**
+*
+* 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.agent.repository;
+
+import org.apache.airavata.agent.entity.AgentExecutionStatusEntity;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface AgentExecutionStatusRepository extends CrudRepository {}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/ExperimentStorageCacheRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/ExperimentStorageCacheRepository.java
new file mode 100644
index 00000000000..0745946a5db
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/ExperimentStorageCacheRepository.java
@@ -0,0 +1,35 @@
+/**
+*
+* 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.agent.repository;
+
+import java.util.Optional;
+import org.apache.airavata.agent.entity.ExperimentStorageCacheEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ExperimentStorageCacheRepository extends JpaRepository {
+
+ @Query("SELECT e FROM ExperimentStorageCacheEntity e WHERE e.cacheKey = :cacheKey AND e.cachedAt > :minTimestamp")
+ Optional findFreshEntry(
+ @Param("cacheKey") String cacheKey, @Param("minTimestamp") long minTimestamp);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/PlanRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/PlanRepository.java
new file mode 100644
index 00000000000..efc8c98e448
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/repository/PlanRepository.java
@@ -0,0 +1,31 @@
+/**
+*
+* 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.agent.repository;
+
+import java.util.List;
+import org.apache.airavata.agent.entity.PlanEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface PlanRepository extends JpaRepository {
+
+ List findAllByUserIdAndGatewayId(String userId, String gatewayId);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/service/AgentConnectionService.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/service/AgentConnectionService.java
new file mode 100644
index 00000000000..9200d92e236
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/service/AgentConnectionService.java
@@ -0,0 +1,98 @@
+/**
+*
+* 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.agent.service;
+
+import org.apache.airavata.agent.model.AgentAsyncCommandExecutionRequest;
+import org.apache.airavata.agent.model.AgentAsyncCommandExecutionResponse;
+import org.apache.airavata.agent.model.AgentAsyncCommandListRequest;
+import org.apache.airavata.agent.model.AgentAsyncCommandListResponse;
+import org.apache.airavata.agent.model.AgentAsyncCommandTerminateRequest;
+import org.apache.airavata.agent.model.AgentAsyncCommandTerminateResponse;
+import org.apache.airavata.agent.model.AgentCommandExecutionAck;
+import org.apache.airavata.agent.model.AgentCommandExecutionRequest;
+import org.apache.airavata.agent.model.AgentCommandExecutionResponse;
+import org.apache.airavata.agent.model.AgentEnvSetupAck;
+import org.apache.airavata.agent.model.AgentEnvSetupRequest;
+import org.apache.airavata.agent.model.AgentEnvSetupResponse;
+import org.apache.airavata.agent.model.AgentInfoResponse;
+import org.apache.airavata.agent.model.AgentJupyterExecutionAck;
+import org.apache.airavata.agent.model.AgentJupyterExecutionRequest;
+import org.apache.airavata.agent.model.AgentJupyterExecutionResponse;
+import org.apache.airavata.agent.model.AgentKernelRestartAck;
+import org.apache.airavata.agent.model.AgentKernelRestartRequest;
+import org.apache.airavata.agent.model.AgentKernelRestartResponse;
+import org.apache.airavata.agent.model.AgentPythonExecutionAck;
+import org.apache.airavata.agent.model.AgentPythonExecutionRequest;
+import org.apache.airavata.agent.model.AgentPythonExecutionResponse;
+import org.apache.airavata.agent.model.AgentTunnelAck;
+import org.apache.airavata.agent.model.AgentTunnelCreateRequest;
+import org.apache.airavata.agent.model.AgentTunnelCreateResponse;
+import org.apache.airavata.agent.model.AgentTunnelTerminateRequest;
+
+/**
+ * Interface for agent connection operations.
+ *
+ *
Defines the contract for managing agent communication — sending commands
+ * to agents and retrieving responses. The gRPC-based implementation lives in
+ * the {@code grpc-api} module; REST controllers in {@code rest-api} depend
+ * only on this interface.
+ */
+public interface AgentConnectionService {
+
+ AgentInfoResponse isAgentUp(String agentId);
+
+ AgentEnvSetupResponse getEnvSetupResponse(String executionId);
+
+ AgentCommandExecutionResponse getCommandExecutionResponse(String executionId);
+
+ AgentAsyncCommandExecutionResponse getAsyncCommandExecutionResponse(String executionId);
+
+ AgentAsyncCommandListResponse getAsyncCommandListResponse(String executionId);
+
+ AgentAsyncCommandTerminateResponse getAsyncCommandTerminateResponse(String executionId);
+
+ AgentJupyterExecutionResponse getJupyterExecutionResponse(String executionId);
+
+ AgentKernelRestartResponse getKernelRestartResponse(String executionId);
+
+ AgentPythonExecutionResponse getPythonExecutionResponse(String executionId);
+
+ AgentTunnelCreateResponse getTunnelCreateResponse(String executionId);
+
+ AgentEnvSetupAck runEnvSetupOnAgent(AgentEnvSetupRequest envSetupRequest);
+
+ AgentCommandExecutionAck runCommandOnAgent(AgentCommandExecutionRequest commandRequest);
+
+ AgentCommandExecutionAck runAsyncCommandOnAgent(AgentAsyncCommandExecutionRequest commandRequest);
+
+ AgentCommandExecutionAck runAsyncCommandListOnAgent(AgentAsyncCommandListRequest commandRequest);
+
+ AgentCommandExecutionAck runAsyncCommandTerminateOnAgent(AgentAsyncCommandTerminateRequest commandRequest);
+
+ AgentJupyterExecutionAck runJupyterOnAgent(AgentJupyterExecutionRequest jupyterExecutionRequest);
+
+ AgentPythonExecutionAck runPythonOnAgent(AgentPythonExecutionRequest pythonRunRequest);
+
+ AgentTunnelAck terminateTunnelOnAgent(AgentTunnelTerminateRequest tunnelTerminateRequest);
+
+ AgentTunnelAck runTunnelOnAgent(AgentTunnelCreateRequest tunnelRequest);
+
+ AgentKernelRestartAck runKernelRestartOnAgent(AgentKernelRestartRequest kernelRestartRequest);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/service/AgentManagementService.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/service/AgentManagementService.java
new file mode 100644
index 00000000000..820ac180257
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/service/AgentManagementService.java
@@ -0,0 +1,418 @@
+/**
+*
+* 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.agent.service;
+
+import jakarta.persistence.EntityNotFoundException;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import org.apache.airavata.agent.UserContext;
+import org.apache.airavata.agent.config.ClusterApplicationConfiguration;
+import org.apache.airavata.agent.model.AgentLaunchRequest;
+import org.apache.airavata.agent.model.AgentLaunchResponse;
+import org.apache.airavata.agent.model.AgentTerminateResponse;
+import org.apache.airavata.compute.resource.adapter.ResourceProfileAdapter;
+import org.apache.airavata.compute.resource.entity.ResourceBindingEntity;
+import org.apache.airavata.compute.resource.model.ComputationalResourceScheduling;
+import org.apache.airavata.compute.resource.model.ComputeResourceType;
+import org.apache.airavata.compute.resource.model.Resource;
+import org.apache.airavata.compute.resource.service.ResourceService;
+import org.apache.airavata.core.exception.CoreExceptions.AiravataSystemException;
+import org.apache.airavata.core.util.IdGenerator;
+import org.apache.airavata.execution.process.ProcessModel;
+import org.apache.airavata.research.application.adapter.ApplicationAdapter;
+import org.apache.airavata.research.experiment.model.Experiment;
+import org.apache.airavata.research.experiment.model.UserConfigurationData;
+import org.apache.airavata.research.experiment.service.ExperimentSearchService;
+import org.apache.airavata.research.experiment.service.ExperimentService;
+import org.apache.airavata.research.project.model.Project;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AgentManagementService {
+
+ private static final Logger logger = LoggerFactory.getLogger(AgentManagementService.class);
+ private static final long ONE_HOUR_MS = 60 * 60 * 1000;
+ private final ExperimentService experimentService;
+ private final ExperimentSearchService experimentSearchService;
+ private final ClusterApplicationConfiguration clusterApplicationConfig;
+ private final ResourceProfileAdapter resourceProfileAdapter;
+ private final ResourceService resourceService;
+ private final ApplicationAdapter applicationAdapter;
+ private final String storageResourceId;
+ private final String storagePath;
+
+ // no longer configurable; keep local default (used for the "server_url" app input)
+ private final String grpcHost = "localhost";
+
+ public AgentManagementService(
+ ExperimentService experimentService,
+ ExperimentSearchService experimentSearchService,
+ ClusterApplicationConfiguration clusterApplicationConfig,
+ ResourceProfileAdapter resourceProfileAdapter,
+ ResourceService resourceService,
+ ApplicationAdapter applicationAdapter,
+ @Value("${airavata.services.agent.storage.id}") String storageResourceId,
+ @Value("${airavata.services.agent.storage.path}") String storagePath) {
+ this.experimentService = experimentService;
+ this.experimentSearchService = experimentSearchService;
+ this.clusterApplicationConfig = clusterApplicationConfig;
+ this.resourceProfileAdapter = resourceProfileAdapter;
+ this.resourceService = resourceService;
+ this.applicationAdapter = applicationAdapter;
+ this.storageResourceId = storageResourceId;
+ this.storagePath = storagePath;
+ }
+
+ public AgentTerminateResponse terminateExperiment(String experimentId) {
+ try {
+ var experiment = experimentService.getExperiment(UserContext.authzToken(), experimentId);
+ experimentService.terminateExperiment(experiment.getExperimentId(), experiment.getGatewayId());
+ return new AgentTerminateResponse(experimentId, true);
+ } catch (Exception e) {
+ logger.error("Error terminating experiment {}", experimentId, e);
+ throw new IllegalStateException("Error terminating experiment with the id: " + experimentId, e);
+ }
+ }
+
+ public Experiment getExperiment(String experimentId) {
+ try {
+ var experiment = experimentService.getExperiment(UserContext.authzToken(), experimentId);
+ return experiment;
+ } catch (Exception e) {
+ logger.error("Error while extracting the experiment with the id: {}", experimentId);
+ throw new IllegalStateException("Error while extracting the experiment with the id: " + experimentId, e);
+ }
+ }
+
+ /**
+ * Meta-scheduling logic
+ */
+ public AgentLaunchRequest filterOptimumLaunchRequest(List launchRequests) throws Exception {
+ int leastRunningExpCount = Integer.MAX_VALUE;
+ var sortedLaunchRequest = launchRequests.get(0);
+
+ for (var req : launchRequests) {
+ var appInterfaceId = clusterApplicationConfig.getApplicationInterfaceId();
+ var experimentStatistics = experimentSearchService.getExperimentStatistics(
+ UserContext.gatewayId(),
+ IdGenerator.getUniqueTimestamp().toEpochMilli() - ONE_HOUR_MS,
+ IdGenerator.getUniqueTimestamp().toEpochMilli(),
+ null,
+ appInterfaceId,
+ null,
+ null,
+ 100,
+ 0);
+
+ int runningExperimentCount = experimentStatistics.getRunningExperimentCount();
+ int failedExperimentCount = experimentStatistics.getFailedExperimentCount();
+ logger.info(
+ "Running count {} failed count {} for appInterfaceId {}",
+ runningExperimentCount,
+ failedExperimentCount,
+ appInterfaceId);
+ if (runningExperimentCount + failedExperimentCount < leastRunningExpCount) {
+ leastRunningExpCount = runningExperimentCount + failedExperimentCount;
+ sortedLaunchRequest = req;
+ }
+ }
+ return sortedLaunchRequest;
+ }
+
+ private String generateEnvName(List libraries, List pip) {
+ var key = String.join(",", libraries) + "|" + String.join(",", pip);
+ return Integer.toHexString(key.hashCode());
+ }
+
+ public AgentLaunchResponse createAndLaunchExperiment(AgentLaunchRequest req) {
+ try {
+ var agentId = "agent_" + UUID.randomUUID().toString();
+ var envName = generateEnvName(req.getLibraries(), req.getPip());
+ logger.info("Creating an Airavata Experiment for {} with agent id {}", req.getExperimentName(), agentId);
+ var experiment = generateExperiment(req, agentId, envName);
+
+ var experimentId = experimentService.createExperiment(experiment.getGatewayId(), experiment);
+ logger.info("Launching the application, Id: {}, Name: {}", experimentId, experiment.getExperimentName());
+ experimentService.launchExperiment(UserContext.authzToken(), experiment.getGatewayId(), experimentId);
+ return new AgentLaunchResponse(agentId, experimentId, envName);
+ } catch (Exception e) {
+ logger.error("Error while creating the experiment with the name: {}", req.getExperimentName(), e);
+ throw new IllegalStateException(
+ "Error while creating the experiment with the name: " + req.getExperimentName(), e);
+ }
+ }
+
+ public void terminateApplication(String gatewayId, String experimentId) {
+ try {
+ logger.info("Terminating the application with experiment Id: {}", experimentId);
+ experimentService.terminateExperiment(experimentId, gatewayId);
+ } catch (Exception e) {
+ logger.error("Error while terminating the application with the experiment Id: {}", experimentId);
+ throw new IllegalStateException(
+ "Error while terminating the application with the experiment Id: " + experimentId, e);
+ }
+ }
+
+ public ProcessModel getEnvProcessModel(String expId) {
+ try {
+ logger.info("Extracting the process model for experiment id: {}", expId);
+ var expModel = experimentService.getExperiment(expId);
+ if (expModel.getProcesses() != null && !expModel.getProcesses().isEmpty()) {
+ return expModel.getProcesses().get(0);
+ } else {
+ logger.error("No process found for experiment id: {}", expId);
+ return null;
+ }
+ } catch (Exception e) {
+ logger.error("Error while extracting the process model for experiment id: {}", expId, e);
+ throw new IllegalStateException("Error while extracting the process model for experiment id: " + expId, e);
+ }
+ }
+
+ private Experiment generateExperiment(AgentLaunchRequest req, String agentId, String envName)
+ throws AiravataSystemException {
+ var experimentName = req.getExperimentName();
+ var projectName = req.getProjectName() != null ? req.getProjectName() : "Default Project";
+ var projectDir = projectName.replace(" ", "_");
+ var projectId = getProjectId(projectName);
+ var userName = UserContext.username();
+ var gatewayId = UserContext.gatewayId();
+ var appInterfaceId = clusterApplicationConfig.getApplicationInterfaceId();
+ var experimentModel = new Experiment();
+ experimentModel.setExperimentName(experimentName);
+ experimentModel.setProjectId(projectId);
+ experimentModel.setUserName(userName);
+ experimentModel.setGatewayId(gatewayId);
+ experimentModel.setApplicationId(appInterfaceId);
+
+ var computationalResourceSchedulingModel = new ComputationalResourceScheduling();
+ var binding = resolveBindingForCluster(req.getGroup(), req.getRemoteCluster(), gatewayId);
+ computationalResourceSchedulingModel.setQueueName(req.getQueue());
+ computationalResourceSchedulingModel.setNodeCount(req.getNodeCount());
+ computationalResourceSchedulingModel.setTotalCPUCount(req.getCpuCount());
+ computationalResourceSchedulingModel.setWallTimeLimit(req.getWallTime());
+ computationalResourceSchedulingModel.setTotalPhysicalMemory(req.getMemory());
+ computationalResourceSchedulingModel.setResourceHostId(binding.getResourceId());
+ computationalResourceSchedulingModel.setOverrideScratchLocation(
+ ResourceProfileAdapter.getMetadataString(binding.getMetadata(), "scratchLocation"));
+ computationalResourceSchedulingModel.setOverrideAllocationProjectNumber(extractSlurmAllocationProject(binding));
+ computationalResourceSchedulingModel.setOverrideLoginUserName(binding.getLoginUsername());
+
+ var userConfigurationDataModel = new UserConfigurationData();
+ userConfigurationDataModel.setComputationalResourceScheduling(computationalResourceSchedulingModel);
+ userConfigurationDataModel.setAiravataAutoSchedule(false);
+ userConfigurationDataModel.setOverrideManualScheduledParams(false);
+ userConfigurationDataModel.setInputStorageResourceId(
+ StringUtils.isNotBlank(req.getInputStorageId()) ? req.getInputStorageId() : storageResourceId);
+ userConfigurationDataModel.setOutputStorageResourceId(
+ StringUtils.isNotBlank(req.getOutputStorageId()) ? req.getInputStorageId() : storageResourceId);
+ var experimentDataDir = Paths.get(storagePath, gatewayId, userName, projectDir, experimentName)
+ .toString();
+ userConfigurationDataModel.setExperimentDataDir(experimentDataDir);
+ // groupResourceProfileId now stores the resource binding ID
+ userConfigurationDataModel.setGroupResourceProfileId(binding.getBindingId());
+
+ experimentModel.setUserConfigurationData(userConfigurationDataModel);
+
+ var applicationInputs = applicationAdapter.getApplicationInputs(appInterfaceId);
+ var experimentInputs = applicationInputs.stream()
+ .map(appInput -> {
+ var ei = new org.apache.airavata.research.experiment.model.ExperimentInput();
+ ei.setName(appInput.getName());
+ ei.setType(
+ appInput.getType() != null
+ ? appInput.getType()
+ : org.apache.airavata.storage.resource.model.DataType.STRING);
+ ei.setValue(appInput.getValue());
+ ei.setRequired(appInput.getIsRequired());
+ ei.setAddToCommandLine(appInput.getRequiredToAddedToCommandLine());
+ ei.setOrderIndex(appInput.getInputOrder());
+ ei.setDescription(appInput.getApplicationArgument());
+ if (appInput.getName() != null) {
+ switch (appInput.getName()) {
+ case "agent_id" -> ei.setValue(agentId);
+ case "env_name" -> ei.setValue(envName);
+ case "server_url" -> ei.setValue(grpcHost);
+ case "libraries" ->
+ ei.setValue(req.getLibraries() != null ? String.join(",", req.getLibraries()) : "");
+ case "pip" -> ei.setValue(req.getPip() != null ? String.join(",", req.getPip()) : "");
+ case "mounts" ->
+ ei.setValue(req.getMounts() != null ? String.join(",", req.getMounts()) : "");
+ default -> {}
+ }
+ }
+ return ei;
+ })
+ .collect(Collectors.toList());
+
+ experimentModel.setInputs(experimentInputs);
+ var applicationOutputs = applicationAdapter.getApplicationOutputs(appInterfaceId);
+ experimentModel.setOutputs(applicationOutputs.stream()
+ .map(appOutput -> {
+ var eo = new org.apache.airavata.research.experiment.model.ExperimentOutput();
+ eo.setName(appOutput.getName());
+ eo.setType(
+ appOutput.getType() != null
+ ? appOutput.getType()
+ : org.apache.airavata.storage.resource.model.DataType.STRING);
+ eo.setValue(appOutput.getValue());
+ eo.setRequired(appOutput.getIsRequired());
+ eo.setDescription(appOutput.getApplicationArgument());
+ return eo;
+ })
+ .collect(Collectors.toList()));
+ logger.info("Generated the experiment: {}", experimentModel.getExperimentId());
+
+ return experimentModel;
+ }
+
+ /**
+ * Resolve the SLURM allocation project number from the binding metadata.
+ *
+ *
Returns the {@code allocationProjectNumber} metadata value only when the
+ * resource's compute capability is SLURM. Returns {@code null} for all other
+ * resource types or when the metadata key is absent.
+ *
+ * @param binding the resource binding whose metadata and resource capabilities to inspect
+ * @return the allocation project number string, or {@code null}
+ */
+ private String extractSlurmAllocationProject(ResourceBindingEntity binding) {
+ Resource resource = resourceService.getResource(binding.getResourceId());
+ if (resource != null
+ && resource.getCapabilities() != null
+ && resource.getCapabilities().getCompute() != null) {
+ ComputeResourceType resourceType =
+ resource.getCapabilities().getCompute().getComputeResourceType();
+ if (resourceType == ComputeResourceType.SLURM) {
+ return ResourceProfileAdapter.getMetadataString(binding.getMetadata(), "allocationProjectNumber");
+ }
+ }
+ return null;
+ }
+
+ private String getProjectId(String projectName) {
+ int limit = 10;
+ int offset = 0;
+
+ while (true) {
+ List userProjects;
+ try {
+ userProjects = experimentService.getUserProjects(
+ UserContext.authzToken(), UserContext.gatewayId(), UserContext.username(), limit, offset);
+ } catch (Exception e) {
+ String msg = String.format(
+ "Error getting user projects: projectName=%s, gatewayId=%s, username=%s, limit=%d, offset=%d. Reason: %s",
+ projectName, UserContext.gatewayId(), UserContext.username(), limit, offset, e.getMessage());
+ logger.error(msg, e);
+ throw new IllegalStateException(msg, e);
+ }
+
+ var defaultProject = userProjects.stream()
+ .filter(project -> projectName.equals(project.getProjectName()))
+ .findFirst();
+
+ if (defaultProject.isPresent()) {
+ return defaultProject.get().getProjectId();
+ }
+ if (userProjects.size() < limit) {
+ break;
+ }
+ offset += limit;
+ }
+
+ throw new EntityNotFoundException(
+ "Could not find project: " + projectName + " for the user: " + UserContext.username());
+ }
+
+ /**
+ * Resolve the resource binding for the given cluster identifier within the current gateway.
+ *
+ *
GroupResourceProfile and GroupComputeResourcePreference have been removed from the model.
+ * Resources and their bindings are now looked up directly from the resource and binding
+ * repositories. The {@code group} parameter previously referred to a named group resource
+ * profile; it is now used as an optional secondary filter on the resource name. The
+ * {@code remoteCluster} is matched against the start of both the resource name and the
+ * resource hostname to identify the target compute resource.
+ *
+ *
Lookup order:
+ *
+ *
List all resources for the gateway.
+ *
Filter resources whose name or hostname starts with {@code remoteCluster} (case-insensitive).
+ * If {@code group} is also provided, further narrow to resources whose name contains {@code group}.
+ *
For each candidate resource, attempt to find a binding via
+ * {@link ResourceProfileAdapter#getBinding(String, String)}.
+ *
Return the first binding found, or throw if none match.
+ *
+ *
+ * @param group optional group/profile name hint used as a secondary resource name filter
+ * @param remoteCluster prefix matched against resource name or hostname
+ * @param gatewayId the gateway to scope the lookup to
+ * @return the resolved binding entity
+ * @throws EntityNotFoundException if no matching binding can be found
+ */
+ private ResourceBindingEntity resolveBindingForCluster(String group, String remoteCluster, String gatewayId) {
+ List gatewayResources = resourceService.getResources(gatewayId);
+
+ if (gatewayResources == null || gatewayResources.isEmpty()) {
+ throw new EntityNotFoundException("No compute resources registered for gateway: " + gatewayId
+ + ". Cannot resolve binding for cluster: " + remoteCluster);
+ }
+
+ String clusterLower = remoteCluster != null ? remoteCluster.toLowerCase() : "";
+ String groupLower = StringUtils.isNotBlank(group) ? group.toLowerCase() : null;
+
+ for (Resource resource : gatewayResources) {
+ String nameLower = resource.getName() != null ? resource.getName().toLowerCase() : "";
+ String hostLower =
+ resource.getHostName() != null ? resource.getHostName().toLowerCase() : "";
+
+ boolean matchesCluster = nameLower.startsWith(clusterLower) || hostLower.startsWith(clusterLower);
+ if (!matchesCluster) {
+ continue;
+ }
+
+ // When a group hint is given, apply it as an additional filter on resource name.
+ if (groupLower != null && !nameLower.contains(groupLower)) {
+ continue;
+ }
+
+ ResourceBindingEntity binding = resourceProfileAdapter.getBinding(resource.getResourceId(), gatewayId);
+ if (binding != null) {
+ logger.debug(
+ "Resolved binding id={} for cluster={}, group={}, gatewayId={}",
+ binding.getBindingId(),
+ remoteCluster,
+ group,
+ gatewayId);
+ return binding;
+ }
+ }
+
+ throw new EntityNotFoundException("Could not find a resource binding for cluster='" + remoteCluster
+ + "', group='" + group + "', gatewayId='" + gatewayId
+ + "' for user: " + UserContext.username());
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/service/PlanHandler.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/service/PlanHandler.java
new file mode 100644
index 00000000000..93821bc7048
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/agent/service/PlanHandler.java
@@ -0,0 +1,55 @@
+/**
+*
+* 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.agent.service;
+
+import java.util.List;
+import org.apache.airavata.agent.entity.PlanEntity;
+import org.apache.airavata.agent.repository.PlanRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@Transactional
+public class PlanHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(PlanHandler.class);
+
+ private final PlanRepository planRepo;
+
+ public PlanHandler(PlanRepository planRepo) {
+ this.planRepo = planRepo;
+ }
+
+ public PlanEntity savePlan(PlanEntity plan) {
+ var savedPlan = planRepo.save(plan);
+ logger.info("Created the plan with the id: {}", plan.getId());
+ return savedPlan;
+ }
+
+ public List getAllPlansByUserId(String userId, String gatewayId) {
+ return planRepo.findAllByUserIdAndGatewayId(userId, gatewayId);
+ }
+
+ public PlanEntity getPlanById(String planId) {
+ return planRepo.findById(planId).orElseThrow(() -> new IllegalArgumentException("Plan not found: " + planId));
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/ComputeProvider.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/ComputeProvider.java
new file mode 100644
index 00000000000..5f2f1bdc776
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/ComputeProvider.java
@@ -0,0 +1,56 @@
+/**
+*
+* 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.compute.provider;
+
+import org.apache.airavata.core.model.DagTaskResult;
+import org.apache.airavata.execution.dag.TaskContext;
+
+/**
+ * Full lifecycle contract for a compute provider.
+ *
+ *
Each compute backend (SLURM, AWS, Local) implements this interface as a
+ * single class covering all lifecycle phases. The methods are registered as
+ * individual {@link org.apache.airavata.execution.dag.DagTask} beans via
+ * {@link ComputeProviderConfiguration} so the DAG engine can invoke them by name.
+ *
+ *
Lifecycle-independent logic (parsers, job specs, SSH adapters) stays in
+ * separate utility classes.
+ */
+public interface ComputeProvider {
+
+ // --- Resource lifecycle ---
+
+ /** Acquire and prepare compute resources (e.g. create working directory, launch EC2). */
+ DagTaskResult provision(TaskContext context);
+
+ /** Release compute resources (cleanup working directory, terminate instance). */
+ DagTaskResult deprovision(TaskContext context);
+
+ // --- Job lifecycle ---
+
+ /** Submit a job to the provisioned compute resource. */
+ DagTaskResult submit(TaskContext context);
+
+ /** Poll job status until the job reaches a terminal state. */
+ DagTaskResult monitor(TaskContext context);
+
+ /** Cancel running jobs on the compute resource. */
+ DagTaskResult cancel(TaskContext context);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/ComputeProviderConfiguration.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/ComputeProviderConfiguration.java
new file mode 100644
index 00000000000..19cd8f37eca
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/ComputeProviderConfiguration.java
@@ -0,0 +1,121 @@
+/**
+*
+* 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.compute.provider;
+
+import org.apache.airavata.compute.provider.aws.AwsComputeProvider;
+import org.apache.airavata.compute.provider.local.LocalComputeProvider;
+import org.apache.airavata.compute.provider.slurm.SlurmComputeProvider;
+import org.apache.airavata.config.ServiceConditionals.ConditionalOnParticipant;
+import org.apache.airavata.execution.dag.DagTask;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Registers {@link DagTask} beans that delegate to {@link ComputeProvider} lifecycle methods.
+ *
+ *
Bean names match those referenced in
+ * {@link org.apache.airavata.execution.dag.DAGTemplates} so the DAG engine resolves
+ * the correct provider method for each node.
+ */
+@Configuration
+@ConditionalOnParticipant
+public class ComputeProviderConfiguration {
+
+ // ---- AWS ----------------------------------------------------------------
+
+ @Bean("awsProvisioningTask")
+ DagTask awsProvision(AwsComputeProvider p) {
+ return p::provision;
+ }
+
+ @Bean("awsSubmitTask")
+ DagTask awsSubmit(AwsComputeProvider p) {
+ return p::submit;
+ }
+
+ @Bean("awsMonitoringTask")
+ DagTask awsMonitor(AwsComputeProvider p) {
+ return p::monitor;
+ }
+
+ @Bean("awsCancelTask")
+ DagTask awsCancel(AwsComputeProvider p) {
+ return p::cancel;
+ }
+
+ @Bean("awsDeprovisioningTask")
+ DagTask awsDeprovision(AwsComputeProvider p) {
+ return p::deprovision;
+ }
+
+ // ---- SLURM --------------------------------------------------------------
+
+ @Bean("slurmProvisioningTask")
+ DagTask slurmProvision(SlurmComputeProvider p) {
+ return p::provision;
+ }
+
+ @Bean("slurmSubmitTask")
+ DagTask slurmSubmit(SlurmComputeProvider p) {
+ return p::submit;
+ }
+
+ @Bean("slurmMonitoringTask")
+ DagTask slurmMonitor(SlurmComputeProvider p) {
+ return p::monitor;
+ }
+
+ @Bean("slurmCancelTask")
+ DagTask slurmCancel(SlurmComputeProvider p) {
+ return p::cancel;
+ }
+
+ @Bean("slurmDeprovisioningTask")
+ DagTask slurmDeprovision(SlurmComputeProvider p) {
+ return p::deprovision;
+ }
+
+ // ---- Local (PLAIN) ------------------------------------------------------
+
+ @Bean("localProvisioningTask")
+ DagTask localProvision(LocalComputeProvider p) {
+ return p::provision;
+ }
+
+ @Bean("localSubmitTask")
+ DagTask localSubmit(LocalComputeProvider p) {
+ return p::submit;
+ }
+
+ @Bean("localMonitoringTask")
+ DagTask localMonitor(LocalComputeProvider p) {
+ return p::monitor;
+ }
+
+ @Bean("localCancelTask")
+ DagTask localCancel(LocalComputeProvider p) {
+ return p::cancel;
+ }
+
+ @Bean("localDeprovisioningTask")
+ DagTask localDeprovision(LocalComputeProvider p) {
+ return p::deprovision;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/aws/AwsComputeProvider.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/aws/AwsComputeProvider.java
new file mode 100644
index 00000000000..d616c9d6638
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/aws/AwsComputeProvider.java
@@ -0,0 +1,619 @@
+/**
+*
+* 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.compute.provider.aws;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.airavata.compute.provider.ComputeProvider;
+import org.apache.airavata.compute.resource.adapter.ComputeResourceAdapter;
+import org.apache.airavata.compute.resource.adapter.ResourceProfileAdapter;
+import org.apache.airavata.compute.resource.entity.ResourceBindingEntity;
+import org.apache.airavata.compute.resource.model.Job;
+import org.apache.airavata.compute.resource.model.JobState;
+import org.apache.airavata.compute.resource.service.JobService;
+import org.apache.airavata.compute.resource.submission.JobManagerSpec;
+import org.apache.airavata.compute.resource.submission.JobSubmissionData;
+import org.apache.airavata.compute.resource.submission.JobSubmissionDataBuilder;
+import org.apache.airavata.compute.resource.submission.JobSubmissionSupport;
+import org.apache.airavata.compute.resource.submission.RawCommandInfo;
+import org.apache.airavata.config.ServiceConditionals.ConditionalOnParticipant;
+import org.apache.airavata.core.model.DagTaskResult;
+import org.apache.airavata.credential.model.SSHCredential;
+import org.apache.airavata.execution.dag.TaskContext;
+import org.apache.airavata.execution.orchestration.ComputeSubmissionTracker;
+import org.apache.airavata.execution.process.ProcessService;
+import org.apache.airavata.iam.service.CredentialStoreService;
+import org.apache.airavata.protocol.AgentAdapter;
+import org.apache.airavata.protocol.AgentAdapter.CommandOutput;
+import org.apache.airavata.protocol.ssh.SSHJAgentAdapter;
+import org.apache.airavata.protocol.ssh.SSHUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import software.amazon.awssdk.core.exception.SdkException;
+import software.amazon.awssdk.services.ec2.Ec2Client;
+import software.amazon.awssdk.services.ec2.model.CreateKeyPairResponse;
+import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupResponse;
+import software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest;
+import software.amazon.awssdk.services.ec2.model.DescribeInstancesResponse;
+import software.amazon.awssdk.services.ec2.model.Ec2Exception;
+import software.amazon.awssdk.services.ec2.model.Instance;
+import software.amazon.awssdk.services.ec2.model.InstanceStateName;
+import software.amazon.awssdk.services.ec2.model.InstanceType;
+import software.amazon.awssdk.services.ec2.model.RunInstancesRequest;
+import software.amazon.awssdk.services.ec2.model.RunInstancesResponse;
+
+/**
+ * AWS compute provider — covers the full lifecycle of EC2-backed job execution.
+ *
+ *
+ */
+@Component
+@ConditionalOnParticipant
+public class AwsComputeProvider implements ComputeProvider {
+
+ private static final Logger logger = LoggerFactory.getLogger(AwsComputeProvider.class);
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ // AWS provider context keys
+ private static final String AWS_INSTANCE_ID = "AWS_INSTANCE_ID";
+ private static final String AWS_SECURITY_GROUP_ID = "AWS_SECURITY_GROUP_ID";
+ private static final String AWS_KEY_PAIR_NAME = "AWS_KEY_PAIR_NAME";
+ private static final String AWS_SSH_CREDENTIAL_TOKEN = "AWS_SSH_CREDENTIAL_TOKEN";
+ private static final String AWS_PUBLIC_IP = "AWS_PUBLIC_IP";
+ private static final String AWS_JOB_ID = "AWS_JOB_ID";
+
+ private final AwsTaskUtil awsTaskUtil;
+ private final ProcessService processService;
+ private final CredentialStoreService credentialStoreService;
+ private final JobSubmissionSupport jobSubmissionSupport;
+ private final JobSubmissionDataBuilder jobSubmissionDataBuilder;
+ private final ComputeSubmissionTracker computeSubmissionTracker;
+ private final JobService jobService;
+ private final ComputeResourceAdapter computeResourceAdapter;
+ private final ResourceProfileAdapter resourceProfileAdapter;
+
+ public AwsComputeProvider(
+ AwsTaskUtil awsTaskUtil,
+ ProcessService processService,
+ CredentialStoreService credentialStoreService,
+ JobSubmissionSupport jobSubmissionSupport,
+ JobSubmissionDataBuilder jobSubmissionDataBuilder,
+ ComputeSubmissionTracker computeSubmissionTracker,
+ JobService jobService,
+ ComputeResourceAdapter computeResourceAdapter,
+ ResourceProfileAdapter resourceProfileAdapter) {
+ this.awsTaskUtil = awsTaskUtil;
+ this.processService = processService;
+ this.credentialStoreService = credentialStoreService;
+ this.jobSubmissionSupport = jobSubmissionSupport;
+ this.jobSubmissionDataBuilder = jobSubmissionDataBuilder;
+ this.computeSubmissionTracker = computeSubmissionTracker;
+ this.jobService = jobService;
+ this.computeResourceAdapter = computeResourceAdapter;
+ this.resourceProfileAdapter = resourceProfileAdapter;
+ }
+
+ // =========================================================================
+ // Provision — create EC2 infrastructure
+ // =========================================================================
+
+ @Override
+ public DagTaskResult provision(TaskContext context) {
+ logger.info("Starting AWS provisioning for process {}", context.getProcessId());
+
+ Ec2Client ec2Client = null;
+
+ try {
+ String credentialToken = null;
+
+ // Resolve AWS configuration: binding metadata takes priority over resourceSchedule.
+ // Operators configure awsRegion/awsAmiId/awsInstanceType in the ResourceBinding
+ // metadata for this compute resource; resourceSchedule values serve as per-experiment
+ // overrides when the binding does not carry a value.
+ ResourceBindingEntity binding =
+ resourceProfileAdapter.getBinding(context.getComputeResourceId(), context.getGatewayId());
+ String region = resolveAwsConfig(binding, context, "awsRegion", "us-east-1");
+
+ ec2Client = awsTaskUtil.buildEc2Client(credentialToken, context.getGatewayId(), region);
+
+ String securityGroupId = createSecurityGroup(ec2Client, context);
+ saveProviderState(context, AWS_SECURITY_GROUP_ID, securityGroupId);
+ logger.info("Created security group: {}", securityGroupId);
+
+ String keyPairName = "airavata-key-" + context.getProcessId();
+ CreateKeyPairResponse kpRes = ec2Client.createKeyPair(req -> req.keyName(keyPairName));
+ saveProviderState(context, AWS_KEY_PAIR_NAME, keyPairName);
+
+ String privateKeyPEM = kpRes.keyMaterial();
+ String publicKey = SSHUtil.generatePublicKey(privateKeyPEM);
+
+ String sshCredentialToken = saveSSHCredential(privateKeyPEM, publicKey, context.getGatewayId());
+ saveProviderState(context, AWS_SSH_CREDENTIAL_TOKEN, sshCredentialToken);
+ logger.info("Created key pair {} with credential token {}", keyPairName, sshCredentialToken);
+
+ String amiId = resolveAwsConfig(binding, context, "awsAmiId", null);
+ String instanceType = resolveAwsConfig(binding, context, "awsInstanceType", "t2.micro");
+
+ RunInstancesRequest runRequest = RunInstancesRequest.builder()
+ .imageId(amiId)
+ .instanceType(InstanceType.fromValue(instanceType))
+ .keyName(keyPairName)
+ .securityGroupIds(securityGroupId)
+ .minCount(1)
+ .maxCount(1)
+ .build();
+ RunInstancesResponse runResponse = ec2Client.runInstances(runRequest);
+
+ if (runResponse.instances() == null || runResponse.instances().isEmpty()) {
+ awsTaskUtil.terminateEC2Instance(context, context.getGatewayId());
+ return new DagTaskResult.Failure("No instances were launched by AWS", false);
+ }
+
+ String instanceId = runResponse.instances().get(0).instanceId();
+ saveProviderState(context, AWS_INSTANCE_ID, instanceId);
+ logger.info("Launched EC2 instance {}", instanceId);
+
+ return new DagTaskResult.Success("AWS provisioning completed for " + context.getTaskId());
+
+ } catch (Ec2Exception e) {
+ String errorCode = e.awsErrorDetails() != null ? e.awsErrorDetails().errorCode() : "UNKNOWN";
+ logger.error("AWS EC2 error for process {}: {}", context.getProcessId(), errorCode, e);
+ awsTaskUtil.terminateEC2Instance(context, context.getGatewayId());
+ boolean fatal = errorCode.contains("InvalidAMIID")
+ || errorCode.contains("UnauthorizedOperation")
+ || errorCode.contains("AuthFailure");
+ return new DagTaskResult.Failure("AWS EC2 error: " + errorCode + " - " + e.getMessage(), fatal, e);
+
+ } catch (SdkException e) {
+ logger.error("AWS SDK error for process {}", context.getProcessId(), e);
+ awsTaskUtil.terminateEC2Instance(context, context.getGatewayId());
+ return new DagTaskResult.Failure("AWS SDK error: " + e.getMessage(), false, e);
+
+ } catch (Exception e) {
+ logger.error("Unexpected error during AWS provisioning for process {}", context.getProcessId(), e);
+ awsTaskUtil.terminateEC2Instance(context, context.getGatewayId());
+ return new DagTaskResult.Failure("Unexpected error during AWS provisioning", false, e);
+
+ } finally {
+ if (ec2Client != null) {
+ ec2Client.close();
+ }
+ }
+ }
+
+ // =========================================================================
+ // Submit — verify instance readiness and execute job via SSH
+ // =========================================================================
+
+ @Override
+ public DagTaskResult submit(TaskContext context) {
+ logger.info("Starting AWS job submission for process {}", context.getProcessId());
+
+ try {
+ String instanceId = getProviderState(context, AWS_INSTANCE_ID);
+ String sshCredentialToken = getProviderState(context, AWS_SSH_CREDENTIAL_TOKEN);
+ String awsCredentialToken = null;
+
+ if (instanceId == null || sshCredentialToken == null) {
+ logger.error(
+ "Could not find instanceId: {} or sshCredentialToken: {} in the AWS process context {}",
+ instanceId,
+ sshCredentialToken,
+ context.getProcessId());
+ return new DagTaskResult.Failure(
+ "Could not find instanceId: " + instanceId + " or sshCredentialToken: " + sshCredentialToken
+ + " in the AWS process context: " + context.getProcessId(),
+ true);
+ }
+
+ ResourceBindingEntity binding =
+ resourceProfileAdapter.getBinding(context.getComputeResourceId(), context.getGatewayId());
+ String region = resolveAwsConfig(binding, context, "awsRegion", "us-east-1");
+
+ String publicIpAddress = verifyInstanceIsRunning(awsCredentialToken, instanceId, region, context);
+ logger.info("Instance {} is verified and running at IP: {}", instanceId, publicIpAddress);
+ saveProviderState(context, AWS_PUBLIC_IP, publicIpAddress);
+
+ SSHJAgentAdapter adapter = initSSHJAgentAdapter(sshCredentialToken, publicIpAddress, context);
+
+ JobManagerSpec jobManagerConfig =
+ jobSubmissionSupport.getJobManagerConfiguration(context.getComputeResource());
+ JobSubmissionData mapData = jobSubmissionDataBuilder.build(context);
+ jobSubmissionSupport.addMonitoringCommands(mapData);
+ String scriptContent = mapData.loadFromFile(jobManagerConfig.getJobDescriptionTemplateName());
+ logger.info("Generated job submission script for AWS:\n{}", scriptContent);
+
+ Job jobModel = jobSubmissionSupport.createJob(context.getProcessId(), context.getTaskId(), mapData);
+ jobModel.setJobId("DEFAULT_JOB_ID");
+ jobModel.setJobDescription(scriptContent);
+ jobSubmissionSupport.publishJobStatus(
+ jobModel, JobState.SUBMITTED, "Job submitted to EC2 instance with PID: DEFAULT_JOB_ID");
+ jobService.saveJob(jobModel);
+
+ File localScriptFile = new File(
+ jobSubmissionSupport.getLocalDataDir(context.getProcessId()),
+ "aws-job-" + new SecureRandom().nextInt() + jobManagerConfig.getScriptExtension());
+ Files.writeString(
+ localScriptFile.toPath(),
+ scriptContent,
+ StandardCharsets.UTF_8,
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING);
+ jobSubmissionSupport.publishJobStatus(jobModel, JobState.QUEUED, "Job queued on EC2 instance");
+ jobService.saveJob(jobModel);
+
+ String remoteWorkingDir = context.getWorkingDir();
+ try {
+ adapter.createDirectory(remoteWorkingDir, true);
+ } catch (Exception e) {
+ String reason = "Failed to connect to SSH daemon or create remote directory " + remoteWorkingDir + ". "
+ + e.getMessage();
+ logger.error(reason, e);
+ return new DagTaskResult.Failure(reason, false, e);
+ }
+ jobSubmissionSupport.publishJobStatus(jobModel, JobState.ACTIVE, "SSH connected to EC2 instance");
+ jobService.saveJob(jobModel);
+
+ adapter.uploadFile(localScriptFile.getAbsolutePath(), remoteWorkingDir);
+ logger.info("Successfully uploaded script {} to {}", localScriptFile.getName(), remoteWorkingDir);
+
+ RawCommandInfo submitCommandInfo =
+ jobManagerConfig.getSubmitCommand(remoteWorkingDir, localScriptFile.getName());
+ String remoteScriptPath = remoteWorkingDir + File.separator + localScriptFile.getName();
+ String command = "chmod +x " + remoteScriptPath + " && nohup " + submitCommandInfo.getCommand() + " > "
+ + remoteWorkingDir + "/AiravataAgent.stdout 2> " + remoteWorkingDir
+ + "/AiravataAgent.stderr & echo $!";
+ logger.info("Executing command on EC2 instance: {} for the process: {}", command, context.getProcessId());
+
+ CommandOutput commandOutput = adapter.executeCommand(command, remoteWorkingDir);
+ String jobId = commandOutput.getStdOut().trim();
+
+ if (commandOutput.getExitCode() != 0) {
+ String reason = "Failed to execute job submission command. STDERR: " + commandOutput.getStdError();
+ return handleJobSubmissionFailure(mapData, reason, context);
+ }
+
+ if (jobId.isEmpty() || !jobId.matches("\\d+")) {
+ String reason = "Job submission command did not return a valid process ID (PID). Output: "
+ + commandOutput.getStdOut();
+ return handleJobSubmissionFailure(mapData, reason, context);
+ }
+
+ saveProviderState(context, AWS_JOB_ID, jobId);
+ jobModel.setJobId(jobId);
+ jobService.saveJob(jobModel);
+ jobSubmissionSupport.saveAndPublishJobStatus(jobModel);
+
+ logger.info("Successfully launched job on EC2 instance. Remote process ID (jobId): {}", jobId);
+ return new DagTaskResult.Success("Launched job " + jobId + " in instance " + instanceId);
+
+ } catch (Exception e) {
+ logger.error("Fatal error during AWS job submission for process {}", context.getProcessId(), e);
+ return new DagTaskResult.Failure("Task failed due to unexpected issue", false, e);
+ }
+ }
+
+ // =========================================================================
+ // Deprovision — terminate EC2 instance
+ // =========================================================================
+
+ @Override
+ public DagTaskResult deprovision(TaskContext context) {
+ logger.info("Deprovisioning AWS resources for process {}", context.getProcessId());
+
+ try {
+ clearProviderState(context, AWS_INSTANCE_ID, AWS_SECURITY_GROUP_ID, AWS_KEY_PAIR_NAME);
+ awsTaskUtil.terminateEC2Instance(context, context.getGatewayId());
+ return new DagTaskResult.Success("AWS resources deprovisioned for process " + context.getProcessId());
+ } catch (Exception e) {
+ logger.error("Error during AWS deprovisioning for process {}", context.getProcessId(), e);
+ return new DagTaskResult.Failure(
+ "AWS deprovisioning failed for process " + context.getProcessId(), false, e);
+ }
+ }
+
+ // =========================================================================
+ // Monitor — poll PID status on EC2 instance
+ // =========================================================================
+
+ @Override
+ public DagTaskResult monitor(TaskContext context) {
+ try {
+ java.util.List jobs = jobService.getJobs("processId", context.getProcessId());
+ if (jobs == null || jobs.isEmpty()) {
+ return new DagTaskResult.Success("No running jobs found for process " + context.getProcessId());
+ }
+
+ String publicIp = getProviderState(context, AWS_PUBLIC_IP);
+ String sshCredentialToken = getProviderState(context, AWS_SSH_CREDENTIAL_TOKEN);
+
+ if (publicIp == null || sshCredentialToken == null) {
+ return new DagTaskResult.Success(
+ "No AWS instance info for monitoring process " + context.getProcessId());
+ }
+
+ SSHJAgentAdapter adapter = initSSHJAgentAdapter(sshCredentialToken, publicIp, context);
+ JobManagerSpec config = jobSubmissionSupport.getJobManagerConfiguration(context.getComputeResource());
+
+ for (Job job : jobs) {
+ pollJobUntilSaturated(adapter, config, job);
+ }
+
+ return new DagTaskResult.Success("Job monitoring completed for process " + context.getProcessId());
+
+ } catch (Exception e) {
+ logger.error(
+ "Error during AWS job monitoring for process {} — continuing (non-critical)",
+ context.getProcessId(),
+ e);
+ return new DagTaskResult.Success("Job monitoring encountered errors but continuing (non-critical)");
+ }
+ }
+
+ // =========================================================================
+ // Cancel — terminate EC2 instance (killing the running job)
+ // =========================================================================
+
+ @Override
+ public DagTaskResult cancel(TaskContext context) {
+ return deprovision(context);
+ }
+
+ // -------------------------------------------------------------------------
+ // Provider state helpers — read/write AWS context from providerContext JSON
+ // -------------------------------------------------------------------------
+
+ private void saveProviderState(TaskContext context, String key, String value) {
+ try {
+ context.getDagState().put(key, value);
+ Map contextMap = loadProviderContext(context);
+ contextMap.put(key, value);
+ context.getProcessModel().setProviderContext(MAPPER.writeValueAsString(contextMap));
+ processService.updateProcess(context.getProcessModel(), context.getProcessId());
+ } catch (Exception e) {
+ logger.warn("Failed to persist provider state key '{}' for process {}", key, context.getProcessId(), e);
+ }
+ }
+
+ private String getProviderState(TaskContext context, String key) {
+ String value = context.getDagState().get(key);
+ if (value != null) return value;
+ try {
+ return loadProviderContext(context).get(key);
+ } catch (Exception e) {
+ logger.warn("Failed to load provider context for process {}", context.getProcessId(), e);
+ return null;
+ }
+ }
+
+ private Map loadProviderContext(TaskContext context) throws Exception {
+ String json = context.getProcessModel().getProviderContext();
+ if (json == null || json.isEmpty()) return new HashMap<>();
+ return MAPPER.readValue(json, new TypeReference<>() {});
+ }
+
+ private void clearProviderState(TaskContext context, String... keys) {
+ try {
+ Map contextMap = loadProviderContext(context);
+ for (String key : keys) {
+ contextMap.put(key, null);
+ context.getDagState().remove(key);
+ }
+ context.getProcessModel().setProviderContext(MAPPER.writeValueAsString(contextMap));
+ processService.updateProcess(context.getProcessModel(), context.getProcessId());
+ } catch (Exception e) {
+ logger.warn("Failed to clear provider state for process {}", context.getProcessId(), e);
+ }
+ }
+
+ /**
+ * Resolves an AWS configuration value for the given key using a two-tier lookup:
+ *
+ *
ResourceBinding metadata — operator-level config stored on the binding for this
+ * compute resource and gateway. Takes priority so that gateway admins can configure
+ * defaults without requiring per-experiment scheduling overrides.
+ *
Experiment resourceSchedule — per-experiment override supplied at launch time.
+ * Falls through to {@code defaultValue} when neither source carries the key.
+ *
+ *
+ * @param binding the binding for this compute resource/gateway pair (may be {@code null})
+ * @param context the current task context
+ * @param key the configuration key (e.g. {@code "awsRegion"})
+ * @param defaultValue fallback value when neither binding metadata nor schedule carries the key
+ * @return the resolved configuration value, or {@code defaultValue} if not found
+ */
+ private String resolveAwsConfig(
+ ResourceBindingEntity binding, TaskContext context, String key, String defaultValue) {
+ // 1. Try binding metadata
+ if (binding != null) {
+ String fromBinding = ResourceProfileAdapter.getMetadataString(binding.getMetadata(), key);
+ if (fromBinding != null && !fromBinding.isBlank()) {
+ return fromBinding;
+ }
+ }
+ // 2. Try per-experiment resourceSchedule
+ try {
+ var schedule = context.getProcessModel().getResourceSchedule();
+ if (schedule != null && schedule.get(key) != null) {
+ return schedule.get(key).toString();
+ }
+ } catch (Exception e) {
+ logger.warn(
+ "Could not read resource schedule key '{}' for process {}; using default '{}'",
+ key,
+ context.getProcessId(),
+ defaultValue,
+ e);
+ }
+ return defaultValue;
+ }
+
+ // -------------------------------------------------------------------------
+ // Monitor helpers
+ // -------------------------------------------------------------------------
+
+ private void pollJobUntilSaturated(AgentAdapter adapter, JobManagerSpec config, Job job) {
+ try {
+ var monitorCommand = config.getMonitorCommand(job.getJobId());
+ if (monitorCommand.isEmpty()) {
+ logger.info("No monitor command for job {} — skipping", job.getJobId());
+ return;
+ }
+
+ CommandOutput output = adapter.executeCommand(monitorCommand.get().getRawCommand(), null);
+ if (output.getExitCode() != 0) {
+ logger.warn(
+ "Monitor command failed for job {}: stdout={}, stderr={}",
+ job.getJobId(),
+ output.getStdOut(),
+ output.getStdError());
+ return;
+ }
+
+ var jobStatus = config.getParser().parseJobStatus(job.getJobId(), output.getStdOut());
+ if (jobStatus != null) {
+ logger.info("Job {} status: {}", job.getJobId(), jobStatus.getState());
+ }
+ } catch (Exception e) {
+ logger.warn("Error polling job {} — continuing", job.getJobId(), e);
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Provision helpers
+ // -------------------------------------------------------------------------
+
+ private String saveSSHCredential(String privateKey, String publicKey, String gatewayId) throws Exception {
+ SSHCredential credential = new SSHCredential();
+ credential.setGatewayId(gatewayId);
+ credential.setToken(UUID.randomUUID().toString());
+ credential.setPrivateKey(privateKey);
+ credential.setPublicKey(publicKey);
+ return credentialStoreService.addSSHCredential(credential);
+ }
+
+ private String createSecurityGroup(Ec2Client ec2, TaskContext context) throws Exception {
+ var vpcs = ec2.describeVpcs(req -> req.filters(f -> f.name("is-default").values("true")))
+ .vpcs();
+ if (vpcs.isEmpty()) {
+ throw new Exception("No default VPC found in the AWS account");
+ }
+ String vpcId = vpcs.get(0).vpcId();
+ CreateSecurityGroupResponse sgRes =
+ ec2.createSecurityGroup(req -> req.groupName("airavata-sg-" + context.getProcessId())
+ .description("Airavata temporary security group for " + context.getProcessId())
+ .vpcId(vpcId));
+
+ // TODO: Restrict to specific IP range instead of 0.0.0.0/0 in production
+ String allowedCidr = "0.0.0.0/0";
+ logger.warn("Security group allows SSH from any IP ({}). Restrict in production.", allowedCidr);
+ ec2.authorizeSecurityGroupIngress(req -> req.groupId(sgRes.groupId())
+ .ipPermissions(p -> p.ipProtocol("tcp").fromPort(22).toPort(22).ipRanges(r -> r.cidrIp(allowedCidr))));
+
+ return sgRes.groupId();
+ }
+
+ // -------------------------------------------------------------------------
+ // Submit helpers
+ // -------------------------------------------------------------------------
+
+ private String verifyInstanceIsRunning(String token, String instanceId, String region, TaskContext context)
+ throws Exception {
+ try (Ec2Client ec2Client = awsTaskUtil.buildEc2Client(token, context.getGatewayId(), region)) {
+ DescribeInstancesRequest request =
+ DescribeInstancesRequest.builder().instanceIds(instanceId).build();
+ DescribeInstancesResponse response = ec2Client.describeInstances(request);
+
+ if (response.reservations().isEmpty()
+ || response.reservations().get(0).instances().isEmpty()) {
+ throw new Exception(
+ "No instance found with ID: " + instanceId + " for process: " + context.getProcessId());
+ }
+
+ Instance instance = response.reservations().get(0).instances().get(0);
+ InstanceStateName state = instance.state().name();
+ logger.info("Instance {} state: {} for process {}", instanceId, state, context.getProcessId());
+
+ if (state == InstanceStateName.RUNNING) {
+ String publicIp = instance.publicIpAddress();
+ if (publicIp == null || publicIp.isEmpty()) {
+ throw new Exception("Instance " + instanceId + " is running but has no public IP yet");
+ }
+ return publicIp;
+ }
+
+ if (state == InstanceStateName.SHUTTING_DOWN
+ || state == InstanceStateName.TERMINATED
+ || state == InstanceStateName.STOPPED) {
+ throw new Exception(
+ "Instance entered failure state: " + state + " for process: " + context.getProcessId());
+ }
+
+ // Still pending — throw so Temporal retries
+ throw new Exception("Instance " + instanceId + " not yet running (state: " + state + ")");
+ }
+ }
+
+ private DagTaskResult handleJobSubmissionFailure(JobSubmissionData mapData, String reason, TaskContext context)
+ throws Exception {
+ logger.error(reason);
+ Job jobModel = jobSubmissionSupport.createJob(context.getProcessId(), context.getTaskId(), mapData);
+ jobSubmissionSupport.publishJobStatus(jobModel, JobState.FAILED, reason);
+ jobService.saveJob(jobModel);
+ return new DagTaskResult.Failure(reason, false);
+ }
+
+ private SSHJAgentAdapter initSSHJAgentAdapter(
+ String sshCredentialToken, String publicIpAddress, TaskContext context) throws Exception {
+ SSHJAgentAdapter adapter = new SSHJAgentAdapter(computeResourceAdapter, credentialStoreService);
+ SSHCredential sshCredential =
+ credentialStoreService.getSSHCredential(sshCredentialToken, context.getGatewayId());
+ adapter.init(
+ context.getComputeResourceLoginUserName(),
+ publicIpAddress,
+ 22,
+ sshCredential.getPublicKey(),
+ sshCredential.getPrivateKey(),
+ sshCredential.getPassphrase());
+
+ return adapter;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/aws/AwsJobSpec.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/aws/AwsJobSpec.java
new file mode 100644
index 00000000000..fd31a3c64f6
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/aws/AwsJobSpec.java
@@ -0,0 +1,78 @@
+/**
+*
+* 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.compute.provider.aws;
+
+import java.io.File;
+import java.util.Optional;
+import org.apache.airavata.compute.resource.submission.CustomCommandOutputParser;
+import org.apache.airavata.compute.resource.submission.JobManagerSpec;
+import org.apache.airavata.compute.resource.submission.JobOutputParser;
+import org.apache.airavata.compute.resource.submission.RawCommandInfo;
+
+/**
+ * Job manager for executing jobs directly on a cloud VM via SSH.
+ * Monitors processes via PID-based ps commands rather than a batch scheduler.
+ */
+public class AwsJobSpec implements JobManagerSpec {
+
+ private final String jobDescriptionTemplateName;
+
+ public AwsJobSpec(String jobDescriptionTemplateName) {
+ this.jobDescriptionTemplateName = jobDescriptionTemplateName;
+ }
+
+ @Override
+ public RawCommandInfo getCancelCommand(String jobID) {
+ return new RawCommandInfo("kill -9 " + jobID);
+ }
+
+ @Override
+ public String getJobDescriptionTemplateName() {
+ return jobDescriptionTemplateName;
+ }
+
+ @Override
+ public Optional getMonitorCommand(String jobID) {
+ return Optional.of(new RawCommandInfo("ps -p " + jobID + " -o stat="));
+ }
+
+ @Override
+ public Optional getJobIdMonitorCommand(String jobName, String userName) {
+ return Optional.empty();
+ }
+
+ @Override
+ public String getScriptExtension() {
+ return ".sh";
+ }
+
+ @Override
+ public RawCommandInfo getSubmitCommand(String workingDirectory, String scriptFilePath) {
+ String remoteScriptPath = workingDirectory
+ + File.separator
+ + java.nio.file.Path.of(scriptFilePath).getFileName().toString();
+ return new RawCommandInfo("/bin/bash " + remoteScriptPath);
+ }
+
+ @Override
+ public JobOutputParser getParser() {
+ return new CustomCommandOutputParser();
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/aws/AwsTaskUtil.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/aws/AwsTaskUtil.java
new file mode 100644
index 00000000000..2790723a4f5
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/aws/AwsTaskUtil.java
@@ -0,0 +1,150 @@
+/**
+*
+* 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.compute.provider.aws;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Map;
+import org.apache.airavata.credential.model.PasswordCredential;
+import org.apache.airavata.execution.dag.TaskContext;
+import org.apache.airavata.iam.service.CredentialStoreService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.ec2.Ec2Client;
+import software.amazon.awssdk.services.ec2.model.TerminateInstancesRequest;
+
+/**
+ * Utility bean for AWS-specific task operations.
+ * Provides helpers for building EC2 clients and cleaning up EC2 resources.
+ * AWS credentials are stored as {@link PasswordCredential} where
+ * {@code loginUserName} is the AWS access key ID and {@code password} is the AWS secret access key.
+ */
+@Component
+public class AwsTaskUtil {
+
+ private static final Logger logger = LoggerFactory.getLogger(AwsTaskUtil.class);
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private final CredentialStoreService credentialStoreService;
+
+ public AwsTaskUtil(CredentialStoreService credentialStoreService) {
+ this.credentialStoreService = credentialStoreService;
+ }
+
+ /**
+ * Build an authenticated EC2 client using AWS credentials stored as a {@link PasswordCredential}.
+ *
+ * @param credentialToken the credential store token pointing to the AWS PasswordCredential
+ * @param gatewayId the gateway identifier
+ * @param region the AWS region string (e.g. "us-east-1")
+ * @return a configured and open {@link Ec2Client}; caller is responsible for closing it
+ * @throws Exception if the credential cannot be retrieved or the client cannot be built
+ */
+ public Ec2Client buildEc2Client(String credentialToken, String gatewayId, String region) throws Exception {
+ logger.debug("Building EC2 client for region {} using credential token {}", region, credentialToken);
+ try {
+ PasswordCredential cred = credentialStoreService.getPasswordCredential(credentialToken, gatewayId);
+ if (cred == null) {
+ throw new Exception(
+ "No AWS credential found for token " + credentialToken + " in gateway " + gatewayId);
+ }
+ var awsBasicCredentials = AwsBasicCredentials.create(cred.getLoginUserName(), cred.getPassword());
+ return Ec2Client.builder()
+ .region(Region.of(region))
+ .credentialsProvider(StaticCredentialsProvider.create(awsBasicCredentials))
+ .build();
+ } catch (Exception e) {
+ logger.error(
+ "Failed to build EC2 client for region {} with credential token {}", region, credentialToken, e);
+ throw new Exception("Failed to build EC2 client for region " + region + ": " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Terminate the EC2 instance associated with the given task context.
+ * Reads instance ID and AWS credential token from the process context JSON.
+ *
+ * @param taskContext the task context providing process and gateway information
+ * @param gatewayId the gateway identifier
+ */
+ public void terminateEC2Instance(TaskContext taskContext, String gatewayId) {
+ logger.info("Terminating EC2 instance for process {}", taskContext.getProcessId());
+ try {
+ String instanceId = readProviderContextKey(taskContext, "AWS_INSTANCE_ID");
+ if (instanceId == null || instanceId.isBlank()) {
+ logger.warn(
+ "No instance ID found in process context for process {}; skipping termination",
+ taskContext.getProcessId());
+ return;
+ }
+
+ // Determine region from resource schedule if set
+ String region = "us-east-1";
+ try {
+ var schedule = taskContext.getProcessModel().getResourceSchedule();
+ if (schedule != null && schedule.get("awsRegion") != null) {
+ region = schedule.get("awsRegion").toString();
+ }
+ } catch (Exception regionEx) {
+ logger.warn("Could not determine AWS region for termination; defaulting to us-east-1", regionEx);
+ }
+
+ // Use the AWS credential token from the context (stored as the SSH token placeholder)
+ String credentialToken = readProviderContextKey(taskContext, "AWS_SSH_CREDENTIAL_TOKEN");
+ if (credentialToken == null) {
+ logger.warn(
+ "No credential token found for process {}; cannot terminate instance {}",
+ taskContext.getProcessId(),
+ instanceId);
+ return;
+ }
+
+ try (Ec2Client ec2 = buildEc2Client(credentialToken, gatewayId, region)) {
+ TerminateInstancesRequest request = TerminateInstancesRequest.builder()
+ .instanceIds(instanceId)
+ .build();
+ ec2.terminateInstances(request);
+ logger.info(
+ "Successfully sent termination request for EC2 instance {} in process {}",
+ instanceId,
+ taskContext.getProcessId());
+ }
+ } catch (Exception e) {
+ logger.error("Failed to terminate EC2 instance for process {}", taskContext.getProcessId(), e);
+ }
+ }
+
+ private String readProviderContextKey(TaskContext taskContext, String key) {
+ try {
+ String json = taskContext.getProcessModel().getProviderContext();
+ if (json == null || json.isEmpty()) return null;
+ Map contextMap = MAPPER.readValue(json, new TypeReference<>() {});
+ return contextMap.get(key);
+ } catch (Exception e) {
+ logger.warn("Failed to read provider context key '{}' for process {}", key, taskContext.getProcessId(), e);
+ return null;
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/local/LocalComputeProvider.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/local/LocalComputeProvider.java
new file mode 100644
index 00000000000..f5b3df8b7c8
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/local/LocalComputeProvider.java
@@ -0,0 +1,76 @@
+/**
+*
+* 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.compute.provider.local;
+
+import org.apache.airavata.compute.provider.ComputeProvider;
+import org.apache.airavata.compute.provider.slurm.SlurmComputeProvider;
+import org.apache.airavata.config.ServiceConditionals.ConditionalOnParticipant;
+import org.apache.airavata.core.model.DagTaskResult;
+import org.apache.airavata.execution.dag.TaskContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * Compute provider for local (PLAIN) resources.
+ *
+ *
Local compute shares SLURM's provisioning and deprovisioning logic (working
+ * directory setup/cleanup) but does not require job submission — the machine is
+ * already available for execution.
+ */
+@Component
+@ConditionalOnParticipant
+public class LocalComputeProvider implements ComputeProvider {
+
+ private static final Logger logger = LoggerFactory.getLogger(LocalComputeProvider.class);
+
+ private final SlurmComputeProvider slurmProvider;
+
+ public LocalComputeProvider(SlurmComputeProvider slurmProvider) {
+ this.slurmProvider = slurmProvider;
+ }
+
+ @Override
+ public DagTaskResult provision(TaskContext context) {
+ return slurmProvider.provision(context);
+ }
+
+ @Override
+ public DagTaskResult submit(TaskContext context) {
+ logger.info("Local provider: no submission needed for process {}", context.getProcessId());
+ return new DagTaskResult.Success("No submission needed for local compute");
+ }
+
+ @Override
+ public DagTaskResult monitor(TaskContext context) {
+ logger.info("Local provider: no monitoring needed for process {}", context.getProcessId());
+ return new DagTaskResult.Success("No monitoring needed for local compute");
+ }
+
+ @Override
+ public DagTaskResult cancel(TaskContext context) {
+ return slurmProvider.cancel(context);
+ }
+
+ @Override
+ public DagTaskResult deprovision(TaskContext context) {
+ return slurmProvider.deprovision(context);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/local/LocalJobSpec.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/local/LocalJobSpec.java
new file mode 100644
index 00000000000..e42efff77ee
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/local/LocalJobSpec.java
@@ -0,0 +1,92 @@
+/**
+*
+* 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.compute.provider.local;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.airavata.compute.resource.model.JobManagerCommand;
+import org.apache.airavata.compute.resource.submission.JobManagerSpec;
+import org.apache.airavata.compute.resource.submission.JobOutputParser;
+import org.apache.airavata.compute.resource.submission.RawCommandInfo;
+
+public class LocalJobSpec implements JobManagerSpec {
+ private final Map jobManagerCommands;
+ private final String jobDescriptionTemplateName;
+ private final String scriptExtension;
+ private final String installedPath;
+ private final JobOutputParser parser;
+
+ public LocalJobSpec(
+ String jobDescriptionTemplateName,
+ String scriptExtension,
+ String installedPath,
+ Map jobManagerCommands,
+ JobOutputParser parser) {
+ this.jobDescriptionTemplateName = jobDescriptionTemplateName;
+ this.scriptExtension = scriptExtension;
+ this.parser = parser;
+ this.jobManagerCommands = jobManagerCommands;
+ installedPath = installedPath.trim();
+ if (installedPath.isEmpty() || installedPath.endsWith("/")) {
+ this.installedPath = installedPath;
+ } else {
+ this.installedPath = installedPath + "/";
+ }
+ }
+
+ @Override
+ public RawCommandInfo getCancelCommand(String jobID) {
+ return new RawCommandInfo(this.installedPath
+ + jobManagerCommands.get(JobManagerCommand.DELETION).trim() + " " + jobID);
+ }
+
+ @Override
+ public String getJobDescriptionTemplateName() {
+ return jobDescriptionTemplateName;
+ }
+
+ @Override
+ public Optional getMonitorCommand(String jobID) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional getJobIdMonitorCommand(String jobName, String userName) {
+ return Optional.empty();
+ }
+
+ @Override
+ public String getScriptExtension() {
+ return scriptExtension;
+ }
+
+ @Override
+ public RawCommandInfo getSubmitCommand(String workingDirectory, String scriptFilePath) {
+ return new RawCommandInfo(this.installedPath
+ + jobManagerCommands.get(JobManagerCommand.SUBMISSION).trim() + " " + workingDirectory + File.separator
+ + java.nio.file.Path.of(scriptFilePath).getFileName().toString());
+ }
+
+ @Override
+ public JobOutputParser getParser() {
+ return parser;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/local/LocalOutputParser.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/local/LocalOutputParser.java
new file mode 100644
index 00000000000..8c0b06953ee
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/local/LocalOutputParser.java
@@ -0,0 +1,57 @@
+/**
+*
+* 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.compute.provider.local;
+
+import java.util.Map;
+import org.apache.airavata.compute.resource.model.JobState;
+import org.apache.airavata.compute.resource.submission.JobOutputParser;
+import org.apache.airavata.core.model.StatusModel;
+import org.apache.airavata.core.util.IdGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LocalOutputParser implements JobOutputParser {
+ private static final Logger logger = LoggerFactory.getLogger(LocalOutputParser.class);
+
+ @Override
+ public String parseJobSubmission(String rawOutput) throws Exception {
+ return IdGenerator.getId("JOB_ID_");
+ }
+
+ @Override
+ public boolean isJobSubmissionFailed(String rawOutput) {
+ return false;
+ }
+
+ @Override
+ public StatusModel parseJobStatus(String jobID, String rawOutput) throws Exception {
+ return null;
+ }
+
+ @Override
+ public void parseJobStatuses(String userName, Map> statusMap, String rawOutput)
+ throws Exception {}
+
+ @Override
+ public String parseJobId(String jobName, String rawOutput) throws Exception {
+ // For fork jobs there is no job ID, hence airavata generates a job ID
+ return IdGenerator.getId(jobName);
+ }
+}
diff --git a/airavata-api/src/main/java/org/apache/airavata/monitor/email/parser/ResourceConfig.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/ResourceConfig.java
similarity index 92%
rename from airavata-api/src/main/java/org/apache/airavata/monitor/email/parser/ResourceConfig.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/ResourceConfig.java
index f58ba34deb4..c5c9a782d4f 100644
--- a/airavata-api/src/main/java/org/apache/airavata/monitor/email/parser/ResourceConfig.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/ResourceConfig.java
@@ -17,10 +17,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.monitor.email.parser;
+package org.apache.airavata.compute.provider.slurm;
import java.util.List;
-import org.apache.airavata.model.appcatalog.computeresource.ResourceJobManagerType;
+import org.apache.airavata.compute.resource.model.ResourceJobManagerType;
public class ResourceConfig {
private ResourceJobManagerType jobManagerType;
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SLURMEmailParser.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SLURMEmailParser.java
new file mode 100644
index 00000000000..102ea09b246
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SLURMEmailParser.java
@@ -0,0 +1,97 @@
+/**
+*
+* 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.compute.provider.slurm;
+
+import jakarta.mail.Message;
+import jakarta.mail.MessagingException;
+import java.util.regex.Pattern;
+import org.apache.airavata.compute.resource.model.JobState;
+import org.apache.airavata.compute.resource.service.JobService;
+import org.apache.airavata.core.exception.CoreExceptions.AiravataException;
+import org.apache.airavata.execution.monitoring.JobStatusResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+@Component
+@Profile("!test")
+@ConditionalOnProperty(prefix = "airavata.services.monitor.email", name = "enabled", havingValue = "true")
+public class SLURMEmailParser {
+
+ private static final Logger logger = LoggerFactory.getLogger(SLURMEmailParser.class);
+
+ static final String STATUS = "status";
+ static final String JOBID = "jobId";
+ static final String JOBNAME = "jobName";
+ static final String EXIT_STATUS = "exitStatus";
+
+ private static final String REGEX = "[A-Z]*\\s[a-zA-Z]*_[a-z]*=(?<" + JOBID + ">\\d*)[ ]*[a-zA-Z]*=(?<" + JOBNAME
+ + ">[a-zA-Z0-9-]*)[ ]*(?<" + STATUS + ">[]a-zA-Z ]*),.*";
+
+ public static final String BEGAN = "Began";
+ public static final String STAGE_OUT = "Staged Out";
+ public static final String ENDED = "Ended";
+ public static final String FAILED = "Failed";
+ private static final Pattern cancelledStatePattern = Pattern.compile("CANCELLED");
+ private static final Pattern pattern = Pattern.compile(REGEX);
+
+ public JobStatusResult parseEmail(Message message, JobService jobService)
+ throws MessagingException, AiravataException {
+ return parseSubject(message.getSubject());
+ }
+
+ private JobStatusResult parseSubject(String subject) throws MessagingException {
+ var matcher = pattern.matcher(subject);
+ if (matcher.find()) {
+ String jobId = matcher.group(JOBID);
+ String jobName = matcher.group(JOBNAME);
+ JobState state = getJobState(matcher.group(STATUS), subject);
+ return new JobStatusResult(state, jobId, jobName);
+ } else {
+ logger.error("[EJM]: No matched found for subject -> {}", subject);
+ return null;
+ }
+ }
+
+ private JobState getJobState(String state, String subject) {
+ switch (state.trim()) {
+ case BEGAN:
+ case STAGE_OUT:
+ return JobState.ACTIVE;
+ case ENDED:
+ var matcher = cancelledStatePattern.matcher(subject);
+ if (matcher.find()) {
+ return JobState.CANCELED;
+ }
+ return JobState.COMPLETED;
+ case FAILED:
+ if (subject.contains("NODE_FAIL")) {
+ return JobState.NON_CRITICAL_FAIL;
+ } else {
+ return JobState.FAILED;
+ }
+ default:
+ logger.error("[EJM]: Job State {} isn't handle by SLURM parser", state);
+ return JobState.UNKNOWN;
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SlurmComputeProvider.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SlurmComputeProvider.java
new file mode 100644
index 00000000000..68cdc8a00d8
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SlurmComputeProvider.java
@@ -0,0 +1,638 @@
+/**
+*
+* 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.compute.provider.slurm;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.List;
+import org.apache.airavata.compute.provider.ComputeProvider;
+import org.apache.airavata.compute.resource.model.Job;
+import org.apache.airavata.compute.resource.model.JobState;
+import org.apache.airavata.compute.resource.repository.JobRepository;
+import org.apache.airavata.compute.resource.service.JobService;
+import org.apache.airavata.compute.resource.submission.JobFactory;
+import org.apache.airavata.compute.resource.submission.JobManagerSpec;
+import org.apache.airavata.compute.resource.submission.JobSubmissionDataBuilder;
+import org.apache.airavata.compute.resource.submission.JobSubmissionSupport;
+import org.apache.airavata.compute.resource.submission.RawCommandInfo;
+import org.apache.airavata.config.ServerProperties;
+import org.apache.airavata.config.ServiceConditionals.ConditionalOnParticipant;
+import org.apache.airavata.core.model.DagTaskResult;
+import org.apache.airavata.core.model.ProcessState;
+import org.apache.airavata.core.model.StatusModel;
+import org.apache.airavata.core.telemetry.CounterMetric;
+import org.apache.airavata.execution.dag.TaskContext;
+import org.apache.airavata.execution.orchestration.ComputeSubmissionTracker;
+import org.apache.airavata.protocol.AdapterSupport;
+import org.apache.airavata.protocol.AgentAdapter;
+import org.apache.airavata.protocol.AgentAdapter.CommandOutput;
+import org.apache.airavata.status.model.ErrorModel;
+import org.apache.airavata.status.service.StatusService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * SLURM/PLAIN compute provider — covers the full lifecycle of SLURM-scheduled
+ * job execution.
+ *
+ *
Resource lifecycle:
+ *
+ *
{@link #provision} — creates working directory on remote resource via SSH
+ *
{@link #monitor} — polls job status via squeue until terminal state
+ *
{@link #cancel} — cancels remote jobs + force-publishes CANCELED status
+ *
+ */
+@Component
+@ConditionalOnParticipant
+public class SlurmComputeProvider implements ComputeProvider {
+
+ private static final Logger logger = LoggerFactory.getLogger(SlurmComputeProvider.class);
+ private static final CounterMetric defaultJSTaskCounter = new CounterMetric("default_js_task_counter");
+ private static final String DEFAULT_JOB_ID = "DEFAULT_JOB_ID";
+
+ private final AdapterSupport adapterSupport;
+ private final JobSubmissionSupport jobSubmissionSupport;
+ private final JobSubmissionDataBuilder jobSubmissionDataBuilder;
+ private final ComputeSubmissionTracker computeSubmissionTracker;
+ private final JobService jobService;
+ private final StatusService statusService;
+ private final ServerProperties serverProperties;
+ private final JobFactory jobFactory;
+ private final JobRepository jobRepository;
+
+ public SlurmComputeProvider(
+ AdapterSupport adapterSupport,
+ JobSubmissionSupport jobSubmissionSupport,
+ JobSubmissionDataBuilder jobSubmissionDataBuilder,
+ ComputeSubmissionTracker computeSubmissionTracker,
+ JobService jobService,
+ StatusService statusService,
+ ServerProperties serverProperties,
+ JobFactory jobFactory,
+ JobRepository jobRepository) {
+ this.adapterSupport = adapterSupport;
+ this.jobSubmissionSupport = jobSubmissionSupport;
+ this.jobSubmissionDataBuilder = jobSubmissionDataBuilder;
+ this.computeSubmissionTracker = computeSubmissionTracker;
+ this.jobService = jobService;
+ this.statusService = statusService;
+ this.serverProperties = serverProperties;
+ this.jobFactory = jobFactory;
+ this.jobRepository = jobRepository;
+ }
+
+ // =========================================================================
+ // Provision — create working directory on remote resource
+ // =========================================================================
+
+ @Override
+ public DagTaskResult provision(TaskContext context) {
+ try {
+ AgentAdapter adapter = adapterSupport.fetchAdapter(
+ context.getGatewayId(),
+ context.getComputeResourceId(),
+ context.getJobSubmissionProtocol(),
+ context.getComputeResourceCredentialToken(),
+ context.getComputeResourceLoginUserName());
+
+ String workingDir = context.getWorkingDir();
+ logger.info(
+ "Creating directory {} on compute resource {} by user {} using token {}",
+ workingDir,
+ context.getComputeResourceId(),
+ context.getComputeResourceLoginUserName(),
+ context.getComputeResourceCredentialToken());
+
+ adapter.createDirectory(workingDir, true);
+ return new DagTaskResult.Success("Provisioning completed for " + context.getTaskId());
+
+ } catch (Exception e) {
+ return new DagTaskResult.Failure(
+ "Failed to provision compute resource for task " + context.getTaskId(), false, e);
+ }
+ }
+
+ // =========================================================================
+ // Submit — submit batch job to SLURM scheduler
+ // =========================================================================
+
+ @Override
+ public DagTaskResult submit(TaskContext context) {
+
+ defaultJSTaskCounter.inc();
+ String jobId = null;
+ AgentAdapter adapter;
+ String computeId = null;
+
+ try {
+ computeId = context.getComputeResourceId();
+ adapter = adapterSupport.fetchAdapter(
+ context.getGatewayId(),
+ computeId,
+ context.getJobSubmissionProtocol(),
+ context.getComputeResourceCredentialToken(),
+ context.getComputeResourceLoginUserName());
+ } catch (Exception e) {
+ return new DagTaskResult.Failure("Failed to fetch adapter to connect to " + computeId, true, e);
+ }
+
+ try {
+ var jobsOfTask = jobService.getJobs("processId", context.getProcessId());
+
+ if (!jobsOfTask.isEmpty()) {
+ logger.warn("A job is already available for process {}", context.getProcessId());
+ return new DagTaskResult.Success("A job is already available for process " + context.getProcessId());
+ }
+
+ var mapData = jobSubmissionDataBuilder.build(context);
+
+ var jobModel = jobSubmissionSupport.createJob(context.getProcessId(), context.getTaskId(), mapData);
+
+ var submissionOutput = jobSubmissionSupport.submitBatchJob(
+ adapter,
+ mapData,
+ mapData.getWorkingDirectory(),
+ context.getComputeResource(),
+ context.getProcessId(),
+ computeSubmissionTracker,
+ computeId);
+
+ jobModel.setJobDescription(submissionOutput.getDescription());
+ jobModel.setExitCode(submissionOutput.getExitCode());
+ jobModel.setStdErr(submissionOutput.getStdErr());
+ jobModel.setStdOut(submissionOutput.getStdOut());
+
+ jobId = submissionOutput.getJobId();
+
+ if (submissionOutput.getExitCode() != 0 || submissionOutput.isJobSubmissionFailed()) {
+
+ jobModel.setJobId(DEFAULT_JOB_ID);
+ if (submissionOutput.isJobSubmissionFailed()) {
+ jobSubmissionSupport.publishJobStatus(
+ jobModel, JobState.FAILED, submissionOutput.getFailureReason());
+ jobService.saveJob(jobModel);
+ logger.error(
+ "Job submission failed for job name {}. Exit code : {}, Submission failed : {}",
+ jobModel.getJobName(),
+ submissionOutput.getExitCode(),
+ submissionOutput.isJobSubmissionFailed());
+
+ logger.error("Standard error message : {}", submissionOutput.getStdErr());
+ logger.error("Standard out message : {}", submissionOutput.getStdOut());
+ return new DagTaskResult.Failure(
+ "Job submission command didn't return a jobId. Reason "
+ + submissionOutput.getFailureReason(),
+ false);
+
+ } else {
+ String msg;
+ jobService.saveJob(jobModel);
+ var errorModel = new ErrorModel();
+ if (submissionOutput.getExitCode() != Integer.MIN_VALUE) {
+ msg = "Returned non zero exit code:" + submissionOutput.getExitCode() + " for JobName:"
+ + jobModel.getJobName() + ", with failure reason : "
+ + submissionOutput.getFailureReason()
+ + " Hence changing job state to Failed.";
+ errorModel.setActualErrorMessage(submissionOutput.getFailureReason());
+ } else {
+ msg = "Didn't return valid job submission exit code for JobName:" + jobModel.getJobName()
+ + ", with failure reason : stdout ->"
+ + submissionOutput.getStdOut() + " stderr -> "
+ + submissionOutput.getStdErr() + " Hence changing job state to Failed.";
+ errorModel.setActualErrorMessage(msg);
+ }
+ logger.error(msg);
+ return new DagTaskResult.Failure(msg, false);
+ }
+
+ } else if (jobId != null && !jobId.isEmpty()) {
+
+ logger.info("Received job id {} from compute resource", jobId);
+ jobModel.setJobId(jobId);
+ jobService.saveJob(jobModel);
+
+ jobSubmissionSupport.publishJobStatus(
+ jobModel,
+ JobState.SUBMITTED,
+ "Successfully Submitted to "
+ + context.getComputeResource().getHostName());
+
+ if (verifyJobSubmissionByJobId(adapter, jobId, context)) {
+ jobSubmissionSupport.publishJobStatus(jobModel, JobState.QUEUED, "Verification step succeeded");
+ }
+
+ } else {
+
+ String verifyJobId = verifyJobSubmission(
+ adapter, jobModel.getJobName(), context.getComputeResourceLoginUserName(), context);
+ if (verifyJobId != null && !verifyJobId.isEmpty()) {
+ jobId = verifyJobId;
+ jobModel.setJobId(jobId);
+ jobService.saveJob(jobModel);
+ jobSubmissionSupport.publishJobStatus(jobModel, JobState.QUEUED, "Verification step succeeded");
+ logger.info("Job id {} verification succeeded", verifyJobId);
+ }
+ }
+
+ if (jobId == null || jobId.isEmpty()) {
+ jobModel.setJobId(DEFAULT_JOB_ID);
+ jobService.saveJob(jobModel);
+ String msg = "expId:" + context.getExperimentId() + " Couldn't find " + "remote jobId for JobName:"
+ + jobModel.getJobName() + ", both submit and verify steps " + "doesn't return a valid JobId. "
+ + "Hence changing experiment state to Failed";
+ logger.error(msg);
+ return new DagTaskResult.Failure(
+ "Couldn't find job id in both submitted and verified steps. " + msg, false);
+
+ } else {
+ mapData.setJobId(jobId);
+ return new DagTaskResult.Success("Submitted job to compute resource");
+ }
+
+ } catch (Exception e) {
+
+ logger.error("Task failed due to unexpected issue. Trying to control damage", e);
+
+ if (jobId != null && !jobId.isEmpty()) {
+ logger.warn("Job {} has already being submitted. Trying to cancel the job", jobId);
+ try {
+ boolean cancelled = jobSubmissionSupport.cancelJob(adapter, jobId, context.getComputeResource());
+ if (cancelled) {
+ logger.info("Job {} cancellation triggered", jobId);
+ } else {
+ logger.error("Failed to cancel job {}. Please contact system admins", jobId);
+ }
+ } catch (Exception e1) {
+ logger.error("Error while cancelling the job {}. Please contact system admins", jobId);
+ }
+ }
+
+ return new DagTaskResult.Failure("Task failed due to unexpected issue", false, e);
+ }
+ }
+
+ // =========================================================================
+ // Deprovision — state correction + directory cleanup
+ // =========================================================================
+
+ @Override
+ public DagTaskResult deprovision(TaskContext context) {
+ logger.info("Deprovisioning for process {}", context.getProcessId());
+
+ ensureExecutingStateBeforeCompleted(context);
+ cleanupLocalData(context);
+ cleanupWorkingDirectory(context);
+
+ return new DagTaskResult.Success("Process " + context.getProcessId() + " successfully completed");
+ }
+
+ // =========================================================================
+ // Monitor — poll job status until terminal state
+ // =========================================================================
+
+ @Override
+ public DagTaskResult monitor(TaskContext context) {
+ try {
+ List jobs = jobService.getJobs("processId", context.getProcessId());
+ if (jobs == null || jobs.isEmpty()) {
+ return new DagTaskResult.Success("No running jobs found for process " + context.getProcessId());
+ }
+
+ logger.info("Found {} jobs for process {} — polling until saturated", jobs.size(), context.getProcessId());
+
+ JobManagerSpec jobManagerConfig = jobFactory.getJobManagerConfiguration(context.getComputeResource());
+ AgentAdapter adapter = adapterSupport.fetchAdapter(
+ context.getGatewayId(),
+ context.getComputeResourceId(),
+ context.getJobSubmissionProtocol(),
+ context.getComputeResourceCredentialToken(),
+ context.getComputeResourceLoginUserName());
+
+ for (Job job : jobs) {
+ pollJobUntilSaturated(adapter, jobManagerConfig, job);
+ }
+
+ return new DagTaskResult.Success("Job monitoring completed for process " + context.getProcessId());
+
+ } catch (Exception e) {
+ logger.error(
+ "Error during job monitoring for process {} — continuing (non-critical)",
+ context.getProcessId(),
+ e);
+ return new DagTaskResult.Success("Job monitoring encountered errors but continuing (non-critical)");
+ }
+ }
+
+ // =========================================================================
+ // Cancel — cancel remote jobs
+ // =========================================================================
+
+ @Override
+ public DagTaskResult cancel(TaskContext context) {
+ logger.info("Cancelling jobs for process {}", context.getProcessId());
+
+ try {
+ cancelRemoteJobs(context);
+ } catch (Exception e) {
+ logger.error("Error during job cancellation for process {}", context.getProcessId(), e);
+ return new DagTaskResult.Failure(
+ "Error during job cancellation for process " + context.getProcessId(), true, e);
+ }
+
+ return new DagTaskResult.Success("Process " + context.getProcessId() + " successfully cancelled");
+ }
+
+ // -------------------------------------------------------------------------
+ // Monitor helpers
+ // -------------------------------------------------------------------------
+
+ private void pollJobUntilSaturated(AgentAdapter adapter, JobManagerSpec config, Job job) {
+ try {
+ var monitorCommand = config.getMonitorCommand(job.getJobId());
+ if (monitorCommand.isEmpty()) {
+ logger.info("No monitor command for job {} — skipping", job.getJobId());
+ return;
+ }
+
+ CommandOutput output = adapter.executeCommand(monitorCommand.get().getRawCommand(), null);
+ if (output.getExitCode() != 0) {
+ logger.warn(
+ "Monitor command failed for job {}: stdout={}, stderr={}",
+ job.getJobId(),
+ output.getStdOut(),
+ output.getStdError());
+ return;
+ }
+
+ StatusModel jobStatus = config.getParser().parseJobStatus(job.getJobId(), output.getStdOut());
+ if (jobStatus != null) {
+ logger.info("Job {} status: {}", job.getJobId(), jobStatus.getState());
+ }
+ } catch (Exception e) {
+ logger.warn("Error polling job {} — continuing", job.getJobId(), e);
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Submit helpers
+ // -------------------------------------------------------------------------
+
+ private boolean verifyJobSubmissionByJobId(AgentAdapter agentAdapter, String jobID, TaskContext context) {
+ StatusModel status = null;
+ try {
+ status = jobSubmissionSupport.getJobStatus(agentAdapter, jobID, context.getComputeResource());
+ } catch (Exception e) {
+ logger.warn("Error while fetching the job status for id {}", jobID);
+ }
+ return status != null && status.getState() != JobState.UNKNOWN;
+ }
+
+ private String verifyJobSubmission(
+ AgentAdapter agentAdapter, String jobName, String userName, TaskContext context) {
+ String jobId = null;
+ try {
+ jobId = jobSubmissionSupport.getJobIdByJobName(
+ agentAdapter, jobName, userName, context.getComputeResource());
+ } catch (Exception e) {
+ logger.warn("Error while verifying JobId from JobName {}", jobName);
+ }
+ return jobId;
+ }
+
+ // -------------------------------------------------------------------------
+ // Deprovision helpers — state machine correction
+ // -------------------------------------------------------------------------
+
+ private void ensureExecutingStateBeforeCompleted(TaskContext context) {
+ try {
+ var currentStatus = statusService.getLatestProcessStatus(context.getProcessId());
+ if (currentStatus != null && currentStatus.getState() != null) {
+ var currentState = currentStatus.getState();
+ if (currentState != ProcessState.EXECUTING
+ && currentState != ProcessState.MONITORING
+ && currentState != ProcessState.OUTPUT_DATA_STAGING
+ && currentState != ProcessState.POST_PROCESSING
+ && currentState != ProcessState.COMPLETED) {
+ logger.info(
+ "Process {} at state {}, transitioning through EXECUTING before COMPLETED",
+ context.getProcessId(),
+ currentState);
+ publishProcessStatus(context, ProcessState.EXECUTING);
+ }
+ }
+ } catch (Exception e) {
+ logger.warn("Could not check/update intermediate process state for {}", context.getProcessId(), e);
+ }
+ }
+
+ private void publishProcessStatus(TaskContext context, ProcessState state) {
+ try {
+ var status = org.apache.airavata.core.model.StatusModel.of(state);
+ statusService.addProcessStatus(status, context.getProcessId());
+ logger.info("Saved intermediate process status {} for process {}", state, context.getProcessId());
+ } catch (Exception e) {
+ logger.warn("Failed to save process status {} for process {}", state, context.getProcessId(), e);
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Deprovision helpers — local data cleanup
+ // -------------------------------------------------------------------------
+
+ private void cleanupLocalData(TaskContext context) {
+ try {
+ String localDataPath = serverProperties.localDataLocation();
+ localDataPath = (localDataPath.endsWith(File.separator) ? localDataPath : localDataPath + File.separator);
+ localDataPath = localDataPath + context.getProcessId();
+
+ Path path = Path.of(localDataPath);
+ if (Files.exists(path)) {
+ Files.walk(path).sorted((a, b) -> b.compareTo(a)).forEach(p -> {
+ try {
+ Files.delete(p);
+ } catch (IOException e) {
+ logger.warn("Failed to delete {}", p, e);
+ }
+ });
+ logger.info("Cleaned up local data directory {}", localDataPath);
+ }
+ } catch (Exception e) {
+ logger.error("Failed to clean up local data directory for process {}", context.getProcessId(), e);
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Deprovision helpers — remote working directory cleanup
+ // -------------------------------------------------------------------------
+
+ private void cleanupWorkingDirectory(TaskContext context) {
+ try {
+ var adapter = adapterSupport.fetchAdapter(
+ context.getGatewayId(),
+ context.getComputeResourceId(),
+ context.getJobSubmissionProtocol(),
+ context.getComputeResourceCredentialToken(),
+ context.getComputeResourceLoginUserName());
+ logger.info("Cleaning up working directory {}", context.getWorkingDir());
+ adapter.deleteDirectory(context.getWorkingDir());
+ } catch (Exception e) {
+ logger.error("Failed to clean up working directory for experiment {}", context.getExperimentId(), e);
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Cancel helpers — remote job cancellation
+ // -------------------------------------------------------------------------
+
+ private void cancelRemoteJobs(TaskContext context) throws Exception {
+ List jobs = jobRepository.findByProcessId(context.getProcessId()).stream()
+ .map(entity -> {
+ Job m = new Job();
+ m.setJobId(entity.getJobId());
+ m.setProcessId(entity.getProcessId());
+ m.setJobStatuses(entity.getJobStatuses());
+ return m;
+ })
+ .toList();
+
+ if (jobs.isEmpty()) {
+ logger.info("No jobs found for process {} — nothing to cancel", context.getProcessId());
+ return;
+ }
+
+ logger.info(
+ "Found {} jobs for process {} — checking states and cancelling", jobs.size(), context.getProcessId());
+
+ JobManagerSpec jobManagerConfig;
+ try {
+ jobManagerConfig = jobFactory.getJobManagerConfiguration(context.getComputeResource());
+ } catch (Exception e) {
+ logger.warn(
+ "Failed to resolve job manager configuration for process {}. Skipping cancellation.",
+ context.getProcessId(),
+ e);
+ return;
+ }
+
+ AgentAdapter adapter = adapterSupport.fetchAdapter(
+ context.getGatewayId(),
+ context.getComputeResourceId(),
+ context.getJobSubmissionProtocol(),
+ context.getComputeResourceCredentialToken(),
+ context.getComputeResourceLoginUserName());
+
+ for (Job job : jobs) {
+ if (isJobAlreadySaturated(job, adapter, jobManagerConfig)) {
+ continue;
+ }
+
+ try {
+ RawCommandInfo cancelCommand = jobManagerConfig.getCancelCommand(job.getJobId());
+ logger.info("Cancelling job {} with command: {}", job.getJobId(), cancelCommand.getRawCommand());
+
+ CommandOutput cancelOutput = adapter.executeCommand(cancelCommand.getRawCommand(), null);
+ if (cancelOutput.getExitCode() != 0) {
+ logger.warn(
+ "Cancel command failed for job {}: stdout={}, stderr={}",
+ job.getJobId(),
+ cancelOutput.getStdOut(),
+ cancelOutput.getStdError());
+ }
+ } catch (Exception ex) {
+ logger.error("Error cancelling job {} of process {}", job.getJobId(), context.getProcessId());
+ throw ex;
+ }
+
+ publishJobStatus(job.getJobId(), JobState.CANCELED);
+ }
+ }
+
+ private boolean isJobAlreadySaturated(Job job, AgentAdapter adapter, JobManagerSpec config) {
+ if (job.getJobStatuses() != null && !job.getJobStatuses().isEmpty()) {
+ StatusModel lastStatus = job.getJobStatuses().stream()
+ .max(Comparator.comparing(StatusModel::getTimeOfStateChange))
+ .orElse(null);
+ if (lastStatus != null) {
+ switch (lastStatus.getState()) {
+ case FAILED, CANCELED, COMPLETED, SUSPENDED -> {
+ logger.info(
+ "Job {} already in saturated state {} (monitoring)",
+ job.getJobId(),
+ lastStatus.getState());
+ return true;
+ }
+ default -> {}
+ }
+ }
+ }
+
+ try {
+ var monitorCommand = config.getMonitorCommand(job.getJobId());
+ if (monitorCommand.isEmpty()) {
+ return false;
+ }
+
+ CommandOutput output = adapter.executeCommand(monitorCommand.get().getRawCommand(), null);
+ if (output.getExitCode() == 0) {
+ StatusModel clusterStatus =
+ config.getParser().parseJobStatus(job.getJobId(), output.getStdOut());
+ if (clusterStatus != null) {
+ switch (clusterStatus.getState()) {
+ case COMPLETED, CANCELED, SUSPENDED, FAILED -> {
+ logger.info(
+ "Job {} already in saturated state {} (cluster)",
+ job.getJobId(),
+ clusterStatus.getState());
+ return true;
+ }
+ default -> {}
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.warn("Error checking cluster status for job {} — proceeding with cancellation", job.getJobId(), e);
+ }
+
+ return false;
+ }
+
+ // -------------------------------------------------------------------------
+ // Cancel helpers — job status publishing
+ // -------------------------------------------------------------------------
+
+ private void publishJobStatus(String jobId, JobState jobState) {
+ try {
+ StatusModel jobStatus = StatusModel.of(jobState, jobState.name());
+ statusService.addJobStatus(jobStatus, jobId);
+ } catch (Exception e) {
+ logger.error("Error persisting job status for job {}", jobId, e);
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SlurmJobSpec.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SlurmJobSpec.java
new file mode 100644
index 00000000000..57b5fe3227b
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SlurmJobSpec.java
@@ -0,0 +1,94 @@
+/**
+*
+* 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.compute.provider.slurm;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.airavata.compute.resource.model.JobManagerCommand;
+import org.apache.airavata.compute.resource.submission.JobManagerSpec;
+import org.apache.airavata.compute.resource.submission.JobOutputParser;
+import org.apache.airavata.compute.resource.submission.RawCommandInfo;
+
+public class SlurmJobSpec implements JobManagerSpec {
+ private final Map jMCommands;
+ private final String jobDescriptionTemplateName;
+ private final String scriptExtension;
+ private final String installedPath;
+ private final JobOutputParser parser;
+
+ public SlurmJobSpec(
+ String jobDescriptionTemplateName,
+ String scriptExtension,
+ String installedPath,
+ Map jobManagerCommands,
+ JobOutputParser parser) {
+ this.jobDescriptionTemplateName = jobDescriptionTemplateName;
+ this.scriptExtension = scriptExtension;
+ this.parser = parser;
+ this.jMCommands = jobManagerCommands;
+ installedPath = installedPath.trim();
+ if (installedPath.endsWith("/")) {
+ this.installedPath = installedPath;
+ } else {
+ this.installedPath = installedPath + "/";
+ }
+ }
+
+ @Override
+ public RawCommandInfo getCancelCommand(String jobID) {
+ return new RawCommandInfo(
+ this.installedPath + jMCommands.get(JobManagerCommand.DELETION).trim() + " " + jobID);
+ }
+
+ @Override
+ public String getJobDescriptionTemplateName() {
+ return jobDescriptionTemplateName;
+ }
+
+ @Override
+ public Optional getMonitorCommand(String jobID) {
+ return Optional.of(new RawCommandInfo(this.installedPath
+ + jMCommands.get(JobManagerCommand.JOB_MONITORING).trim() + " -j " + jobID));
+ }
+
+ @Override
+ public Optional getJobIdMonitorCommand(String jobName, String userName) {
+ return Optional.of(new RawCommandInfo(this.installedPath
+ + jMCommands.get(JobManagerCommand.JOB_MONITORING).trim() + " -n " + jobName + " -u " + userName));
+ }
+
+ @Override
+ public String getScriptExtension() {
+ return scriptExtension;
+ }
+
+ @Override
+ public RawCommandInfo getSubmitCommand(String workingDirectory, String scriptFilePath) {
+ return new RawCommandInfo(this.installedPath
+ + jMCommands.get(JobManagerCommand.SUBMISSION).trim() + " " + workingDirectory + File.separator
+ + java.nio.file.Path.of(scriptFilePath).getFileName().toString());
+ }
+
+ @Override
+ public JobOutputParser getParser() {
+ return parser;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SlurmOutputParser.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SlurmOutputParser.java
new file mode 100644
index 00000000000..d4edac7f802
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/provider/slurm/SlurmOutputParser.java
@@ -0,0 +1,138 @@
+/**
+*
+* 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.compute.provider.slurm;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.airavata.compute.resource.model.JobState;
+import org.apache.airavata.compute.resource.submission.JobOutputParser;
+import org.apache.airavata.compute.resource.submission.JobStateParser;
+import org.apache.airavata.core.model.StatusModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SlurmOutputParser implements JobOutputParser {
+ private static final Logger logger = LoggerFactory.getLogger(SlurmOutputParser.class);
+ public static final int JOB_NAME_OUTPUT_LENGTH = 8;
+ public static final String STATUS = "status";
+ public static final String JOBID = "jobId";
+
+ /**
+ * This can be used to parseSingleJob the outpu of sbatch and extrac the jobID from the content
+ *
+ * @param rawOutput
+ * @return
+ */
+ public String parseJobSubmission(String rawOutput) throws Exception {
+ logger.info(rawOutput);
+ Pattern pattern = Pattern.compile("Submitted batch job (?<" + JOBID + ">[^\\s]*)");
+ Matcher matcher = pattern.matcher(rawOutput);
+ if (matcher.find()) {
+ return matcher.group(JOBID);
+ }
+ return "";
+ }
+
+ @Override
+ public boolean isJobSubmissionFailed(String rawOutput) {
+ Pattern pattern = Pattern.compile("FAILED");
+ Matcher matcher = pattern.matcher(rawOutput);
+ return matcher.find();
+ }
+
+ public StatusModel parseJobStatus(String jobID, String rawOutput) throws Exception {
+ logger.info(rawOutput);
+ Pattern pattern = Pattern.compile(jobID + "(?=\\s+\\S+\\s+\\S+\\s+\\S+\\s+(?<" + STATUS + ">\\w+))");
+ Matcher matcher = pattern.matcher(rawOutput);
+ if (matcher.find()) {
+ StatusModel jobStatus = new StatusModel<>();
+ jobStatus.setState(JobStateParser.getJobState(matcher.group(STATUS)));
+ return jobStatus;
+ }
+ return null;
+ }
+
+ public void parseJobStatuses(String userName, Map> statusMap, String rawOutput)
+ throws Exception {
+ logger.debug(rawOutput);
+ String[] info = rawOutput.split("\n");
+ String lastString = info[info.length - 1];
+ if (lastString.contains("JOBID") || lastString.contains("PARTITION")) {
+ logger.info("There are no jobs with this username ... ");
+ return;
+ }
+ // int lastStop = 0;
+ for (String jobID : statusMap.keySet()) {
+ String jobId = jobID.split(",")[0];
+ String jobName = jobID.split(",")[1];
+ boolean found = false;
+ for (int i = 0; i < info.length; i++) {
+ if (info[i].contains(jobName.substring(0, 8))) {
+ // now starts processing this line
+ logger.info(info[i]);
+ String correctLine = info[i];
+ List columnList = java.util.Arrays.stream(correctLine.split(" "))
+ .filter(s -> !s.isEmpty())
+ .toList();
+ try {
+ StatusModel jobStatus = new StatusModel<>();
+ jobStatus.setState(JobState.valueOf(columnList.get(4)));
+ statusMap.put(jobID, jobStatus);
+ } catch (IndexOutOfBoundsException e) {
+ StatusModel jobStatus = new StatusModel<>();
+ jobStatus.setState(JobState.UNKNOWN);
+ statusMap.put(jobID, jobStatus);
+ }
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ logger.error("Couldn't find the status of the Job with JobName: {} Job Id: {}", jobName, jobId);
+ }
+ }
+ }
+
+ @Override
+ public String parseJobId(String jobName, String rawOutput) throws Exception {
+ String regJobId = "jobId";
+ if (jobName == null) {
+ return null;
+ } else if (jobName.length() > JOB_NAME_OUTPUT_LENGTH) {
+ jobName = jobName.substring(0, JOB_NAME_OUTPUT_LENGTH);
+ }
+ Pattern pattern = Pattern.compile(
+ "(?=(?<" + regJobId + ">\\d+)\\s+\\w+\\s+" + jobName + ")"); // regex - look ahead and match
+ if (rawOutput != null) {
+ Matcher matcher = pattern.matcher(rawOutput);
+ if (matcher.find()) {
+ return matcher.group(regJobId);
+ } else {
+ logger.error("No match is found for JobName");
+ return null;
+ }
+ } else {
+ logger.error("Error: RawOutput shouldn't be null");
+ return null;
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/adapter/ComputeResourceAdapter.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/adapter/ComputeResourceAdapter.java
new file mode 100644
index 00000000000..738fb983f25
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/adapter/ComputeResourceAdapter.java
@@ -0,0 +1,164 @@
+/**
+*
+* 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.compute.resource.adapter;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.airavata.compute.resource.entity.ResourceEntity;
+import org.apache.airavata.compute.resource.model.ComputeCapability;
+import org.apache.airavata.compute.resource.model.JobSubmissionProtocol;
+import org.apache.airavata.compute.resource.model.Resource;
+import org.apache.airavata.compute.resource.model.ResourceJobManagerType;
+import org.apache.airavata.compute.resource.repository.ResourceRepository;
+import org.apache.airavata.protocol.ResourceLookup;
+import org.apache.airavata.storage.resource.model.DataMovementProtocol;
+import org.apache.airavata.storage.resource.model.StorageCapability;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Adapter service that maps compute and storage resource entities to the clean {@link Resource}
+ * domain model used by protocol adapters and the workflow engine.
+ *
+ *
Implements {@link ResourceLookup} so that protocol adapters can resolve connection details
+ * (hostname, port, capabilities) from resource IDs without depending on JPA entities directly.
+ *
+ *
All methods are read-only and return {@code null} or empty collections when a resource
+ * is not found.
+ */
+@Service
+@Transactional(readOnly = true)
+public class ComputeResourceAdapter implements ResourceLookup {
+
+ private static final Logger logger = LoggerFactory.getLogger(ComputeResourceAdapter.class);
+
+ private final ResourceRepository resourceRepository;
+
+ public ComputeResourceAdapter(ResourceRepository resourceRepository) {
+ this.resourceRepository = resourceRepository;
+ }
+
+ private Resource toModel(ResourceEntity entity) {
+ var model = new Resource();
+ model.setResourceId(entity.getResourceId());
+ model.setGatewayId(entity.getGatewayId());
+ model.setName(entity.getName());
+ model.setHostName(entity.getHostName());
+ model.setPort(entity.getPort() != null ? entity.getPort() : 22);
+ model.setDescription(entity.getDescription());
+ model.setCapabilities(entity.getCapabilities());
+ model.setCreatedAt(entity.getCreatedAt());
+ model.setUpdatedAt(entity.getUpdatedAt());
+ return model;
+ }
+
+ // -------------------------------------------------------------------------
+ // Resource lookup
+ // -------------------------------------------------------------------------
+
+ @Override
+ public Resource getResource(String resourceId) {
+ return resourceRepository.findById(resourceId).map(this::toModel).orElseGet(() -> {
+ logger.debug("getResource: no resource found for id={}", resourceId);
+ return null;
+ });
+ }
+
+ // -------------------------------------------------------------------------
+ // Resource name maps
+ // -------------------------------------------------------------------------
+
+ /**
+ * Return a map of all resource IDs to resource names.
+ *
+ * @return map; empty if no resources exist
+ */
+ public Map getAllComputeResourceNames() {
+ return resourceRepository.findAll().stream()
+ .collect(Collectors.toMap(ResourceEntity::getResourceId, ResourceEntity::getName));
+ }
+
+ /**
+ * Return a map of resource IDs to resource names for resources that have a storage capability.
+ *
+ * @return map; empty if no storage resources exist
+ */
+ public Map getAllStorageResourceNames() {
+ return resourceRepository.findAll().stream()
+ .filter(r -> r.getCapabilities() != null && r.getCapabilities().getStorage() != null)
+ .collect(Collectors.toMap(ResourceEntity::getResourceId, ResourceEntity::getName));
+ }
+
+ // -------------------------------------------------------------------------
+ // Enum mapping helpers
+ // -------------------------------------------------------------------------
+
+ /**
+ * Map the protocol string from {@link ComputeCapability} to the
+ * {@link JobSubmissionProtocol} enum.
+ *
+ *
Recognised values (case-insensitive): {@code "SSH_FORK"} →
+ * {@link JobSubmissionProtocol#SSH_FORK}, {@code "CLOUD"} →
+ * {@link JobSubmissionProtocol#CLOUD}. All other values map to
+ * {@link JobSubmissionProtocol#SSH}.
+ *
+ * @param protocol protocol string from the capability model
+ * @return matching enum constant
+ */
+ public JobSubmissionProtocol mapJobSubmissionProtocol(String protocol) {
+ if (protocol == null) {
+ return JobSubmissionProtocol.SSH;
+ }
+ return switch (protocol.toUpperCase()) {
+ case "SSH_FORK" -> JobSubmissionProtocol.SSH_FORK;
+ case "CLOUD" -> JobSubmissionProtocol.CLOUD;
+ default -> JobSubmissionProtocol.SSH;
+ };
+ }
+
+ /**
+ * Map the protocol string from {@link StorageCapability} to the
+ * {@link DataMovementProtocol} enum.
+ *
+ *
Recognised values (case-insensitive): {@code "SFTP"} →
+ * {@link DataMovementProtocol#SFTP}, {@code "LOCAL"} →
+ * {@link DataMovementProtocol#LOCAL}. All other values map to
+ * {@link DataMovementProtocol#SCP}.
+ *
+ * @param protocol protocol string from the capability model
+ * @return matching enum constant
+ */
+ public DataMovementProtocol mapDataMovementProtocol(String protocol) {
+ if (protocol == null) {
+ return DataMovementProtocol.SCP;
+ }
+ return switch (protocol.toUpperCase()) {
+ case "SFTP" -> DataMovementProtocol.SFTP;
+ case "LOCAL" -> DataMovementProtocol.LOCAL;
+ default -> DataMovementProtocol.SCP;
+ };
+ }
+
+ public ResourceJobManagerType mapResourceJobManagerType(String jobManagerType) {
+ return ResourceJobManagerType.fromString(jobManagerType);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/adapter/ResourceProfileAdapter.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/adapter/ResourceProfileAdapter.java
new file mode 100644
index 00000000000..94987a1d3bc
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/adapter/ResourceProfileAdapter.java
@@ -0,0 +1,210 @@
+/**
+*
+* 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.compute.resource.adapter;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.airavata.compute.resource.entity.ResourceBindingEntity;
+import org.apache.airavata.compute.resource.entity.ResourceEntity;
+import org.apache.airavata.compute.resource.repository.ResourceBindingRepository;
+import org.apache.airavata.compute.resource.repository.ResourceRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Adapter service that maps resource binding entities to profile/preference views
+ * used by the workflow engine and IAM layer.
+ *
+ *
Read methods return {@code null} or empty collections when bindings are not found.
+ */
+@Service
+@Transactional(readOnly = true)
+public class ResourceProfileAdapter {
+
+ private static final Logger logger = LoggerFactory.getLogger(ResourceProfileAdapter.class);
+
+ private final ResourceRepository resourceRepository;
+ private final ResourceBindingRepository resourceBindingRepository;
+
+ public ResourceProfileAdapter(
+ ResourceRepository resourceRepository, ResourceBindingRepository resourceBindingRepository) {
+ this.resourceRepository = resourceRepository;
+ this.resourceBindingRepository = resourceBindingRepository;
+ }
+
+ // -------------------------------------------------------------------------
+ // Clean binding methods
+ // -------------------------------------------------------------------------
+
+ /**
+ * Look up a resource binding for the given compute resource within a gateway.
+ *
+ *
Falls back to a gateway-agnostic lookup if no binding matches the gateway filter.
+ *
+ * @param computeResourceId the compute resource identifier
+ * @param gatewayId the gateway identifier
+ * @return the first matching binding, or {@code null} if none found
+ */
+ public ResourceBindingEntity getBinding(String computeResourceId, String gatewayId) {
+ List bindings =
+ resourceBindingRepository.findByGatewayIdAndResourceId(gatewayId, computeResourceId);
+ if (bindings.isEmpty()) {
+ bindings = resourceBindingRepository.findByResourceId(computeResourceId);
+ }
+ if (bindings.isEmpty()) {
+ logger.debug("getBinding: no binding found for resourceId={}, gatewayId={}", computeResourceId, gatewayId);
+ return null;
+ }
+ return bindings.get(0);
+ }
+
+ /**
+ * Look up a resource binding for the given user, gateway, and compute resource.
+ *
+ * @param userName the user name (informational; not stored on bindings)
+ * @param gatewayId the gateway identifier
+ * @param computeResourceId the compute resource identifier
+ * @return the first matching binding, or {@code null} if none found
+ */
+ public ResourceBindingEntity getUserBinding(String userName, String gatewayId, String computeResourceId) {
+ List bindings =
+ resourceBindingRepository.findByGatewayIdAndResourceId(gatewayId, computeResourceId);
+ if (bindings.isEmpty()) {
+ logger.debug(
+ "getUserBinding: no binding found for gatewayId={}, resourceId={}", gatewayId, computeResourceId);
+ return null;
+ }
+ return bindings.get(0);
+ }
+
+ /**
+ * Look up a storage binding for the given gateway and storage resource.
+ *
+ * @param gatewayId the gateway identifier
+ * @param storageResourceId the storage resource identifier
+ * @return the first matching binding, or {@code null} if none found
+ */
+ public ResourceBindingEntity getStorageBinding(String gatewayId, String storageResourceId) {
+ List bindings =
+ resourceBindingRepository.findByGatewayIdAndResourceId(gatewayId, storageResourceId);
+ if (bindings.isEmpty()) {
+ logger.debug(
+ "getStorageBinding: no binding found for gatewayId={}, storageResourceId={}",
+ gatewayId,
+ storageResourceId);
+ return null;
+ }
+ return bindings.get(0);
+ }
+
+ /**
+ * Resolve the file system root location for a storage binding.
+ *
+ *
Looks up the {@code "fileSystemRootLocation"} from the binding metadata first;
+ * falls back to {@code basePath} from the resource's storage capability.
+ *
+ * @param binding the storage binding
+ * @param storageResourceId the storage resource identifier
+ * @return the root location, or {@code null} if not configured
+ */
+ public String resolveStorageRootLocation(ResourceBindingEntity binding, String storageResourceId) {
+ String rootLocation = getMetadataString(binding.getMetadata(), "fileSystemRootLocation");
+ if (rootLocation == null) {
+ Optional resourceOpt = resourceRepository.findById(storageResourceId);
+ if (resourceOpt.isPresent()) {
+ ResourceEntity resource = resourceOpt.get();
+ if (resource.getCapabilities() != null
+ && resource.getCapabilities().getStorage() != null) {
+ rootLocation = resource.getCapabilities().getStorage().getBasePath();
+ }
+ }
+ }
+ return rootLocation;
+ }
+
+ /**
+ * Return the default credential store token for a gateway.
+ *
+ *
Returns the credential ID from the first binding found for the gateway,
+ * or {@code null} if no bindings exist.
+ *
+ * @param gatewayId the gateway identifier
+ * @return credential token, or {@code null}
+ */
+ public String getGatewayDefaultCredentialToken(String gatewayId) {
+ List bindings = resourceBindingRepository.findByGatewayId(gatewayId);
+ if (bindings.isEmpty()) {
+ return null;
+ }
+ return bindings.get(0).getCredentialId();
+ }
+
+ // -------------------------------------------------------------------------
+ // Existence checks
+ // -------------------------------------------------------------------------
+
+ public boolean isGroupResourceProfileExists(String groupResourceProfileId) {
+ return !resourceBindingRepository
+ .findByGatewayId(groupResourceProfileId)
+ .isEmpty();
+ }
+
+ public boolean isGroupComputeResourcePreferenceExists(String computeResourceId, String groupResourceProfileId) {
+ return !resourceBindingRepository
+ .findByGatewayIdAndResourceId(groupResourceProfileId, computeResourceId)
+ .isEmpty();
+ }
+
+ public boolean isUserComputeResourcePreferenceExists(String userId, String gatewayId, String computeResourceId) {
+ return !resourceBindingRepository
+ .findByGatewayIdAndResourceId(gatewayId, computeResourceId)
+ .isEmpty();
+ }
+
+ public boolean isUserResourceProfileExists(String userId, String gatewayId) {
+ return !resourceBindingRepository.findByGatewayId(gatewayId).isEmpty();
+ }
+
+ // -------------------------------------------------------------------------
+ // Helpers
+ // -------------------------------------------------------------------------
+
+ /**
+ * Safely retrieve a String value from a metadata map.
+ *
+ * @param metadata the metadata map (may be {@code null})
+ * @param key the map key to look up
+ * @return the value as String, or {@code null} if the map is null or the key is absent
+ */
+ public static String getMetadataString(Map metadata, String key) {
+ if (metadata == null) {
+ return null;
+ }
+ Object value = metadata.get(key);
+ return value instanceof String ? (String) value : (value != null ? String.valueOf(value) : null);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/JobEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/JobEntity.java
new file mode 100644
index 00000000000..a3eab852f96
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/JobEntity.java
@@ -0,0 +1,180 @@
+/**
+*
+* 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.compute.resource.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Index;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import jakarta.persistence.Transient;
+import java.io.Serializable;
+import java.time.Instant;
+import java.util.List;
+import org.apache.airavata.compute.resource.model.JobState;
+import org.apache.airavata.core.model.StatusModel;
+import org.apache.airavata.execution.process.ProcessEntity;
+
+@Entity
+@Table(
+ name = "job",
+ indexes = {
+ @Index(name = "idx_job_process_id", columnList = "process_id"),
+ @Index(name = "idx_job_name", columnList = "job_name")
+ })
+public class JobEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @Column(name = "job_id", nullable = false, length = 255)
+ private String jobId;
+
+ @Column(name = "process_id", nullable = false, length = 255)
+ private String processId;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "process_id", insertable = false, updatable = false)
+ private ProcessEntity process;
+
+ @Column(name = "job_name", length = 255)
+ private String jobName;
+
+ @Column(name = "working_dir", columnDefinition = "MEDIUMTEXT")
+ private String workingDir;
+
+ @Column(name = "job_description", columnDefinition = "MEDIUMTEXT")
+ private String jobDescription;
+
+ @Column(name = "std_out", columnDefinition = "MEDIUMTEXT")
+ private String stdOut;
+
+ @Column(name = "std_err", columnDefinition = "MEDIUMTEXT")
+ private String stdErr;
+
+ @Column(name = "exit_code")
+ private int exitCode;
+
+ @Column(name = "created_at", nullable = false, updatable = false)
+ private Instant createdAt;
+
+ @Column(name = "compute_resource_consumed", length = 255)
+ private String computeResourceConsumed;
+
+ /** Job status history loaded from the EVENT table. Not persisted as a JSON column. */
+ @Transient
+ private List> jobStatuses;
+
+ public JobEntity() {}
+
+ public String getJobId() {
+ return jobId;
+ }
+
+ public void setJobId(String jobId) {
+ this.jobId = jobId;
+ }
+
+ public String getProcessId() {
+ return processId;
+ }
+
+ public void setProcessId(String processId) {
+ this.processId = processId;
+ }
+
+ public String getJobName() {
+ return jobName;
+ }
+
+ public void setJobName(String jobName) {
+ this.jobName = jobName;
+ }
+
+ public String getWorkingDir() {
+ return workingDir;
+ }
+
+ public void setWorkingDir(String workingDir) {
+ this.workingDir = workingDir;
+ }
+
+ public String getJobDescription() {
+ return jobDescription;
+ }
+
+ public void setJobDescription(String jobDescription) {
+ this.jobDescription = jobDescription;
+ }
+
+ public String getStdOut() {
+ return stdOut;
+ }
+
+ public void setStdOut(String stdOut) {
+ this.stdOut = stdOut;
+ }
+
+ public String getStdErr() {
+ return stdErr;
+ }
+
+ public void setStdErr(String stdErr) {
+ this.stdErr = stdErr;
+ }
+
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ public void setExitCode(int exitCode) {
+ this.exitCode = exitCode;
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public String getComputeResourceConsumed() {
+ return computeResourceConsumed;
+ }
+
+ public void setComputeResourceConsumed(String computeResourceConsumed) {
+ this.computeResourceConsumed = computeResourceConsumed;
+ }
+
+ /**
+ * Returns the transient job status list. This field is loaded from the EVENT table
+ * at the service layer and is not persisted directly on this entity.
+ */
+ public List> getJobStatuses() {
+ return jobStatuses;
+ }
+
+ public void setJobStatuses(List> jobStatuses) {
+ this.jobStatuses = jobStatuses;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/ResourceBindingEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/ResourceBindingEntity.java
new file mode 100644
index 00000000000..4ed191bc656
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/ResourceBindingEntity.java
@@ -0,0 +1,191 @@
+/**
+*
+* 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.compute.resource.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import jakarta.persistence.UniqueConstraint;
+import java.io.Serializable;
+import java.time.Instant;
+import java.util.Map;
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+/**
+ * Entity that binds a specific credential to a specific resource within a gateway.
+ *
+ *
A binding associates a credential with a {@link ResourceEntity} and
+ * records the login username to be used when authenticating against that resource with
+ * the given credential. The {@code metadata} JSON map carries any additional resource-specific
+ * properties that vary per binding (e.g. scratch directory, allocation project codes).
+ *
+ *
A unique constraint on {@code (CREDENTIAL_ID, RESOURCE_ID, LOGIN_USERNAME)} ensures each
+ * credential is bound to a given resource with a specific login username exactly once.
+ */
+@Entity
+@Table(
+ name = "resource_binding",
+ uniqueConstraints = {
+ @UniqueConstraint(
+ name = "uk_binding_cred_resource_user",
+ columnNames = {"credential_id", "resource_id", "login_username"})
+ })
+@EntityListeners(AuditingEntityListener.class)
+public class ResourceBindingEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @Column(name = "binding_id")
+ private String bindingId;
+
+ @Column(name = "credential_id", nullable = false)
+ private String credentialId;
+
+ @Column(name = "resource_id", nullable = false)
+ private String resourceId;
+
+ @Column(name = "login_username", nullable = false)
+ private String loginUsername;
+
+ @JdbcTypeCode(SqlTypes.JSON)
+ @Column(name = "metadata", columnDefinition = "json")
+ private Map metadata;
+
+ @Column(name = "enabled")
+ private boolean enabled = true;
+
+ @Column(name = "gateway_id", nullable = false)
+ private String gatewayId;
+
+ @CreatedDate
+ @Column(name = "created_at", nullable = false, updatable = false)
+ private Instant createdAt;
+
+ @LastModifiedDate
+ @Column(name = "updated_at")
+ private Instant updatedAt;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "resource_id", insertable = false, updatable = false)
+ private ResourceEntity resource;
+
+ public ResourceBindingEntity() {}
+
+ public String getBindingId() {
+ return bindingId;
+ }
+
+ public void setBindingId(String bindingId) {
+ this.bindingId = bindingId;
+ }
+
+ public String getCredentialId() {
+ return credentialId;
+ }
+
+ public void setCredentialId(String credentialId) {
+ this.credentialId = credentialId;
+ }
+
+ public String getResourceId() {
+ return resourceId;
+ }
+
+ public void setResourceId(String resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public String getLoginUsername() {
+ return loginUsername;
+ }
+
+ public void setLoginUsername(String loginUsername) {
+ this.loginUsername = loginUsername;
+ }
+
+ public Map getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(Map metadata) {
+ this.metadata = metadata;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public Instant getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(Instant updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+
+ public ResourceEntity getResource() {
+ return resource;
+ }
+
+ public void setResource(ResourceEntity resource) {
+ this.resource = resource;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceBindingEntity{"
+ + "bindingId='" + bindingId + '\''
+ + ", credentialId='" + credentialId + '\''
+ + ", resourceId='" + resourceId + '\''
+ + ", loginUsername='" + loginUsername + '\''
+ + ", enabled=" + enabled
+ + ", gatewayId='" + gatewayId + '\''
+ + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/ResourceEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/ResourceEntity.java
new file mode 100644
index 00000000000..d1565a6fc27
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/ResourceEntity.java
@@ -0,0 +1,198 @@
+/**
+*
+* 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.compute.resource.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import java.io.Serializable;
+import java.time.Instant;
+import org.apache.airavata.compute.resource.model.ResourceCapabilities;
+import org.apache.airavata.compute.resource.model.ResourceType;
+import org.apache.airavata.gateway.entity.GatewayEntity;
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+/**
+ * Entity representing a compute or storage resource accessible within a gateway.
+ *
+ *
A resource corresponds to a physical or virtual host that Airavata can submit
+ * jobs to or transfer data from/to. The {@code capabilities} JSON field captures
+ * the set of supported job submission and data movement protocols as a structured object.
+ * The {@code resourceType} field classifies the resource as either
+ * {@link ResourceType#COMPUTE} or {@link ResourceType#STORAGE}.
+ */
+@Entity
+@Table(name = "resource")
+@EntityListeners(AuditingEntityListener.class)
+public class ResourceEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @Column(name = "resource_id")
+ private String resourceId;
+
+ @Column(name = "gateway_id", nullable = false)
+ private String gatewayId;
+
+ @Column(name = "name", nullable = false)
+ private String name;
+
+ @Column(name = "host_name", nullable = false)
+ private String hostName;
+
+ @Column(name = "port")
+ private Integer port = 22;
+
+ @Column(name = "description", columnDefinition = "TEXT")
+ private String description;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "resource_type", nullable = false, length = 20)
+ private ResourceType resourceType = ResourceType.COMPUTE;
+
+ @JdbcTypeCode(SqlTypes.JSON)
+ @Column(name = "capabilities", nullable = false, columnDefinition = "json")
+ private ResourceCapabilities capabilities;
+
+ @CreatedDate
+ @Column(name = "created_at", nullable = false, updatable = false)
+ private Instant createdAt;
+
+ @LastModifiedDate
+ @Column(name = "updated_at")
+ private Instant updatedAt;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "gateway_id", insertable = false, updatable = false)
+ private GatewayEntity gateway;
+
+ public ResourceEntity() {}
+
+ public String getResourceId() {
+ return resourceId;
+ }
+
+ public void setResourceId(String resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getHostName() {
+ return hostName;
+ }
+
+ public void setHostName(String hostName) {
+ this.hostName = hostName;
+ }
+
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public ResourceType getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(ResourceType resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public ResourceCapabilities getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(ResourceCapabilities capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public Instant getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(Instant updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+
+ public GatewayEntity getGateway() {
+ return gateway;
+ }
+
+ public void setGateway(GatewayEntity gateway) {
+ this.gateway = gateway;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceEntity{"
+ + "resourceId='" + resourceId + '\''
+ + ", gatewayId='" + gatewayId + '\''
+ + ", name='" + name + '\''
+ + ", hostName='" + hostName + '\''
+ + ", port=" + port
+ + ", resourceType='" + resourceType + '\''
+ + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/ResourcePreferenceEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/ResourcePreferenceEntity.java
new file mode 100644
index 00000000000..4dc07eb9890
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/entity/ResourcePreferenceEntity.java
@@ -0,0 +1,152 @@
+/**
+*
+* 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.compute.resource.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import java.io.Serializable;
+import org.apache.airavata.compute.resource.model.PreferenceLevel;
+import org.apache.airavata.compute.resource.model.PreferenceResourceType;
+import org.apache.airavata.compute.resource.model.PreferenceValueType;
+
+/**
+ * JPA entity for the RESOURCE_PREFERENCE table.
+ *
+ *
Stores multi-level, multi-resource preferences for gateways, groups, and users.
+ * The tuple (resourceType, resourceId, ownerId, level, key) uniquely identifies a preference value.
+ */
+@Entity
+@Table(name = "resource_preference")
+public class ResourcePreferenceEntity implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "preference_id")
+ private Long preferenceId;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "resource_type", nullable = false, length = 64)
+ private PreferenceResourceType resourceType;
+
+ @Column(name = "resource_id", nullable = false, length = 512)
+ private String resourceId;
+
+ @Column(name = "owner_id", nullable = false, length = 512)
+ private String ownerId;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "preference_level", nullable = false, length = 32)
+ private PreferenceLevel level;
+
+ @Column(name = "pref_key", nullable = false, length = 255)
+ private String key;
+
+ @Column(name = "pref_value", columnDefinition = "MEDIUMTEXT")
+ private String value;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "value_type", length = 32)
+ private PreferenceValueType valueType;
+
+ @Column(name = "enforced", nullable = false)
+ private boolean enforced;
+
+ public ResourcePreferenceEntity() {}
+
+ public Long getPreferenceId() {
+ return preferenceId;
+ }
+
+ public void setPreferenceId(Long preferenceId) {
+ this.preferenceId = preferenceId;
+ }
+
+ public PreferenceResourceType getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(PreferenceResourceType resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getResourceId() {
+ return resourceId;
+ }
+
+ public void setResourceId(String resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public String getOwnerId() {
+ return ownerId;
+ }
+
+ public void setOwnerId(String ownerId) {
+ this.ownerId = ownerId;
+ }
+
+ public PreferenceLevel getLevel() {
+ return level;
+ }
+
+ public void setLevel(PreferenceLevel level) {
+ this.level = level;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public PreferenceValueType getValueType() {
+ return valueType;
+ }
+
+ public void setValueType(PreferenceValueType valueType) {
+ this.valueType = valueType;
+ }
+
+ public boolean isEnforced() {
+ return enforced;
+ }
+
+ public void setEnforced(boolean enforced) {
+ this.enforced = enforced;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/mapper/JobMapper.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/mapper/JobMapper.java
new file mode 100644
index 00000000000..1c9183fad33
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/mapper/JobMapper.java
@@ -0,0 +1,35 @@
+/**
+*
+* 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.compute.resource.mapper;
+
+import org.apache.airavata.compute.resource.entity.JobEntity;
+import org.apache.airavata.compute.resource.model.Job;
+import org.apache.airavata.config.EntityMapperConfiguration;
+import org.apache.airavata.core.mapper.EntityMapper;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+@Mapper(componentModel = "spring", config = EntityMapperConfiguration.class)
+public interface JobMapper extends EntityMapper {
+
+ @Override
+ @Mapping(target = "jobStatuses", ignore = true)
+ JobEntity toEntity(Job model);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/mapper/ResourceBindingMapper.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/mapper/ResourceBindingMapper.java
new file mode 100644
index 00000000000..addbc54ceab
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/mapper/ResourceBindingMapper.java
@@ -0,0 +1,41 @@
+/**
+*
+* 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.compute.resource.mapper;
+
+import org.apache.airavata.compute.resource.entity.ResourceBindingEntity;
+import org.apache.airavata.compute.resource.model.ResourceBinding;
+import org.apache.airavata.config.EntityMapperConfiguration;
+import org.apache.airavata.core.mapper.EntityMapper;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+/**
+ * MapStruct mapper for converting between {@link ResourceBindingEntity} and {@link ResourceBinding}.
+ *
+ *
All scalar fields map 1:1. The {@code resource} lazy {@link org.apache.airavata.compute.resource.entity.ResourceEntity}
+ * association is excluded from both directions to prevent unintended Hibernate proxy initialisation.
+ */
+@Mapper(componentModel = "spring", config = EntityMapperConfiguration.class)
+public interface ResourceBindingMapper extends EntityMapper {
+
+ @Override
+ @Mapping(target = "resource", ignore = true)
+ ResourceBindingEntity toEntity(ResourceBinding model);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/mapper/ResourceMapper.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/mapper/ResourceMapper.java
new file mode 100644
index 00000000000..b60a6979f29
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/mapper/ResourceMapper.java
@@ -0,0 +1,44 @@
+/**
+*
+* 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.compute.resource.mapper;
+
+import org.apache.airavata.compute.resource.entity.ResourceEntity;
+import org.apache.airavata.compute.resource.model.Resource;
+import org.apache.airavata.config.EntityMapperConfiguration;
+import org.apache.airavata.core.mapper.EntityMapper;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+/**
+ * MapStruct mapper for converting between {@link ResourceEntity} and {@link Resource}.
+ *
+ *
All scalar fields map 1:1. The {@code gateway} lazy association on the entity side
+ * is excluded from both directions to prevent unintended Hibernate proxy initialisation.
+ * MapStruct handles the {@code Integer} (entity) to {@code int} (model) port widening
+ * automatically using the {@code NullValuePropertyMappingStrategy.IGNORE} policy defined
+ * in {@link EntityMapperConfiguration}.
+ */
+@Mapper(componentModel = "spring", config = EntityMapperConfiguration.class)
+public interface ResourceMapper extends EntityMapper {
+
+ @Override
+ @Mapping(target = "gateway", ignore = true)
+ ResourceEntity toEntity(Resource model);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ComputationalResourceScheduling.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ComputationalResourceScheduling.java
new file mode 100644
index 00000000000..bd8cce77c73
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ComputationalResourceScheduling.java
@@ -0,0 +1,196 @@
+/**
+*
+* 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.compute.resource.model;
+
+import java.util.Objects;
+
+/**
+ * Domain model: ComputationalResourceScheduling
+ */
+public class ComputationalResourceScheduling {
+ private String resourceHostId;
+ private int totalCPUCount;
+ private int nodeCount;
+ private int numberOfThreads;
+ private String queueName;
+ private int wallTimeLimit;
+ private int totalPhysicalMemory;
+ private String chessisNumber;
+ private String staticWorkingDir;
+ private String overrideLoginUserName;
+ private String overrideScratchLocation;
+ private String overrideAllocationProjectNumber;
+ private int mGroupCount;
+
+ public ComputationalResourceScheduling() {}
+
+ public String getResourceHostId() {
+ return resourceHostId;
+ }
+
+ public void setResourceHostId(String resourceHostId) {
+ this.resourceHostId = resourceHostId;
+ }
+
+ public int getTotalCPUCount() {
+ return totalCPUCount;
+ }
+
+ public void setTotalCPUCount(int totalCPUCount) {
+ this.totalCPUCount = totalCPUCount;
+ }
+
+ public int getNodeCount() {
+ return nodeCount;
+ }
+
+ public void setNodeCount(int nodeCount) {
+ this.nodeCount = nodeCount;
+ }
+
+ public int getNumberOfThreads() {
+ return numberOfThreads;
+ }
+
+ public void setNumberOfThreads(int numberOfThreads) {
+ this.numberOfThreads = numberOfThreads;
+ }
+
+ public String getQueueName() {
+ return queueName;
+ }
+
+ public void setQueueName(String queueName) {
+ this.queueName = queueName;
+ }
+
+ public int getWallTimeLimit() {
+ return wallTimeLimit;
+ }
+
+ public void setWallTimeLimit(int wallTimeLimit) {
+ this.wallTimeLimit = wallTimeLimit;
+ }
+
+ public int getTotalPhysicalMemory() {
+ return totalPhysicalMemory;
+ }
+
+ public void setTotalPhysicalMemory(int totalPhysicalMemory) {
+ this.totalPhysicalMemory = totalPhysicalMemory;
+ }
+
+ public String getChessisNumber() {
+ return chessisNumber;
+ }
+
+ public void setChessisNumber(String chessisNumber) {
+ this.chessisNumber = chessisNumber;
+ }
+
+ public String getStaticWorkingDir() {
+ return staticWorkingDir;
+ }
+
+ public void setStaticWorkingDir(String staticWorkingDir) {
+ this.staticWorkingDir = staticWorkingDir;
+ }
+
+ public String getOverrideLoginUserName() {
+ return overrideLoginUserName;
+ }
+
+ public void setOverrideLoginUserName(String overrideLoginUserName) {
+ this.overrideLoginUserName = overrideLoginUserName;
+ }
+
+ public String getOverrideScratchLocation() {
+ return overrideScratchLocation;
+ }
+
+ public void setOverrideScratchLocation(String overrideScratchLocation) {
+ this.overrideScratchLocation = overrideScratchLocation;
+ }
+
+ public String getOverrideAllocationProjectNumber() {
+ return overrideAllocationProjectNumber;
+ }
+
+ public void setOverrideAllocationProjectNumber(String overrideAllocationProjectNumber) {
+ this.overrideAllocationProjectNumber = overrideAllocationProjectNumber;
+ }
+
+ public int getMGroupCount() {
+ return mGroupCount;
+ }
+
+ public void setMGroupCount(int mGroupCount) {
+ this.mGroupCount = mGroupCount;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ComputationalResourceScheduling that = (ComputationalResourceScheduling) o;
+ return Objects.equals(resourceHostId, that.resourceHostId)
+ && Objects.equals(totalCPUCount, that.totalCPUCount)
+ && Objects.equals(nodeCount, that.nodeCount)
+ && Objects.equals(numberOfThreads, that.numberOfThreads)
+ && Objects.equals(queueName, that.queueName)
+ && Objects.equals(wallTimeLimit, that.wallTimeLimit)
+ && Objects.equals(totalPhysicalMemory, that.totalPhysicalMemory)
+ && Objects.equals(chessisNumber, that.chessisNumber)
+ && Objects.equals(staticWorkingDir, that.staticWorkingDir)
+ && Objects.equals(overrideLoginUserName, that.overrideLoginUserName)
+ && Objects.equals(overrideScratchLocation, that.overrideScratchLocation)
+ && Objects.equals(overrideAllocationProjectNumber, that.overrideAllocationProjectNumber)
+ && Objects.equals(mGroupCount, that.mGroupCount);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ resourceHostId,
+ totalCPUCount,
+ nodeCount,
+ numberOfThreads,
+ queueName,
+ wallTimeLimit,
+ totalPhysicalMemory,
+ chessisNumber,
+ staticWorkingDir,
+ overrideLoginUserName,
+ overrideScratchLocation,
+ overrideAllocationProjectNumber,
+ mGroupCount);
+ }
+
+ @Override
+ public String toString() {
+ return "ComputationalResourceScheduling{" + "resourceHostId=" + resourceHostId + ", totalCPUCount="
+ + totalCPUCount + ", nodeCount=" + nodeCount + ", numberOfThreads=" + numberOfThreads + ", queueName="
+ + queueName + ", wallTimeLimit=" + wallTimeLimit + ", totalPhysicalMemory=" + totalPhysicalMemory
+ + ", chessisNumber=" + chessisNumber + ", staticWorkingDir=" + staticWorkingDir
+ + ", overrideLoginUserName=" + overrideLoginUserName + ", overrideScratchLocation="
+ + overrideScratchLocation + ", overrideAllocationProjectNumber=" + overrideAllocationProjectNumber
+ + ", mGroupCount=" + mGroupCount + "}";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ComputeCapability.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ComputeCapability.java
new file mode 100644
index 00000000000..688bf57ca19
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ComputeCapability.java
@@ -0,0 +1,108 @@
+/**
+*
+* 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.compute.resource.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Domain model: ComputeCapability
+ * Describes the job execution capabilities of a {@link Resource}.
+ */
+public class ComputeCapability {
+ /** Job submission protocol (e.g., "SSH", "LOCAL"). */
+ private String protocol;
+ /** Job manager type (e.g., "SLURM", "FORK"). */
+ private String jobManagerType;
+ /** Path to the job manager binaries (e.g., "/usr/bin"). */
+ private String jobManagerBinPath;
+ /** Command overrides keyed by job manager command type. */
+ private Map jobManagerCommands;
+
+ public ComputeCapability() {}
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ /** Raw string getter — preserved for JSON serialization compatibility. */
+ public String getJobManagerType() {
+ return jobManagerType;
+ }
+
+ public void setJobManagerType(String jobManagerType) {
+ this.jobManagerType = jobManagerType;
+ }
+
+ public String getJobManagerBinPath() {
+ return jobManagerBinPath;
+ }
+
+ public void setJobManagerBinPath(String jobManagerBinPath) {
+ this.jobManagerBinPath = jobManagerBinPath;
+ }
+
+ public Map getJobManagerCommands() {
+ return jobManagerCommands;
+ }
+
+ public void setJobManagerCommands(Map jobManagerCommands) {
+ this.jobManagerCommands = jobManagerCommands;
+ }
+
+ /** Typed accessor for the job manager type. */
+ @JsonIgnore
+ public ResourceJobManagerType getJobManagerTypeEnum() {
+ return ResourceJobManagerType.fromString(jobManagerType);
+ }
+
+ /** Derive the coarser ComputeResourceType from the job manager type. */
+ @JsonIgnore
+ public ComputeResourceType getComputeResourceType() {
+ return getJobManagerTypeEnum().toComputeResourceType();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ComputeCapability that = (ComputeCapability) o;
+ return Objects.equals(protocol, that.protocol)
+ && Objects.equals(jobManagerType, that.jobManagerType)
+ && Objects.equals(jobManagerBinPath, that.jobManagerBinPath)
+ && Objects.equals(jobManagerCommands, that.jobManagerCommands);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(protocol, jobManagerType, jobManagerBinPath, jobManagerCommands);
+ }
+
+ @Override
+ public String toString() {
+ return "ComputeCapability{" + "protocol=" + protocol + ", jobManagerType=" + jobManagerType
+ + ", jobManagerBinPath=" + jobManagerBinPath + ", jobManagerCommands=" + jobManagerCommands + "}";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ComputeResourceType.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ComputeResourceType.java
new file mode 100644
index 00000000000..d685ab49932
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ComputeResourceType.java
@@ -0,0 +1,33 @@
+/**
+*
+* 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.compute.resource.model;
+
+public enum ComputeResourceType {
+ SLURM,
+ AWS,
+ PLAIN;
+
+ /**
+ * Map a job manager type string (e.g. "SLURM", "FORK") to a ComputeResourceType.
+ */
+ public static ComputeResourceType fromJobManagerType(String jobManagerType) {
+ return ResourceJobManagerType.fromString(jobManagerType).toComputeResourceType();
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/Job.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/Job.java
new file mode 100644
index 00000000000..ee61f6dc36b
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/Job.java
@@ -0,0 +1,174 @@
+/**
+*
+* 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.compute.resource.model;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Objects;
+import org.apache.airavata.core.model.StatusModel;
+
+/**
+ * Domain model: Job
+ */
+public class Job {
+ private String jobId;
+ private String processId;
+ private String jobDescription;
+ private Instant createdAt;
+ private List> jobStatuses;
+ private String computeResourceConsumed;
+ private String jobName;
+ private String workingDir;
+ private String stdOut;
+ private String stdErr;
+ private int exitCode;
+
+ public Job() {}
+
+ public String getJobId() {
+ return jobId;
+ }
+
+ public void setJobId(String jobId) {
+ this.jobId = jobId;
+ }
+
+ public String getProcessId() {
+ return processId;
+ }
+
+ public void setProcessId(String processId) {
+ this.processId = processId;
+ }
+
+ public String getJobDescription() {
+ return jobDescription;
+ }
+
+ public void setJobDescription(String jobDescription) {
+ this.jobDescription = jobDescription;
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public List> getJobStatuses() {
+ return jobStatuses;
+ }
+
+ public void setJobStatuses(List> jobStatuses) {
+ this.jobStatuses = jobStatuses;
+ }
+
+ public String getComputeResourceConsumed() {
+ return computeResourceConsumed;
+ }
+
+ public void setComputeResourceConsumed(String computeResourceConsumed) {
+ this.computeResourceConsumed = computeResourceConsumed;
+ }
+
+ public String getJobName() {
+ return jobName;
+ }
+
+ public void setJobName(String jobName) {
+ this.jobName = jobName;
+ }
+
+ public String getWorkingDir() {
+ return workingDir;
+ }
+
+ public void setWorkingDir(String workingDir) {
+ this.workingDir = workingDir;
+ }
+
+ public String getStdOut() {
+ return stdOut;
+ }
+
+ public void setStdOut(String stdOut) {
+ this.stdOut = stdOut;
+ }
+
+ public String getStdErr() {
+ return stdErr;
+ }
+
+ public void setStdErr(String stdErr) {
+ this.stdErr = stdErr;
+ }
+
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ public void setExitCode(int exitCode) {
+ this.exitCode = exitCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Job that = (Job) o;
+ return Objects.equals(jobId, that.jobId)
+ && Objects.equals(processId, that.processId)
+ && Objects.equals(jobDescription, that.jobDescription)
+ && Objects.equals(createdAt, that.createdAt)
+ && Objects.equals(jobStatuses, that.jobStatuses)
+ && Objects.equals(computeResourceConsumed, that.computeResourceConsumed)
+ && Objects.equals(jobName, that.jobName)
+ && Objects.equals(workingDir, that.workingDir)
+ && Objects.equals(stdOut, that.stdOut)
+ && Objects.equals(stdErr, that.stdErr)
+ && Objects.equals(exitCode, that.exitCode);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ jobId,
+ processId,
+ jobDescription,
+ createdAt,
+ jobStatuses,
+ computeResourceConsumed,
+ jobName,
+ workingDir,
+ stdOut,
+ stdErr,
+ exitCode);
+ }
+
+ @Override
+ public String toString() {
+ return "Job{" + "jobId=" + jobId + ", processId=" + processId + ", jobDescription="
+ + jobDescription + ", createdAt=" + createdAt + ", jobStatuses=" + jobStatuses
+ + ", computeResourceConsumed=" + computeResourceConsumed + ", jobName=" + jobName + ", workingDir="
+ + workingDir + ", stdOut=" + stdOut + ", stdErr=" + stdErr + ", exitCode=" + exitCode + "}";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/JobManagerCommand.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/JobManagerCommand.java
new file mode 100644
index 00000000000..306a09ee3da
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/JobManagerCommand.java
@@ -0,0 +1,36 @@
+/**
+*
+* 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.compute.resource.model;
+
+/**
+ * Domain enum: JobManagerCommand
+ */
+public enum JobManagerCommand {
+ SUBMISSION,
+ JOB_MONITORING,
+ DELETION,
+ CHECK_JOB,
+ SHOW_QUEUE,
+ SHOW_RESERVATION,
+ SHOW_START,
+ SHOW_CLUSTER_INFO,
+ SHOW_NO_OF_RUNNING_JOBS,
+ SHOW_NO_OF_PENDING_JOBS;
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/JobState.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/JobState.java
new file mode 100644
index 00000000000..7a8f0237750
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/JobState.java
@@ -0,0 +1,35 @@
+/**
+*
+* 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.compute.resource.model;
+
+/**
+ * Domain enum: JobState
+ */
+public enum JobState {
+ SUBMITTED,
+ QUEUED,
+ ACTIVE,
+ COMPLETED,
+ CANCELED,
+ FAILED,
+ SUSPENDED,
+ UNKNOWN,
+ NON_CRITICAL_FAIL;
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/JobSubmissionProtocol.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/JobSubmissionProtocol.java
new file mode 100644
index 00000000000..ea755b58190
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/JobSubmissionProtocol.java
@@ -0,0 +1,29 @@
+/**
+*
+* 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.compute.resource.model;
+
+/**
+ * Domain enum: JobSubmissionProtocol
+ */
+public enum JobSubmissionProtocol {
+ SSH,
+ CLOUD,
+ SSH_FORK;
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceKeys.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceKeys.java
new file mode 100644
index 00000000000..d63d4f1eda9
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceKeys.java
@@ -0,0 +1,388 @@
+/**
+*
+* 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.compute.resource.model;
+
+/**
+ * Defines all preference keys for the unified RESOURCE_PREFERENCE key-value store.
+ *
+ *
This class centralizes all preference key definitions used across all resource types
+ * at GATEWAY, GROUP, and USER levels. Keys are organized by resource type.
+ *
+ *
+ *
+ * @see PreferenceResourceType
+ * @see PreferenceLevel
+ */
+public final class PreferenceKeys {
+
+ private PreferenceKeys() {
+ // Utility class - prevent instantiation
+ }
+
+ // ============================================================================
+ // COMPUTE RESOURCE PREFERENCE KEYS
+ // Resource ID: computeResourceId
+ // ============================================================================
+
+ /** SSH login username for the compute resource */
+ public static final String LOGIN_USERNAME = "loginUsername";
+
+ /** Scratch location/directory on the compute resource */
+ public static final String SCRATCH_LOCATION = "scratchLocation";
+
+ /** Allocation/project number for job accounting */
+ public static final String ALLOCATION_PROJECT_NUMBER = "allocationProjectNumber";
+
+ /** Preferred batch queue name */
+ public static final String PREFERRED_BATCH_QUEUE = "preferredBatchQueue";
+
+ /** Preferred job submission protocol (SSH, LOCAL, etc.) */
+ public static final String PREFERRED_JOB_SUBMISSION_PROTOCOL = "preferredJobSubmissionProtocol";
+
+ /** Preferred data movement protocol (SCP, SFTP, etc.) */
+ public static final String PREFERRED_DATA_MOVEMENT_PROTOCOL = "preferredDataMovementProtocol";
+
+ /** Quality of service setting */
+ public static final String QUALITY_OF_SERVICE = "qualityOfService";
+
+ /** Resource reservation name */
+ public static final String RESERVATION = "reservation";
+
+ /** Reservation start time (as epoch millis) */
+ public static final String RESERVATION_START_TIME = "reservationStartTime";
+
+ /** Reservation end time (as epoch millis) */
+ public static final String RESERVATION_END_TIME = "reservationEndTime";
+
+ /** Token for resource-specific credential in the credential store */
+ public static final String RESOURCE_CREDENTIAL_TOKEN = "resourceSpecificCredentialStoreToken";
+
+ /** Whether Airavata can override user settings */
+ public static final String OVERRIDE_BY_AIRAVATA = "overrideByAiravata";
+
+ /** Whether the user preference has been validated */
+ public static final String VALIDATED = "validated";
+
+ /** Gateway ID for usage reporting */
+ public static final String USAGE_REPORTING_GATEWAY_ID = "usageReportingGatewayId";
+
+ /** SSH account provisioner class name */
+ public static final String SSH_ACCOUNT_PROVISIONER = "sshAccountProvisioner";
+
+ /** Additional info for SSH account provisioner */
+ public static final String SSH_ACCOUNT_PROVISIONER_ADDITIONAL_INFO = "sshAccountProvisionerAdditionalInfo";
+
+ /** Prefix for SSH account provisioner configuration keys */
+ public static final String SSH_PROVISIONER_CONFIG_PREFIX = "ssh.provisioner.config.";
+
+ // ============================================================================
+ // STORAGE RESOURCE PREFERENCE KEYS
+ // Resource ID: storageResourceId
+ // ============================================================================
+
+ /** Root location on the file system */
+ public static final String FILE_SYSTEM_ROOT_LOCATION = "fileSystemRootLocation";
+
+ // Note: LOGIN_USERNAME and RESOURCE_CREDENTIAL_TOKEN are shared with compute preferences
+
+ // ============================================================================
+ // PROFILE METADATA KEYS (RESOURCE_PROFILE merged into RESOURCE_PREFERENCE)
+ // Resource ID: profileId (gatewayId, userId@gatewayId, or groupResourceProfileId)
+ // ============================================================================
+
+ /** Profile credential store token */
+ public static final String PROFILE_CREDENTIAL_STORE_TOKEN = "profile.credentialStoreToken";
+
+ /** Profile identity server password credential token */
+ public static final String PROFILE_IDENTITY_SERVER_PWD_CRED_TOKEN = "profile.identityServerPwdCredToken";
+
+ /** Profile identity server tenant */
+ public static final String PROFILE_IDENTITY_SERVER_TENANT = "profile.identityServerTenant";
+
+ /** Profile creation time (epoch millis) */
+ public static final String PROFILE_CREATION_TIME = "profile.creationTime";
+
+ /** Profile update time (epoch millis) */
+ public static final String PROFILE_UPDATE_TIME = "profile.updateTime";
+
+ /** Profile gateway ID (for group profiles) */
+ public static final String PROFILE_GATEWAY_ID = "profile.gatewayId";
+
+ // ============================================================================
+ // BATCH QUEUE PREFERENCE KEYS
+ // Resource ID: computeResourceId:queueName
+ // ============================================================================
+
+ /**
+ * Inner class for batch queue preference keys.
+ * These control queue policies and limits at different levels.
+ */
+ public static final class BatchQueue {
+ private BatchQueue() {}
+
+ /** Maximum number of nodes allowed (INTEGER) */
+ public static final String MAX_NODES = "maxNodes";
+
+ /** Maximum number of CPUs/cores allowed (INTEGER) */
+ public static final String MAX_CPUS = "maxCpus";
+
+ /** Maximum walltime in minutes (INTEGER) */
+ public static final String MAX_WALLTIME = "maxWalltime";
+
+ /** Maximum number of jobs in queue (INTEGER) */
+ public static final String MAX_JOBS_IN_QUEUE = "maxJobsInQueue";
+
+ /** Maximum memory in MB (INTEGER) */
+ public static final String MAX_MEMORY = "maxMemory";
+
+ /** Default number of nodes (INTEGER) */
+ public static final String DEFAULT_NODES = "defaultNodes";
+
+ /** Default number of CPUs (INTEGER) */
+ public static final String DEFAULT_CPUS = "defaultCpus";
+
+ /** Default walltime in minutes (INTEGER) */
+ public static final String DEFAULT_WALLTIME = "defaultWalltime";
+
+ /** Default memory in MB (INTEGER) */
+ public static final String DEFAULT_MEMORY = "defaultMemory";
+
+ /** Whether the queue is enabled (BOOLEAN) */
+ public static final String QUEUE_ENABLED = "queueEnabled";
+
+ /** JSON array of allowed user IDs (JSON) */
+ public static final String ALLOWED_USERS = "allowedUsers";
+
+ /** JSON array of blocked user IDs (JSON) */
+ public static final String BLOCKED_USERS = "blockedUsers";
+
+ /** JSON array of allowed group IDs (JSON) */
+ public static final String ALLOWED_GROUPS = "allowedGroups";
+
+ /** JSON array of blocked group IDs (JSON) */
+ public static final String BLOCKED_GROUPS = "blockedGroups";
+
+ /** Priority multiplier for scheduling (INTEGER) */
+ public static final String PRIORITY = "priority";
+
+ /** Queue-specific macros for job scripts (STRING) */
+ public static final String QUEUE_MACROS = "queueMacros";
+ }
+
+ // ============================================================================
+ // APPLICATION PREFERENCE KEYS
+ // Resource ID: applicationInterfaceId
+ // ============================================================================
+
+ /**
+ * Inner class for application preference keys.
+ * These control application defaults and behavior.
+ */
+ public static final class Application {
+ private Application() {}
+
+ /** Default compute resource ID for this application (STRING) */
+ public static final String DEFAULT_COMPUTE_RESOURCE = "defaultComputeResource";
+
+ /** Default queue name (STRING) */
+ public static final String DEFAULT_QUEUE = "defaultQueue";
+
+ /** Default walltime in minutes (INTEGER) */
+ public static final String DEFAULT_WALLTIME = "defaultWalltime";
+
+ /** Default number of nodes (INTEGER) */
+ public static final String DEFAULT_NODE_COUNT = "defaultNodeCount";
+
+ /** Default number of CPUs (INTEGER) */
+ public static final String DEFAULT_CPU_COUNT = "defaultCpuCount";
+
+ /** Default memory in MB (INTEGER) */
+ public static final String DEFAULT_MEMORY = "defaultMemory";
+
+ /** Whether the application is enabled (BOOLEAN) */
+ public static final String ENABLED = "enabled";
+
+ /** JSON array of allowed user IDs (JSON) */
+ public static final String ALLOWED_USERS = "allowedUsers";
+
+ /** JSON array of blocked user IDs (JSON) */
+ public static final String BLOCKED_USERS = "blockedUsers";
+
+ /** JSON array of allowed group IDs (JSON) */
+ public static final String ALLOWED_GROUPS = "allowedGroups";
+
+ /** JSON array of required input names (JSON) */
+ public static final String REQUIRED_INPUTS = "requiredInputs";
+
+ /** JSON object of default input values (JSON) */
+ public static final String DEFAULT_INPUT_VALUES = "defaultInputValues";
+
+ /** Maximum concurrent instances per user (INTEGER) */
+ public static final String MAX_CONCURRENT_PER_USER = "maxConcurrentPerUser";
+
+ /** Application-specific credential token (STRING) */
+ public static final String CREDENTIAL_TOKEN = "credentialToken";
+
+ /** Default storage resource for output (STRING) */
+ public static final String DEFAULT_STORAGE_RESOURCE = "defaultStorageResource";
+
+ /** Whether to archive working directory (BOOLEAN) */
+ public static final String ARCHIVE_WORKING_DIR = "archiveWorkingDirectory";
+ }
+
+ // ============================================================================
+ // GATEWAY PREFERENCE KEYS
+ // Resource ID: gatewayId
+ // ============================================================================
+
+ /**
+ * Inner class for gateway configuration keys.
+ * These control gateway-level features and settings.
+ */
+ public static final class Gateway {
+ private Gateway() {}
+
+ /** Whether experiment launching is enabled (BOOLEAN) */
+ public static final String ENABLE_EXPERIMENT_LAUNCH = "enableExperimentLaunch";
+
+ /** Whether data transfer features are enabled (BOOLEAN) */
+ public static final String ENABLE_DATA_TRANSFER = "enableDataTransfer";
+
+ /** Whether workflow features are enabled (BOOLEAN) */
+ public static final String ENABLE_WORKFLOWS = "enableWorkflows";
+
+ /** Maximum concurrent experiments per user (INTEGER) */
+ public static final String MAX_CONCURRENT_EXPERIMENTS = "maxConcurrentExperiments";
+
+ /** Default storage resource ID (STRING) */
+ public static final String DEFAULT_STORAGE_RESOURCE = "defaultStorageResource";
+
+ /** Default compute resource ID (STRING) */
+ public static final String DEFAULT_COMPUTE_RESOURCE = "defaultComputeResource";
+
+ /** UI theme name (STRING) */
+ public static final String UI_THEME = "uiTheme";
+
+ /** Dashboard layout configuration (JSON) */
+ public static final String DASHBOARD_LAYOUT = "dashboardLayout";
+
+ /** Feature flags (JSON object of feature:enabled pairs) */
+ public static final String FEATURE_FLAGS = "featureFlags";
+
+ /** Notification settings (JSON) */
+ public static final String NOTIFICATION_SETTINGS = "notificationSettings";
+
+ /** Whether gateway is in maintenance mode (BOOLEAN) */
+ public static final String MAINTENANCE_MODE = "maintenanceMode";
+
+ /** Maintenance message to display (STRING) */
+ public static final String MAINTENANCE_MESSAGE = "maintenanceMessage";
+
+ /** Gateway announcement/banner message (STRING) */
+ public static final String ANNOUNCEMENT = "announcement";
+
+ /** Whether email notifications are enabled (BOOLEAN) */
+ public static final String ENABLE_EMAIL_NOTIFICATIONS = "enableEmailNotifications";
+
+ /** Default project ID for new experiments (STRING) */
+ public static final String DEFAULT_PROJECT = "defaultProject";
+
+ /** JSON array of enabled application interface IDs (JSON) */
+ public static final String ENABLED_APPLICATIONS = "enabledApplications";
+
+ /** JSON array of enabled compute resource IDs (JSON) */
+ public static final String ENABLED_COMPUTE_RESOURCES = "enabledComputeResources";
+
+ /** JSON array of enabled storage resource IDs (JSON) */
+ public static final String ENABLED_STORAGE_RESOURCES = "enabledStorageResources";
+
+ /** Maximum storage quota per user in bytes (INTEGER) */
+ public static final String MAX_STORAGE_PER_USER = "maxStoragePerUser";
+ }
+
+ // ============================================================================
+ // SYSTEM PREFERENCE KEYS
+ // Resource ID: "GLOBAL" or gatewayId for gateway-specific overrides
+ // ============================================================================
+
+ /**
+ * Inner class for system-wide configuration keys.
+ * These control system-level settings with optional gateway overrides.
+ */
+ public static final class System {
+ private System() {}
+
+ /** Maximum experiments per user across all gateways (INTEGER) */
+ public static final String MAX_EXPERIMENTS_PER_USER = "maxExperimentsPerUser";
+
+ /** Maximum storage per user in bytes (INTEGER) */
+ public static final String MAX_STORAGE_PER_USER = "maxStoragePerUser";
+
+ /** Default credential lifetime in seconds (INTEGER) */
+ public static final String DEFAULT_CREDENTIAL_LIFETIME = "defaultCredentialLifetime";
+
+ /** Session timeout in seconds (INTEGER) */
+ public static final String SESSION_TIMEOUT = "sessionTimeout";
+
+ /** JSON array of enabled authentication providers (JSON) */
+ public static final String ENABLED_AUTH_PROVIDERS = "enabledAuthProviders";
+
+ /** Audit log retention period in days (INTEGER) */
+ public static final String AUDIT_LOG_RETENTION = "auditLogRetention";
+
+ /** Whether new gateway registration is allowed (BOOLEAN) */
+ public static final String ALLOW_GATEWAY_REGISTRATION = "allowGatewayRegistration";
+
+ /** Whether user self-registration is allowed (BOOLEAN) */
+ public static final String ALLOW_USER_REGISTRATION = "allowUserRegistration";
+
+ /** Maximum file upload size in bytes (INTEGER) */
+ public static final String MAX_UPLOAD_SIZE = "maxUploadSize";
+
+ /** Default experiment data retention in days (INTEGER) */
+ public static final String EXPERIMENT_DATA_RETENTION = "experimentDataRetention";
+
+ /** System-wide maintenance mode (BOOLEAN) */
+ public static final String SYSTEM_MAINTENANCE_MODE = "systemMaintenanceMode";
+
+ /** System maintenance message (STRING) */
+ public static final String SYSTEM_MAINTENANCE_MESSAGE = "systemMaintenanceMessage";
+
+ /** Email sender address for notifications (STRING) */
+ public static final String EMAIL_SENDER = "emailSender";
+
+ /** SMTP server host (STRING) */
+ public static final String SMTP_HOST = "smtpHost";
+
+ /** SMTP server port (INTEGER) */
+ public static final String SMTP_PORT = "smtpPort";
+
+ /** Rate limit: max API requests per minute per user (INTEGER) */
+ public static final String API_RATE_LIMIT = "apiRateLimit";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceLevel.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceLevel.java
new file mode 100644
index 00000000000..0a6eb115b90
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceLevel.java
@@ -0,0 +1,130 @@
+/**
+*
+* 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.compute.resource.model;
+
+/**
+ * Enum representing the level at which a preference is set.
+ * Used for hierarchical preference resolution (Zanzibar-like model).
+ *
+ *
Resolution order (lower number = higher authority): SYSTEM > GATEWAY > GROUP
+ *
+ *
When resolving effective preferences for a user:
+ *
+ *
SYSTEM preferences (cross-gateway root authority)
+ *
GATEWAY preferences (gateway-level defaults)
+ *
GROUP preferences (all groups including personal groups - equal at this level)
+ *
+ *
+ *
User-specific preferences are stored at GROUP level under the user's personal group.
+ */
+public enum PreferenceLevel {
+ /**
+ * System-level preferences. Cross-gateway root authority.
+ * Highest priority. Set only by system admins.
+ */
+ SYSTEM(0),
+
+ /**
+ * Gateway-level preferences. Default settings for all users in a gateway.
+ */
+ GATEWAY(1),
+
+ /**
+ * Group-level preferences. All groups (personal, admin, custom) are equal at this level.
+ * When multiple groups have the same preference key, user must explicitly select.
+ */
+ GROUP(2),
+
+ /**
+ * User-level preferences (DB compat only). Do not use for new writes.
+ *
+ *
Existing database rows with {@code preference_level = 'USER'} are still deserialized
+ * to this enum constant by JPA. New code must use {@link #GROUP} with ownerId set to the
+ * user's personal group ID. A pending migration will update existing rows from USER to GROUP,
+ * after which this constant can be removed.
+ */
+ USER(2),
+
+ /**
+ * Project-level access. Used for RESOURCE_ACCESS when credential access is scoped to a project.
+ * ownerId = projectId. Users with project access can use the project's credentials for the resource.
+ */
+ PROJECT(3);
+
+ private final int priority;
+
+ PreferenceLevel(int priority) {
+ this.priority = priority;
+ }
+
+ /**
+ * Get the priority value. Lower values indicate higher authority.
+ *
+ * @return the priority value
+ */
+ public int getPriority() {
+ return priority;
+ }
+
+ /**
+ * Check if this level has higher authority than another level.
+ *
+ * @param other the other level to compare against
+ * @return true if this level should override the other level
+ */
+ public boolean overrides(PreferenceLevel other) {
+ return this.priority < other.priority;
+ }
+
+ /**
+ * Find a PreferenceLevel by its priority value.
+ *
+ * @param priority the priority value
+ * @return the corresponding PreferenceLevel, or null if not found
+ */
+ public static PreferenceLevel findByPriority(int priority) {
+ switch (priority) {
+ case 0:
+ return SYSTEM;
+ case 1:
+ return GATEWAY;
+ case 2:
+ return GROUP;
+ case 3:
+ return PROJECT;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Whether this level represents a group-level preference (GROUP or deprecated USER).
+ */
+ public boolean isGroupLevel() {
+ return this == GROUP || this == USER;
+ }
+
+ /**
+ * Whether this level represents project-scoped access.
+ */
+ public boolean isProjectLevel() {
+ return this == PROJECT;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceResourceType.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceResourceType.java
new file mode 100644
index 00000000000..06e158879a4
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceResourceType.java
@@ -0,0 +1,151 @@
+/**
+*
+* 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.compute.resource.model;
+
+/**
+ * Enum representing the type of resource a preference applies to.
+ *
+ *
This enum supports hierarchical preference resolution where preferences can be set
+ * at GATEWAY, GROUP, or USER levels with USER > GROUP > GATEWAY precedence.
+ *
+ *
SYSTEM: "GLOBAL" for system-wide, or gatewayId for gateway overrides
+ *
+ */
+public enum PreferenceResourceType {
+ /**
+ * Preferences for compute resources (HPC clusters, cloud instances, etc.)
+ * Resource ID: computeResourceId
+ */
+ COMPUTE(0),
+
+ /**
+ * Preferences for storage resources (file systems, object stores, etc.)
+ * Resource ID: storageResourceId
+ */
+ STORAGE(1),
+
+ /**
+ * Preferences for profile-level metadata (e.g., profile name).
+ * Resource ID: profileId
+ */
+ PROFILE(2),
+
+ /**
+ * Preferences for batch queue policies and limits.
+ * Resource ID: computeResourceId:queueName (colon-separated)
+ */
+ BATCH_QUEUE(3),
+
+ /**
+ * Preferences for application defaults and settings.
+ * Resource ID: applicationInterfaceId
+ */
+ APPLICATION(4),
+
+ /**
+ * Preferences for gateway-level configuration.
+ * Resource ID: gatewayId
+ */
+ GATEWAY(5),
+
+ /**
+ * Preferences for system-wide settings with optional gateway overrides.
+ * Resource ID: "GLOBAL" for system-wide, or gatewayId for gateway-specific overrides
+ */
+ SYSTEM(6),
+
+ /**
+ * User explicit group selection for conflict resolution (was USER_GROUP_SELECTION table).
+ * Resource ID: resourceType + ":" + resourceId (e.g. "COMPUTE:cluster1"); key = selectionKey; value = selectedGroupId.
+ */
+ USER_GROUP_SELECTION(7);
+
+ /** Special resource ID for system-wide preferences */
+ public static final String GLOBAL_RESOURCE_ID = "GLOBAL";
+
+ /** Separator for composite resource IDs (e.g., BATCH_QUEUE) */
+ public static final String RESOURCE_ID_SEPARATOR = ":";
+
+ private final int value;
+
+ PreferenceResourceType(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public static PreferenceResourceType findByValue(int value) {
+ switch (value) {
+ case 0:
+ return COMPUTE;
+ case 1:
+ return STORAGE;
+ case 2:
+ return PROFILE;
+ case 3:
+ return BATCH_QUEUE;
+ case 4:
+ return APPLICATION;
+ case 5:
+ return GATEWAY;
+ case 6:
+ return SYSTEM;
+ case 7:
+ return USER_GROUP_SELECTION;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Create a batch queue resource ID from compute resource and queue name.
+ *
+ * @param computeResourceId the compute resource ID
+ * @param queueName the queue name
+ * @return the combined resource ID
+ */
+ public static String batchQueueResourceId(String computeResourceId, String queueName) {
+ return computeResourceId + RESOURCE_ID_SEPARATOR + queueName;
+ }
+
+ /**
+ * Parse a batch queue resource ID into its components.
+ *
+ * @param resourceId the combined resource ID
+ * @return array of [computeResourceId, queueName], or null if invalid format
+ */
+ public static String[] parseBatchQueueResourceId(String resourceId) {
+ if (resourceId == null || !resourceId.contains(RESOURCE_ID_SEPARATOR)) {
+ return null;
+ }
+ int idx = resourceId.indexOf(RESOURCE_ID_SEPARATOR);
+ return new String[] {resourceId.substring(0, idx), resourceId.substring(idx + 1)};
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceValueType.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceValueType.java
new file mode 100644
index 00000000000..002b1342b71
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/PreferenceValueType.java
@@ -0,0 +1,79 @@
+/**
+*
+* 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.compute.resource.model;
+
+/**
+ * Enum representing the data type of a preference value.
+ * Used to properly serialize/deserialize preference values stored as strings.
+ */
+public enum PreferenceValueType {
+ /**
+ * Plain string value. No conversion needed.
+ */
+ STRING(0),
+
+ /**
+ * Integer value. Stored as string, parsed to Integer/Long when read.
+ */
+ INTEGER(1),
+
+ /**
+ * Boolean value. Stored as "true" or "false" string.
+ */
+ BOOLEAN(2),
+
+ /**
+ * JSON value. Complex objects serialized as JSON strings.
+ * Used for lists, maps, or nested objects.
+ */
+ JSON(3),
+
+ /**
+ * Timestamp value. Stored as ISO-8601 string or epoch millis.
+ */
+ TIMESTAMP(4);
+
+ private final int value;
+
+ PreferenceValueType(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public static PreferenceValueType findByValue(int value) {
+ switch (value) {
+ case 0:
+ return STRING;
+ case 1:
+ return INTEGER;
+ case 2:
+ return BOOLEAN;
+ case 3:
+ return JSON;
+ case 4:
+ return TIMESTAMP;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/Resource.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/Resource.java
new file mode 100644
index 00000000000..422b1048c15
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/Resource.java
@@ -0,0 +1,164 @@
+/**
+*
+* 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.compute.resource.model;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * Domain model: Resource
+ * Represents a computational or storage resource (e.g., an HPC cluster, cloud VM, or file server)
+ * registered within a gateway. A resource may expose compute capabilities, storage capabilities,
+ * or both, as described by its {@link ResourceCapabilities}.
+ */
+public class Resource {
+ private String resourceId;
+ private String gatewayId;
+ private String name;
+ private String hostName;
+ private int port = 22;
+ private String description;
+ private ResourceType resourceType = ResourceType.COMPUTE;
+ private ResourceCapabilities capabilities;
+ private Instant createdAt;
+ private Instant updatedAt;
+
+ public Resource() {}
+
+ public String getResourceId() {
+ return resourceId;
+ }
+
+ public void setResourceId(String resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getHostName() {
+ return hostName;
+ }
+
+ public void setHostName(String hostName) {
+ this.hostName = hostName;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public ResourceType getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(ResourceType resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public ResourceCapabilities getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(ResourceCapabilities capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public Instant getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(Instant updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Resource that = (Resource) o;
+ return port == that.port
+ && Objects.equals(resourceId, that.resourceId)
+ && Objects.equals(gatewayId, that.gatewayId)
+ && Objects.equals(name, that.name)
+ && Objects.equals(hostName, that.hostName)
+ && Objects.equals(description, that.description)
+ && Objects.equals(resourceType, that.resourceType)
+ && Objects.equals(capabilities, that.capabilities)
+ && Objects.equals(createdAt, that.createdAt)
+ && Objects.equals(updatedAt, that.updatedAt);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ resourceId,
+ gatewayId,
+ name,
+ hostName,
+ port,
+ description,
+ resourceType,
+ capabilities,
+ createdAt,
+ updatedAt);
+ }
+
+ @Override
+ public String toString() {
+ return "Resource{" + "resourceId=" + resourceId + ", gatewayId=" + gatewayId + ", name=" + name
+ + ", hostName=" + hostName + ", port=" + port + ", description=" + description
+ + ", resourceType=" + resourceType + ", capabilities=" + capabilities
+ + ", createdAt=" + createdAt + ", updatedAt=" + updatedAt + "}";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceBinding.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceBinding.java
new file mode 100644
index 00000000000..1ab8bf43ed7
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceBinding.java
@@ -0,0 +1,153 @@
+/**
+*
+* 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.compute.resource.model;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Domain model: ResourceBinding
+ * Associates a {@link Credential} with a {@link Resource}, specifying the login username
+ * to use when authenticating to that resource. The {@code metadata} map can hold
+ * resource-specific configuration (e.g., proxy settings, jump-host details) as free-form
+ * JSON-compatible key-value pairs.
+ */
+public class ResourceBinding {
+ private String bindingId;
+ private String credentialId;
+ private String resourceId;
+ /** The OS-level username used to log in to the remote resource. */
+ private String loginUsername;
+ /**
+ * Free-form JSON-compatible metadata for resource-specific configuration.
+ * Values may be strings, numbers, booleans, lists, or nested maps.
+ */
+ private Map metadata;
+
+ private boolean enabled;
+ private String gatewayId;
+ private Instant createdAt;
+ private Instant updatedAt;
+
+ public ResourceBinding() {}
+
+ public String getBindingId() {
+ return bindingId;
+ }
+
+ public void setBindingId(String bindingId) {
+ this.bindingId = bindingId;
+ }
+
+ public String getCredentialId() {
+ return credentialId;
+ }
+
+ public void setCredentialId(String credentialId) {
+ this.credentialId = credentialId;
+ }
+
+ public String getResourceId() {
+ return resourceId;
+ }
+
+ public void setResourceId(String resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public String getLoginUsername() {
+ return loginUsername;
+ }
+
+ public void setLoginUsername(String loginUsername) {
+ this.loginUsername = loginUsername;
+ }
+
+ public Map getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(Map metadata) {
+ this.metadata = metadata;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public Instant getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void setUpdatedAt(Instant updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ResourceBinding that = (ResourceBinding) o;
+ return enabled == that.enabled
+ && Objects.equals(bindingId, that.bindingId)
+ && Objects.equals(credentialId, that.credentialId)
+ && Objects.equals(resourceId, that.resourceId)
+ && Objects.equals(loginUsername, that.loginUsername)
+ && Objects.equals(metadata, that.metadata)
+ && Objects.equals(gatewayId, that.gatewayId)
+ && Objects.equals(createdAt, that.createdAt)
+ && Objects.equals(updatedAt, that.updatedAt);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ bindingId, credentialId, resourceId, loginUsername, metadata, enabled, gatewayId, createdAt, updatedAt);
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceBinding{" + "bindingId=" + bindingId + ", credentialId=" + credentialId
+ + ", resourceId=" + resourceId + ", loginUsername=" + loginUsername + ", metadata=" + metadata
+ + ", enabled=" + enabled + ", gatewayId=" + gatewayId + ", createdAt=" + createdAt
+ + ", updatedAt=" + updatedAt + "}";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceCapabilities.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceCapabilities.java
new file mode 100644
index 00000000000..471f71111b6
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceCapabilities.java
@@ -0,0 +1,71 @@
+/**
+*
+* 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.compute.resource.model;
+
+import java.util.Objects;
+import org.apache.airavata.storage.resource.model.StorageCapability;
+
+/**
+ * Domain model: ResourceCapabilities
+ * Describes what a {@link Resource} is capable of. Both fields are nullable: a resource
+ * may expose only compute, only storage, or both.
+ */
+public class ResourceCapabilities {
+ /** Compute capability - present when the resource can execute jobs. May be null. */
+ private ComputeCapability compute;
+ /** Storage capability - present when the resource exposes a file system. May be null. */
+ private StorageCapability storage;
+
+ public ResourceCapabilities() {}
+
+ public ComputeCapability getCompute() {
+ return compute;
+ }
+
+ public void setCompute(ComputeCapability compute) {
+ this.compute = compute;
+ }
+
+ public StorageCapability getStorage() {
+ return storage;
+ }
+
+ public void setStorage(StorageCapability storage) {
+ this.storage = storage;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ResourceCapabilities that = (ResourceCapabilities) o;
+ return Objects.equals(compute, that.compute) && Objects.equals(storage, that.storage);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(compute, storage);
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceCapabilities{" + "compute=" + compute + ", storage=" + storage + "}";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceJobManagerType.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceJobManagerType.java
new file mode 100644
index 00000000000..90f7f91aea3
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceJobManagerType.java
@@ -0,0 +1,53 @@
+/**
+*
+* 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.compute.resource.model;
+
+public enum ResourceJobManagerType {
+ FORK,
+ SLURM,
+ CLOUD;
+
+ /**
+ * Parse a string (case-insensitive) to a ResourceJobManagerType.
+ * Returns FORK for null or unrecognised values.
+ */
+ public static ResourceJobManagerType fromString(String value) {
+ if (value == null) {
+ return FORK;
+ }
+ return switch (value.toUpperCase()) {
+ case "SLURM" -> SLURM;
+ case "CLOUD" -> CLOUD;
+ default -> FORK;
+ };
+ }
+
+ /**
+ * Map this job manager type to the coarser ComputeResourceType used by
+ * task factories and workflow dispatch.
+ */
+ public ComputeResourceType toComputeResourceType() {
+ return switch (this) {
+ case SLURM -> ComputeResourceType.SLURM;
+ case CLOUD -> ComputeResourceType.AWS;
+ case FORK -> ComputeResourceType.PLAIN;
+ };
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceType.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceType.java
new file mode 100644
index 00000000000..f7d585ec35e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/ResourceType.java
@@ -0,0 +1,28 @@
+/**
+*
+* 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.compute.resource.model;
+
+/**
+ * Classifies a resource as either a compute host or a storage endpoint.
+ */
+public enum ResourceType {
+ COMPUTE,
+ STORAGE
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/SecurityProtocol.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/SecurityProtocol.java
new file mode 100644
index 00000000000..7257ac0e16b
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/model/SecurityProtocol.java
@@ -0,0 +1,32 @@
+/**
+*
+* 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.compute.resource.model;
+
+/**
+ * Domain enum: SecurityProtocol
+ */
+public enum SecurityProtocol {
+ USERNAME_PASSWORD,
+ SSH_KEYS,
+ GSI,
+ KERBEROS,
+ OAUTH,
+ LOCAL;
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/JobRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/JobRepository.java
new file mode 100644
index 00000000000..a39f060ec8e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/JobRepository.java
@@ -0,0 +1,44 @@
+/**
+*
+* 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.compute.resource.repository;
+
+import java.util.List;
+import org.apache.airavata.compute.resource.entity.JobEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Spring Data JPA repository for {@link JobEntity}.
+ *
+ *
Provides query methods for batch job records submitted through Airavata processes.
+ * A process may spawn one or more jobs; each job tracks its own lifecycle state,
+ * standard output/error, and exit code.
+ */
+@Repository
+public interface JobRepository extends JpaRepository {
+
+ /**
+ * Find all jobs submitted under a specific process.
+ *
+ * @param processId the process identifier
+ * @return list of jobs for the process, empty list if none found
+ */
+ List findByProcessId(String processId);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/ResourceBindingRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/ResourceBindingRepository.java
new file mode 100644
index 00000000000..354f005e70f
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/ResourceBindingRepository.java
@@ -0,0 +1,70 @@
+/**
+*
+* 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.compute.resource.repository;
+
+import java.util.List;
+import org.apache.airavata.compute.resource.entity.ResourceBindingEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Spring Data JPA repository for {@link ResourceBindingEntity}.
+ *
+ *
Provides query methods for bindings that associate a credential with a compute
+ * or storage resource within a gateway, including the login username used for authentication.
+ * All finder methods use Spring Data JPA method naming conventions for automatic
+ * query derivation.
+ */
+@Repository
+public interface ResourceBindingRepository extends JpaRepository {
+
+ /**
+ * Find all resource bindings belonging to a specific gateway.
+ *
+ * @param gatewayId the gateway identifier
+ * @return list of bindings for the gateway, empty list if none found
+ */
+ List findByGatewayId(String gatewayId);
+
+ /**
+ * Find all bindings associated with a specific resource.
+ *
+ * @param resourceId the resource identifier
+ * @return list of bindings for the resource, empty list if none found
+ */
+ List findByResourceId(String resourceId);
+
+ /**
+ * Find all bindings associated with a specific credential.
+ *
+ * @param credentialId the credential identifier
+ * @return list of bindings for the credential, empty list if none found
+ */
+ List findByCredentialId(String credentialId);
+
+ /**
+ * Find all bindings for a specific resource within a gateway.
+ *
+ * @param gatewayId the gateway identifier
+ * @param resourceId the resource identifier
+ * @return list of bindings matching the gateway and resource, empty list if none found
+ */
+ List findByGatewayIdAndResourceId(String gatewayId, String resourceId);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/ResourcePreferenceRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/ResourcePreferenceRepository.java
new file mode 100644
index 00000000000..de93a14a723
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/ResourcePreferenceRepository.java
@@ -0,0 +1,66 @@
+/**
+*
+* 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.compute.resource.repository;
+
+import java.util.List;
+import org.apache.airavata.compute.resource.entity.ResourcePreferenceEntity;
+import org.apache.airavata.compute.resource.model.PreferenceLevel;
+import org.apache.airavata.compute.resource.model.PreferenceResourceType;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Spring Data JPA repository for {@link ResourcePreferenceEntity}.
+ *
+ *
Supports multi-level preference lookup (GATEWAY, GROUP, USER) across different resource types.
+ */
+@Repository
+public interface ResourcePreferenceRepository extends JpaRepository {
+
+ /**
+ * Find a single preference by its full unique key tuple.
+ */
+ ResourcePreferenceEntity findByResourceTypeAndResourceIdAndOwnerIdAndLevelAndKey(
+ PreferenceResourceType resourceType, String resourceId, String ownerId, PreferenceLevel level, String key);
+
+ /**
+ * Find all preferences for a given resource and owner at a specific level.
+ */
+ List findByResourceTypeAndResourceIdAndOwnerIdAndLevel(
+ PreferenceResourceType resourceType, String resourceId, String ownerId, PreferenceLevel level);
+
+ /**
+ * Find all preferences for a given resource type and resource ID across all owners and levels.
+ */
+ List findByResourceTypeAndResourceId(
+ PreferenceResourceType resourceType, String resourceId);
+
+ /**
+ * Delete a preference by its full unique key tuple.
+ */
+ void deleteByResourceTypeAndResourceIdAndOwnerIdAndLevelAndKey(
+ PreferenceResourceType resourceType, String resourceId, String ownerId, PreferenceLevel level, String key);
+
+ /**
+ * Delete all preferences for a resource at a specific owner/level.
+ */
+ void deleteByResourceTypeAndResourceIdAndOwnerIdAndLevel(
+ PreferenceResourceType resourceType, String resourceId, String ownerId, PreferenceLevel level);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/ResourceRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/ResourceRepository.java
new file mode 100644
index 00000000000..68580da28a2
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/repository/ResourceRepository.java
@@ -0,0 +1,44 @@
+/**
+*
+* 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.compute.resource.repository;
+
+import java.util.List;
+import org.apache.airavata.compute.resource.entity.ResourceEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Spring Data JPA repository for {@link ResourceEntity}.
+ *
+ *
Provides query methods for compute and storage resources scoped to a gateway.
+ * All finder methods use Spring Data JPA method naming conventions for automatic
+ * query derivation; no explicit JPQL is required.
+ */
+@Repository
+public interface ResourceRepository extends JpaRepository {
+
+ /**
+ * Find all resources belonging to a specific gateway.
+ *
+ * @param gatewayId the gateway identifier
+ * @return list of resources for the gateway, empty list if none found
+ */
+ List findByGatewayId(String gatewayId);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/DefaultJobService.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/DefaultJobService.java
new file mode 100644
index 00000000000..12d78ac537a
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/DefaultJobService.java
@@ -0,0 +1,117 @@
+/**
+*
+* 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.compute.resource.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.airavata.compute.resource.entity.JobEntity;
+import org.apache.airavata.compute.resource.mapper.JobMapper;
+import org.apache.airavata.compute.resource.model.Job;
+import org.apache.airavata.compute.resource.repository.JobRepository;
+import org.apache.airavata.core.exception.RegistryExceptions.RegistryException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Service for CRUD operations on job records.
+ *
+ *
A job represents a single batch or fork submission on a compute resource,
+ * owned by a process. Callers query by jobId or processId.
+ */
+@Service
+@Transactional
+public class DefaultJobService implements JobService {
+
+ private static final Logger logger = LoggerFactory.getLogger(DefaultJobService.class);
+
+ private final JobRepository jobRepository;
+ private final JobMapper mapper;
+
+ public DefaultJobService(JobRepository jobRepository, JobMapper mapper) {
+ this.jobRepository = jobRepository;
+ this.mapper = mapper;
+ }
+
+ /**
+ * Query jobs by a dynamic field name. Supports "jobId" and "processId".
+ *
+ * @param fieldName the field to filter by
+ * @param fieldValue the expected value
+ * @return list of matching job models
+ * @throws RegistryException on retrieval failure
+ */
+ @Transactional(readOnly = true)
+ public List getJobs(String fieldName, String fieldValue) throws RegistryException {
+ try {
+ if (fieldValue == null || fieldValue.isBlank()) {
+ return new ArrayList<>();
+ }
+ List entities;
+ switch (fieldName) {
+ case "jobId":
+ entities = jobRepository.findById(fieldValue).map(List::of).orElse(List.of());
+ break;
+ case "processId":
+ entities = jobRepository.findByProcessId(fieldValue);
+ break;
+ default:
+ logger.warn("Unsupported job query field: {}", fieldName);
+ return new ArrayList<>();
+ }
+ return mapper.toModelList(entities);
+ } catch (Exception e) {
+ throw new RegistryException("Failed to query jobs by " + fieldName + "=" + fieldValue, e);
+ }
+ }
+
+ /**
+ * Save or update a job record.
+ *
+ * @param jobModel the job to persist
+ * @throws RegistryException on persistence failure
+ */
+ public void saveJob(Job jobModel) throws RegistryException {
+ try {
+ var entity = mapper.toEntity(jobModel);
+ jobRepository.save(entity);
+ logger.debug("Saved job {}", jobModel.getJobId());
+ } catch (Exception e) {
+ throw new RegistryException("Failed to save job " + jobModel.getJobId(), e);
+ }
+ }
+
+ /**
+ * Delete all jobs for a given process.
+ *
+ * @param processId the process identifier
+ * @throws RegistryException on deletion failure
+ */
+ public void deleteJobsByProcessId(String processId) throws RegistryException {
+ try {
+ List jobs = jobRepository.findByProcessId(processId);
+ jobRepository.deleteAll(jobs);
+ logger.debug("Deleted {} jobs for process {}", jobs.size(), processId);
+ } catch (Exception e) {
+ throw new RegistryException("Failed to delete jobs for process " + processId, e);
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/DefaultResourceService.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/DefaultResourceService.java
new file mode 100644
index 00000000000..7553248b45d
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/DefaultResourceService.java
@@ -0,0 +1,152 @@
+/**
+*
+* 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.compute.resource.service;
+
+import java.util.List;
+import org.apache.airavata.accounting.service.AllocationProjectService;
+import org.apache.airavata.compute.resource.mapper.ResourceBindingMapper;
+import org.apache.airavata.compute.resource.mapper.ResourceMapper;
+import org.apache.airavata.compute.resource.model.Resource;
+import org.apache.airavata.compute.resource.model.ResourceBinding;
+import org.apache.airavata.compute.resource.repository.ResourceBindingRepository;
+import org.apache.airavata.compute.resource.repository.ResourceRepository;
+import org.apache.airavata.core.util.IdGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@Transactional
+public class DefaultResourceService implements ResourceService {
+
+ private static final Logger logger = LoggerFactory.getLogger(DefaultResourceService.class);
+
+ private final ResourceRepository resourceRepository;
+ private final ResourceMapper mapper;
+ private final ResourceBindingRepository bindingRepository;
+ private final ResourceBindingMapper bindingMapper;
+ private final AllocationProjectService allocationProjectService;
+
+ public DefaultResourceService(
+ ResourceRepository resourceRepository,
+ ResourceMapper mapper,
+ ResourceBindingRepository bindingRepository,
+ ResourceBindingMapper bindingMapper,
+ AllocationProjectService allocationProjectService) {
+ this.resourceRepository = resourceRepository;
+ this.mapper = mapper;
+ this.bindingRepository = bindingRepository;
+ this.bindingMapper = bindingMapper;
+ this.allocationProjectService = allocationProjectService;
+ }
+
+ public Resource getResource(String resourceId) {
+ return resourceRepository.findById(resourceId).map(mapper::toModel).orElse(null);
+ }
+
+ public List getResources(String gatewayId) {
+ return mapper.toModelList(resourceRepository.findByGatewayId(gatewayId));
+ }
+
+ public String createResource(Resource resource) {
+ resource.setResourceId(IdGenerator.ensureId(resource.getResourceId()));
+ var entity = mapper.toEntity(resource);
+ var saved = resourceRepository.save(entity);
+ logger.debug("Created resource with id={}", saved.getResourceId());
+ return saved.getResourceId();
+ }
+
+ public void updateResource(String resourceId, Resource resource) {
+ if (!resourceRepository.existsById(resourceId)) {
+ throw new IllegalArgumentException("Resource not found: " + resourceId);
+ }
+ resource.setResourceId(resourceId);
+ resourceRepository.save(mapper.toEntity(resource));
+ logger.debug("Updated resource with id={}", resourceId);
+ }
+
+ public void deleteResource(String resourceId) {
+ if (!resourceRepository.existsById(resourceId)) {
+ throw new IllegalArgumentException("Resource not found: " + resourceId);
+ }
+ resourceRepository.deleteById(resourceId);
+ logger.debug("Deleted resource with id={}", resourceId);
+ }
+
+ // ========== Binding Operations ==========
+
+ public ResourceBinding getBinding(String bindingId) {
+ return bindingRepository.findById(bindingId).map(bindingMapper::toModel).orElse(null);
+ }
+
+ public List getBindings(String gatewayId) {
+ return bindingMapper.toModelList(bindingRepository.findByGatewayId(gatewayId));
+ }
+
+ public List getBindingsByResource(String resourceId) {
+ return bindingMapper.toModelList(bindingRepository.findByResourceId(resourceId));
+ }
+
+ public List getBindingsByCredential(String credentialId) {
+ return bindingMapper.toModelList(bindingRepository.findByCredentialId(credentialId));
+ }
+
+ public String createBinding(ResourceBinding binding) {
+ binding.setBindingId(IdGenerator.ensureId(binding.getBindingId()));
+ var saved = bindingRepository.save(bindingMapper.toEntity(binding));
+ logger.debug("Created resource binding id={}", saved.getBindingId());
+
+ binding.setBindingId(saved.getBindingId());
+ allocationProjectService.syncFromBinding(
+ binding.getBindingId(),
+ binding.getResourceId(),
+ binding.getGatewayId(),
+ binding.getCredentialId(),
+ binding.getMetadata());
+
+ return saved.getBindingId();
+ }
+
+ public void updateBinding(String bindingId, ResourceBinding binding) {
+ if (!bindingRepository.existsById(bindingId)) {
+ throw new IllegalArgumentException("Resource binding not found with id: " + bindingId);
+ }
+ binding.setBindingId(bindingId);
+ bindingRepository.save(bindingMapper.toEntity(binding));
+ logger.debug("Updated resource binding id={}", bindingId);
+
+ allocationProjectService.syncFromBinding(
+ binding.getBindingId(),
+ binding.getResourceId(),
+ binding.getGatewayId(),
+ binding.getCredentialId(),
+ binding.getMetadata());
+ }
+
+ public void deleteBinding(String bindingId) {
+ if (!bindingRepository.existsById(bindingId)) {
+ throw new IllegalArgumentException("Resource binding not found with id: " + bindingId);
+ }
+ allocationProjectService.cleanupForBinding(bindingId);
+ bindingRepository.deleteById(bindingId);
+ logger.debug("Deleted resource binding id={}", bindingId);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/JobService.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/JobService.java
new file mode 100644
index 00000000000..32192c1eddc
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/JobService.java
@@ -0,0 +1,33 @@
+/**
+*
+* 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.compute.resource.service;
+
+import java.util.List;
+import org.apache.airavata.compute.resource.model.Job;
+import org.apache.airavata.core.exception.RegistryExceptions.RegistryException;
+
+public interface JobService {
+
+ List getJobs(String fieldName, String fieldValue) throws RegistryException;
+
+ void saveJob(Job jobModel) throws RegistryException;
+
+ void deleteJobsByProcessId(String processId) throws RegistryException;
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/ResourceService.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/ResourceService.java
new file mode 100644
index 00000000000..c4a28197940
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/service/ResourceService.java
@@ -0,0 +1,53 @@
+/**
+*
+* 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.compute.resource.service;
+
+import java.util.List;
+import org.apache.airavata.compute.resource.model.Resource;
+import org.apache.airavata.compute.resource.model.ResourceBinding;
+
+public interface ResourceService {
+
+ Resource getResource(String resourceId);
+
+ List getResources(String gatewayId);
+
+ String createResource(Resource resource);
+
+ void updateResource(String resourceId, Resource resource);
+
+ void deleteResource(String resourceId);
+
+ // ========== Binding Operations ==========
+
+ ResourceBinding getBinding(String bindingId);
+
+ List getBindings(String gatewayId);
+
+ List getBindingsByResource(String resourceId);
+
+ List getBindingsByCredential(String credentialId);
+
+ String createBinding(ResourceBinding binding);
+
+ void updateBinding(String bindingId, ResourceBinding binding);
+
+ void deleteBinding(String bindingId);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/CustomCommandOutputParser.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/CustomCommandOutputParser.java
new file mode 100644
index 00000000000..abcb691a026
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/CustomCommandOutputParser.java
@@ -0,0 +1,263 @@
+/**
+*
+* 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.compute.resource.submission;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.airavata.compute.resource.model.JobState;
+import org.apache.airavata.core.model.StatusModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * OutputParser implementation for Airavata custom command output parsing.
+ *
+ *
This parser handles output from direct bash script execution on cloud VMs.
+ * The jobID is typically a process ID (PID), and status is determined from `ps` command output.
+ *
+ *
Used by CloudJobManagerSpec for cloud VM job execution.
+ */
+public class CustomCommandOutputParser implements JobOutputParser {
+ private static final Logger logger = LoggerFactory.getLogger(CustomCommandOutputParser.class);
+
+ /**
+ * Parse job ID (PID) from bash script execution output.
+ * The output may contain the PID directly, or we may need to extract it from process info.
+ *
+ * @param rawOutput Output from bash script execution
+ * @return Job ID (PID) as string, or empty string if not found
+ */
+ @Override
+ public String parseJobSubmission(String rawOutput) throws Exception {
+ logger.debug("Parsing job submission output: {}", rawOutput);
+ if (rawOutput == null || rawOutput.trim().isEmpty()) {
+ return "";
+ }
+
+ // Try to extract PID from output - common patterns:
+ // 1. Just the PID number
+ // 2. "PID: "
+ // 3. Process started message with PID
+ Pattern pidPattern = Pattern.compile("(?:PID|pid|process)[\\s:]+(\\d+)", Pattern.CASE_INSENSITIVE);
+ Matcher matcher = pidPattern.matcher(rawOutput);
+ if (matcher.find()) {
+ String pid = matcher.group(1);
+ logger.info("Extracted PID from submission output: {}", pid);
+ return pid;
+ }
+
+ // Try to match just a number (likely the PID)
+ Pattern numberPattern = Pattern.compile("^\\s*(\\d+)\\s*$");
+ matcher = numberPattern.matcher(rawOutput.trim());
+ if (matcher.matches()) {
+ String pid = matcher.group(1);
+ logger.info("Extracted PID (number only): {}", pid);
+ return pid;
+ }
+
+ logger.warn("Could not extract PID from submission output: {}", rawOutput);
+ return "";
+ }
+
+ /**
+ * Check if job submission failed by looking for error indicators.
+ *
+ * @param rawOutput Output from bash script execution
+ * @return true if submission failed, false otherwise
+ */
+ @Override
+ public boolean isJobSubmissionFailed(String rawOutput) {
+ if (rawOutput == null) {
+ return false;
+ }
+
+ // Check for common error indicators
+ String lowerOutput = rawOutput.toLowerCase();
+ return lowerOutput.contains("error")
+ || lowerOutput.contains("failed")
+ || lowerOutput.contains("cannot")
+ || lowerOutput.contains("permission denied")
+ || lowerOutput.contains("no such file")
+ || lowerOutput.contains("command not found");
+ }
+
+ /**
+ * Parse job status from `ps -p -o stat=` output.
+ * The output is a single character representing process state:
+ * R=running, S=sleeping, T=stopped, Z=zombie, D=uninterruptible sleep, etc.
+ *
+ * @param jobID Process ID (PID)
+ * @param rawOutput Output from `ps -p -o stat=` command
+ * @return StatusModel with parsed state, or UNKNOWN if process not found
+ */
+ @Override
+ public StatusModel parseJobStatus(String jobID, String rawOutput) throws Exception {
+ logger.debug("Parsing job status for PID {}: {}", jobID, rawOutput);
+ StatusModel jobStatus = new StatusModel<>();
+
+ if (rawOutput == null || rawOutput.trim().isEmpty()) {
+ // Empty output means process doesn't exist (completed or never started)
+ jobStatus.setState(JobState.UNKNOWN);
+ return jobStatus;
+ }
+
+ // ps -o stat= returns just the status character(s)
+ // Common states: R (running), S (sleeping), T (stopped), Z (zombie), D (uninterruptible)
+ String status = rawOutput.trim();
+ if (status.isEmpty()) {
+ jobStatus.setState(JobState.UNKNOWN);
+ return jobStatus;
+ }
+
+ // Map ps status to JobState
+ char firstChar = status.charAt(0);
+ switch (firstChar) {
+ case 'R': // Running
+ jobStatus.setState(JobState.ACTIVE);
+ break;
+ case 'S': // Sleeping (interruptible)
+ case 'D': // Uninterruptible sleep (usually I/O)
+ jobStatus.setState(JobState.ACTIVE);
+ break;
+ case 'T': // Stopped
+ jobStatus.setState(JobState.SUSPENDED);
+ break;
+ case 'Z': // Zombie (terminated but not reaped)
+ // Zombie processes are essentially completed but not cleaned up
+ jobStatus.setState(JobState.COMPLETED);
+ break;
+ default:
+ // Unknown status character
+ logger.warn("Unknown ps status character: {} for PID {}", firstChar, jobID);
+ jobStatus.setState(JobState.UNKNOWN);
+ }
+
+ return jobStatus;
+ }
+
+ /**
+ * Parse multiple job statuses from `ps -u ` output.
+ * Parses the ps output format: PID STAT TIME COMMAND
+ *
+ * @param userName Username
+ * @param statusMap Map of jobID -> StatusModel to populate
+ * @param rawOutput Output from `ps -u ` command
+ */
+ @Override
+ public void parseJobStatuses(String userName, Map> statusMap, String rawOutput)
+ throws Exception {
+ logger.debug("Parsing job statuses for user {}: {}", userName, rawOutput);
+ if (rawOutput == null || rawOutput.trim().isEmpty()) {
+ logger.info("No processes found for user {}", userName);
+ return;
+ }
+
+ String[] lines = rawOutput.split("\n");
+ for (String line : lines) {
+ line = line.trim();
+ if (line.isEmpty()) {
+ continue;
+ }
+
+ // Parse ps output: PID STAT TIME COMMAND
+ // Format: "12345 R 0:00 /bin/bash script.sh"
+ String[] parts = line.split("\\s+");
+ if (parts.length < 2) {
+ continue;
+ }
+
+ try {
+ String pid = parts[0];
+ String stat = parts[1];
+
+ // Check if this PID is in our status map
+ if (statusMap.containsKey(pid)) {
+ StatusModel jobStatus = new StatusModel<>();
+ char firstChar = stat.charAt(0);
+ switch (firstChar) {
+ case 'R':
+ case 'S':
+ case 'D':
+ jobStatus.setState(JobState.ACTIVE);
+ break;
+ case 'T':
+ jobStatus.setState(JobState.SUSPENDED);
+ break;
+ case 'Z':
+ jobStatus.setState(JobState.COMPLETED);
+ break;
+ default:
+ jobStatus.setState(JobState.UNKNOWN);
+ }
+ statusMap.put(pid, jobStatus);
+ logger.debug("Updated status for PID {}: {}", pid, jobStatus.getState());
+ }
+ } catch (Exception e) {
+ logger.warn("Error parsing ps line: {}", line, e);
+ }
+ }
+
+ // Mark any PIDs not found in ps output as UNKNOWN (process completed or doesn't exist)
+ for (String jobID : statusMap.keySet()) {
+ if (statusMap.get(jobID) == null || statusMap.get(jobID).getState() == null) {
+ StatusModel jobStatus = new StatusModel<>();
+ jobStatus.setState(JobState.UNKNOWN);
+ statusMap.put(jobID, jobStatus);
+ }
+ }
+ }
+
+ /**
+ * Parse job ID from `pgrep -u -f ` output.
+ * pgrep returns just the PID(s), one per line.
+ *
+ * @param jobName Job name/pattern to search for
+ * @param rawOutput Output from pgrep command
+ * @return First PID found, or null if not found
+ */
+ @Override
+ public String parseJobId(String jobName, String rawOutput) throws Exception {
+ logger.debug("Parsing job ID for job name {}: {}", jobName, rawOutput);
+ if (rawOutput == null || rawOutput.trim().isEmpty()) {
+ return null;
+ }
+
+ // pgrep returns PID(s), one per line
+ // Take the first PID found
+ String[] lines = rawOutput.split("\n");
+ for (String line : lines) {
+ line = line.trim();
+ if (!line.isEmpty()) {
+ // Validate it's a number (PID)
+ try {
+ Long.parseLong(line);
+ logger.info("Extracted PID from pgrep output: {}", line);
+ return line;
+ } catch (NumberFormatException e) {
+ logger.warn("Invalid PID format in pgrep output: {}", line);
+ }
+ }
+ }
+
+ logger.warn("No valid PID found in pgrep output for job name: {}", jobName);
+ return null;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobFactory.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobFactory.java
new file mode 100644
index 00000000000..847ee96fc26
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobFactory.java
@@ -0,0 +1,86 @@
+/**
+*
+* 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.compute.resource.submission;
+
+import java.util.Map;
+import org.apache.airavata.compute.provider.aws.AwsJobSpec;
+import org.apache.airavata.compute.provider.local.LocalJobSpec;
+import org.apache.airavata.compute.provider.local.LocalOutputParser;
+import org.apache.airavata.compute.provider.slurm.SlurmJobSpec;
+import org.apache.airavata.compute.provider.slurm.SlurmOutputParser;
+import org.apache.airavata.compute.resource.model.ComputeCapability;
+import org.apache.airavata.compute.resource.model.JobManagerCommand;
+import org.apache.airavata.compute.resource.model.Resource;
+import org.apache.airavata.compute.resource.model.ResourceJobManagerType;
+import org.springframework.stereotype.Component;
+
+@Component
+public class JobFactory {
+
+ public String getTemplateFileName(ResourceJobManagerType resourceJobManagerType) {
+ return switch (resourceJobManagerType) {
+ case FORK -> "FORK_Groovy.template";
+ case SLURM -> "SLURM_Groovy.template";
+ case CLOUD -> "CLOUD_Groovy.template";
+ default -> null;
+ };
+ }
+
+ // -------------------------------------------------------------------------
+ // New Resource-based methods
+ // -------------------------------------------------------------------------
+
+ /**
+ * Derive the {@link ResourceJobManagerType} from a {@link Resource}'s compute capability.
+ * Returns {@link ResourceJobManagerType#FORK} when the resource or its compute capability is absent.
+ */
+ public ResourceJobManagerType getResourceJobManagerType(Resource resource) {
+ if (resource == null
+ || resource.getCapabilities() == null
+ || resource.getCapabilities().getCompute() == null) {
+ return ResourceJobManagerType.FORK;
+ }
+ return resource.getCapabilities().getCompute().getJobManagerTypeEnum();
+ }
+
+ /**
+ * Build a {@link JobManagerSpec} from a {@link Resource}'s compute capability.
+ * Reads the job manager type, bin path, and command overrides from the capability.
+ */
+ public JobManagerSpec getJobManagerConfiguration(Resource resource) throws Exception {
+ ResourceJobManagerType type = getResourceJobManagerType(resource);
+ String templateFileName = "templates/" + getTemplateFileName(type);
+ String binPath = null;
+ Map commands = null;
+ if (resource != null
+ && resource.getCapabilities() != null
+ && resource.getCapabilities().getCompute() != null) {
+ ComputeCapability compute = resource.getCapabilities().getCompute();
+ binPath = compute.getJobManagerBinPath();
+ commands = compute.getJobManagerCommands();
+ }
+ return switch (type) {
+ case SLURM -> new SlurmJobSpec(templateFileName, ".slurm", binPath, commands, new SlurmOutputParser());
+ case FORK -> new LocalJobSpec(templateFileName, ".sh", binPath, commands, new LocalOutputParser());
+ case CLOUD -> new AwsJobSpec(templateFileName);
+ default -> throw new Exception("Could not find a job manager spec for job manager type " + type);
+ };
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobManagerSpec.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobManagerSpec.java
new file mode 100644
index 00000000000..ce6b1757dbb
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobManagerSpec.java
@@ -0,0 +1,39 @@
+/**
+*
+* 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.compute.resource.submission;
+
+import java.util.Optional;
+
+public interface JobManagerSpec {
+
+ RawCommandInfo getCancelCommand(String jobID);
+
+ String getJobDescriptionTemplateName();
+
+ Optional getMonitorCommand(String jobID);
+
+ Optional getJobIdMonitorCommand(String jobName, String userName);
+
+ String getScriptExtension();
+
+ RawCommandInfo getSubmitCommand(String workingDirectory, String scriptFilePath);
+
+ JobOutputParser getParser();
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobOutputParser.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobOutputParser.java
new file mode 100644
index 00000000000..4e4565fb464
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobOutputParser.java
@@ -0,0 +1,58 @@
+/**
+*
+* 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.compute.resource.submission;
+
+import java.util.Map;
+import org.apache.airavata.compute.resource.model.JobState;
+import org.apache.airavata.core.model.StatusModel;
+
+public interface JobOutputParser {
+
+ /**
+ * This can be used to parseSingleJob the result of a job submission to get the JobID
+ * @param rawOutput
+ * @return the job id as a String, or null if no job id found
+ */
+ public String parseJobSubmission(String rawOutput) throws Exception;
+
+ /**
+ * Parse output return by job submission task and identify jobSubmission failures.
+ * @param rawOutput
+ * @return true if job submission has been failed, false otherwise.
+ */
+ public boolean isJobSubmissionFailed(String rawOutput);
+
+ /**
+ * This can be used to get the job status from the output
+ * @param jobID
+ * @param rawOutput
+ */
+ public StatusModel parseJobStatus(String jobID, String rawOutput) throws Exception;
+
+ /**
+ * This can be used to parseSingleJob a big output and get multipleJob statuses
+ * @param statusMap list of status map will return and key will be the job ID
+ * @param rawOutput
+ */
+ public void parseJobStatuses(String userName, Map> statusMap, String rawOutput)
+ throws Exception;
+
+ public String parseJobId(String jobName, String rawOutput) throws Exception;
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobStateParser.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobStateParser.java
new file mode 100644
index 00000000000..39bfb9fed95
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobStateParser.java
@@ -0,0 +1,61 @@
+/**
+*
+* 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.compute.resource.submission;
+
+import org.apache.airavata.compute.resource.model.JobState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JobStateParser {
+ private static final Logger logger = LoggerFactory.getLogger(JobStateParser.class);
+
+ public static JobState getJobState(String status) {
+ logger.info("parsing the job status returned : {}", status);
+ if (status != null) {
+ if ("C".equals(status)
+ || "CD".equals(status)
+ || "E".equals(status)
+ || "CG".equals(status)
+ || "DONE".equals(status)) {
+ return JobState.COMPLETED;
+ } else if ("Q".equals(status) || "qw".equals(status) || "PEND".equals(status)) {
+ return JobState.QUEUED;
+ } else if ("R".equals(status) || "CF".equals(status) || "r".equals(status) || "RUN".equals(status)) {
+ return JobState.ACTIVE;
+ } else if ("W".equals(status) || "PD".equals(status) || "I".equals(status)) {
+ return JobState.QUEUED;
+ } else if ("S".equals(status)
+ || "PSUSP".equals(status)
+ || "USUSP".equals(status)
+ || "SSUSP".equals(status)) {
+ return JobState.SUSPENDED;
+ } else if ("CA".equals(status) || "X".equals(status)) {
+ return JobState.CANCELED;
+ } else if ("F".equals(status) || "NF".equals(status) || "TO".equals(status) || "EXIT".equals(status)) {
+ return JobState.FAILED;
+ } else if ("PR".equals(status) || "Er".equals(status)) {
+ return JobState.FAILED;
+ } else if ("U".equals(status) || ("UNKWN".equals(status))) {
+ return JobState.UNKNOWN;
+ }
+ }
+ return JobState.UNKNOWN;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobSubmissionData.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobSubmissionData.java
new file mode 100644
index 00000000000..b595c97af84
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobSubmissionData.java
@@ -0,0 +1,473 @@
+/**
+*
+* 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.compute.resource.submission;
+
+import groovy.text.GStringTemplateEngine;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Data container used by JobSubmissionDataBuilder to carry job submission parameters.
+ * These are passed into Groovy script templates to generate the final job script.
+ */
+public class JobSubmissionData {
+
+ private static final Logger logger = LoggerFactory.getLogger(JobSubmissionData.class);
+
+ private String jobName;
+ private String jobId;
+ private String workingDirectory;
+ private String taskId;
+ private String processId;
+ private String userName;
+ private String shellName = "/bin/bash";
+ private String queueName;
+ private int nodeCount;
+ private int cpuCount;
+ private int wallTimeLimit;
+ private String totalPhysicalMemory;
+ private String accountString;
+ private String reservationId;
+ private String qualityOfService;
+ private String applicationInputs;
+ private String executablePath;
+ private String standardInputFile;
+ private String standardOutputFile;
+ private String standardErrorFile;
+ private List modulesToLoad;
+ private List preJobCommands;
+ private List postJobCommands;
+ private List exportRequirements;
+ private List emailIds;
+ private String emailOnStart;
+ private String emailOnEnd;
+ private String emailOnFail;
+ private String stdoutFile;
+ private String stderrFile;
+ private boolean overrideLoginUserName;
+ private boolean overrideScratchLocation;
+ private boolean overrideAllocationProjectNumber;
+ private boolean generateJobscript;
+ private boolean hasOptionalFileInputs;
+ private String gatewayId;
+ private String gatewayUserName;
+ private String languageCode;
+ private String scriptExtension;
+ private String partition;
+ private String groupForJob;
+ private boolean exclusiveExecution;
+
+ public JobSubmissionData() {}
+
+ /**
+ * Load and render a Groovy template, returning the resulting script content.
+ * The template is loaded as a classpath resource.
+ *
+ * @param templateName the classpath template name (e.g. "templates/SLURM_Groovy.template")
+ * @return rendered job script content
+ * @throws Exception if template loading or rendering fails
+ */
+ public String loadFromFile(String templateName) throws Exception {
+ try (InputStream is = getClass().getClassLoader().getResourceAsStream(templateName)) {
+ if (is == null) {
+ logger.warn("Template file '{}' not found on classpath; returning empty script", templateName);
+ return "";
+ }
+ try (var reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
+ var engine = new GStringTemplateEngine();
+ var binding = buildBinding();
+ var template = engine.createTemplate(reader);
+ return template.make(binding).toString();
+ }
+ } catch (IOException e) {
+ logger.error("Failed to load template '{}'", templateName, e);
+ throw new Exception("Failed to load template: " + templateName, e);
+ } catch (Exception e) {
+ logger.error("Failed to render template '{}'", templateName, e);
+ throw new Exception("Failed to render template: " + templateName, e);
+ }
+ }
+
+ private Map buildBinding() {
+ var map = new HashMap();
+ map.put("jobName", jobName);
+ map.put("jobId", jobId);
+ map.put("workingDirectory", workingDirectory);
+ map.put("taskId", taskId);
+ map.put("processId", processId);
+ map.put("userName", userName);
+ map.put("shellName", shellName);
+ map.put("queueName", queueName);
+ map.put("nodeCount", nodeCount);
+ map.put("cpuCount", cpuCount);
+ map.put("wallTimeLimit", wallTimeLimit);
+ map.put("totalPhysicalMemory", totalPhysicalMemory);
+ map.put("accountString", accountString);
+ map.put("reservationId", reservationId);
+ map.put("qualityOfService", qualityOfService);
+ map.put("applicationInputs", applicationInputs);
+ map.put("executablePath", executablePath);
+ map.put("standardInputFile", standardInputFile);
+ map.put("standardOutputFile", standardOutputFile);
+ map.put("standardErrorFile", standardErrorFile);
+ map.put("modulesToLoad", modulesToLoad != null ? modulesToLoad : new ArrayList<>());
+ map.put("preJobCommands", preJobCommands != null ? preJobCommands : new ArrayList<>());
+ map.put("postJobCommands", postJobCommands != null ? postJobCommands : new ArrayList<>());
+ map.put("exportRequirements", exportRequirements != null ? exportRequirements : new ArrayList<>());
+ map.put("emailIds", emailIds != null ? emailIds : new ArrayList<>());
+ map.put("emailOnStart", emailOnStart);
+ map.put("emailOnEnd", emailOnEnd);
+ map.put("emailOnFail", emailOnFail);
+ map.put("stdoutFile", stdoutFile);
+ map.put("stderrFile", stderrFile);
+ map.put("partition", partition);
+ map.put("groupForJob", groupForJob);
+ map.put("exclusiveExecution", exclusiveExecution);
+ map.put("gatewayId", gatewayId);
+ map.put("gatewayUserName", gatewayUserName);
+ // Pass self as map entry so templates can call mapData.xyz
+ map.put("mapData", this);
+ return map;
+ }
+
+ public String getJobName() {
+ return jobName;
+ }
+
+ public void setJobName(String jobName) {
+ this.jobName = jobName;
+ }
+
+ public String getJobId() {
+ return jobId;
+ }
+
+ public void setJobId(String jobId) {
+ this.jobId = jobId;
+ }
+
+ public String getWorkingDirectory() {
+ return workingDirectory;
+ }
+
+ public void setWorkingDirectory(String workingDirectory) {
+ this.workingDirectory = workingDirectory;
+ }
+
+ public void setWorkingDir(String workingDir) {
+ this.workingDirectory = workingDir;
+ }
+
+ public String getTaskId() {
+ return taskId;
+ }
+
+ public void setTaskId(String taskId) {
+ this.taskId = taskId;
+ }
+
+ public String getProcessId() {
+ return processId;
+ }
+
+ public void setProcessId(String processId) {
+ this.processId = processId;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getShellName() {
+ return shellName;
+ }
+
+ public void setShellName(String shellName) {
+ this.shellName = shellName;
+ }
+
+ public String getQueueName() {
+ return queueName;
+ }
+
+ public void setQueueName(String queueName) {
+ this.queueName = queueName;
+ }
+
+ public int getNodeCount() {
+ return nodeCount;
+ }
+
+ public void setNodeCount(int nodeCount) {
+ this.nodeCount = nodeCount;
+ }
+
+ public int getCpuCount() {
+ return cpuCount;
+ }
+
+ public void setCpuCount(int cpuCount) {
+ this.cpuCount = cpuCount;
+ }
+
+ public int getWallTimeLimit() {
+ return wallTimeLimit;
+ }
+
+ public void setWallTimeLimit(int wallTimeLimit) {
+ this.wallTimeLimit = wallTimeLimit;
+ }
+
+ public String getTotalPhysicalMemory() {
+ return totalPhysicalMemory;
+ }
+
+ public void setTotalPhysicalMemory(String totalPhysicalMemory) {
+ this.totalPhysicalMemory = totalPhysicalMemory;
+ }
+
+ public String getAccountString() {
+ return accountString;
+ }
+
+ public void setAccountString(String accountString) {
+ this.accountString = accountString;
+ }
+
+ public String getReservationId() {
+ return reservationId;
+ }
+
+ public void setReservationId(String reservationId) {
+ this.reservationId = reservationId;
+ }
+
+ public String getQualityOfService() {
+ return qualityOfService;
+ }
+
+ public void setQualityOfService(String qualityOfService) {
+ this.qualityOfService = qualityOfService;
+ }
+
+ public String getApplicationInputs() {
+ return applicationInputs;
+ }
+
+ public void setApplicationInputs(String applicationInputs) {
+ this.applicationInputs = applicationInputs;
+ }
+
+ public String getExecutablePath() {
+ return executablePath;
+ }
+
+ public void setExecutablePath(String executablePath) {
+ this.executablePath = executablePath;
+ }
+
+ public String getStandardInputFile() {
+ return standardInputFile;
+ }
+
+ public void setStandardInputFile(String standardInputFile) {
+ this.standardInputFile = standardInputFile;
+ }
+
+ public String getStandardOutputFile() {
+ return standardOutputFile;
+ }
+
+ public void setStandardOutputFile(String standardOutputFile) {
+ this.standardOutputFile = standardOutputFile;
+ }
+
+ public String getStandardErrorFile() {
+ return standardErrorFile;
+ }
+
+ public void setStandardErrorFile(String standardErrorFile) {
+ this.standardErrorFile = standardErrorFile;
+ }
+
+ public List getModulesToLoad() {
+ return modulesToLoad;
+ }
+
+ public void setModulesToLoad(List modulesToLoad) {
+ this.modulesToLoad = modulesToLoad;
+ }
+
+ public List getPreJobCommands() {
+ return preJobCommands;
+ }
+
+ public void setPreJobCommands(List preJobCommands) {
+ this.preJobCommands = preJobCommands;
+ }
+
+ public List getPostJobCommands() {
+ return postJobCommands;
+ }
+
+ public void setPostJobCommands(List postJobCommands) {
+ this.postJobCommands = postJobCommands;
+ }
+
+ public List getExportRequirements() {
+ return exportRequirements;
+ }
+
+ public void setExportRequirements(List exportRequirements) {
+ this.exportRequirements = exportRequirements;
+ }
+
+ public List getEmailIds() {
+ return emailIds;
+ }
+
+ public void setEmailIds(List emailIds) {
+ this.emailIds = emailIds;
+ }
+
+ public String getEmailOnStart() {
+ return emailOnStart;
+ }
+
+ public void setEmailOnStart(String emailOnStart) {
+ this.emailOnStart = emailOnStart;
+ }
+
+ public String getEmailOnEnd() {
+ return emailOnEnd;
+ }
+
+ public void setEmailOnEnd(String emailOnEnd) {
+ this.emailOnEnd = emailOnEnd;
+ }
+
+ public String getEmailOnFail() {
+ return emailOnFail;
+ }
+
+ public void setEmailOnFail(String emailOnFail) {
+ this.emailOnFail = emailOnFail;
+ }
+
+ public String getStdoutFile() {
+ return stdoutFile;
+ }
+
+ public void setStdoutFile(String stdoutFile) {
+ this.stdoutFile = stdoutFile;
+ }
+
+ public String getStderrFile() {
+ return stderrFile;
+ }
+
+ public void setStderrFile(String stderrFile) {
+ this.stderrFile = stderrFile;
+ }
+
+ public String getPartition() {
+ return partition;
+ }
+
+ public void setPartition(String partition) {
+ this.partition = partition;
+ }
+
+ public String getGroupForJob() {
+ return groupForJob;
+ }
+
+ public void setGroupForJob(String groupForJob) {
+ this.groupForJob = groupForJob;
+ }
+
+ public boolean isExclusiveExecution() {
+ return exclusiveExecution;
+ }
+
+ public void setExclusiveExecution(boolean exclusiveExecution) {
+ this.exclusiveExecution = exclusiveExecution;
+ }
+
+ public boolean isGenerateJobscript() {
+ return generateJobscript;
+ }
+
+ public void setGenerateJobscript(boolean generateJobscript) {
+ this.generateJobscript = generateJobscript;
+ }
+
+ public boolean isHasOptionalFileInputs() {
+ return hasOptionalFileInputs;
+ }
+
+ public void setHasOptionalFileInputs(boolean hasOptionalFileInputs) {
+ this.hasOptionalFileInputs = hasOptionalFileInputs;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ public String getGatewayUserName() {
+ return gatewayUserName;
+ }
+
+ public void setGatewayUserName(String gatewayUserName) {
+ this.gatewayUserName = gatewayUserName;
+ }
+
+ public String getLanguageCode() {
+ return languageCode;
+ }
+
+ public void setLanguageCode(String languageCode) {
+ this.languageCode = languageCode;
+ }
+
+ public String getScriptExtension() {
+ return scriptExtension;
+ }
+
+ public void setScriptExtension(String scriptExtension) {
+ this.scriptExtension = scriptExtension;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobSubmissionDataBuilder.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobSubmissionDataBuilder.java
new file mode 100644
index 00000000000..9f508d2d94a
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobSubmissionDataBuilder.java
@@ -0,0 +1,114 @@
+/**
+*
+* 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.compute.resource.submission;
+
+import org.apache.airavata.execution.dag.TaskContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * Builder that creates a {@link JobSubmissionData} from a {@link TaskContext}.
+ * The resulting data object is passed into Groovy templates to generate job submission scripts.
+ */
+@Component
+public class JobSubmissionDataBuilder {
+
+ private static final Logger logger = LoggerFactory.getLogger(JobSubmissionDataBuilder.class);
+
+ /**
+ * Build a {@link JobSubmissionData} populated from the given {@link TaskContext}.
+ *
+ * @param taskContext the task execution context
+ * @return populated JobSubmissionData
+ * @throws Exception if required values cannot be resolved from the context
+ */
+ public JobSubmissionData build(TaskContext taskContext) throws Exception {
+ var mapData = new JobSubmissionData();
+
+ mapData.setProcessId(taskContext.getProcessId());
+ mapData.setTaskId(taskContext.getTaskId());
+ mapData.setGatewayId(taskContext.getGatewayId());
+
+ // Working directory
+ try {
+ mapData.setWorkingDirectory(taskContext.getWorkingDir());
+ } catch (Exception e) {
+ logger.warn("Could not resolve working directory from task context", e);
+ }
+
+ // Stdout / stderr
+ try {
+ mapData.setStdoutFile(taskContext.getStdoutLocation());
+ } catch (Exception e) {
+ logger.warn("Could not resolve stdout location", e);
+ }
+ try {
+ mapData.setStderrFile(taskContext.getStderrLocation());
+ } catch (Exception e) {
+ logger.warn("Could not resolve stderr location", e);
+ }
+
+ // Login username
+ mapData.setUserName(taskContext.getComputeResourceLoginUserName());
+
+ // Scheduling parameters from resource schedule map
+ mapData.setQueueName(taskContext.getQueueName());
+ mapData.setAccountString(taskContext.getAllocationProjectNumber());
+ mapData.setReservationId(taskContext.getReservation());
+ mapData.setQualityOfService(taskContext.getQualityOfService());
+
+ // Scheduling integers from process model resource schedule map
+ var processModel = taskContext.getProcessModel();
+ if (processModel != null && processModel.getResourceSchedule() != null) {
+ var schedule = processModel.getResourceSchedule();
+ mapData.setNodeCount(toInt(schedule.get("nodeCount")));
+ mapData.setCpuCount(toInt(schedule.get("totalCPUCount")));
+ mapData.setWallTimeLimit(toInt(schedule.get("wallTimeLimit")));
+ Object mem = schedule.get("totalPhysicalMemory");
+ if (mem != null) {
+ mapData.setTotalPhysicalMemory(mem.toString());
+ }
+ }
+
+ // Application info
+ var appModel = taskContext.getApplication();
+ if (appModel != null) {
+ mapData.setJobName("Airavata_" + appModel.getName() + "_" + taskContext.getProcessId());
+ } else {
+ mapData.setJobName("Airavata_" + taskContext.getProcessId());
+ }
+
+ logger.debug(
+ "Built JobSubmissionData for process {} with working directory {}",
+ taskContext.getProcessId(),
+ mapData.getWorkingDirectory());
+ return mapData;
+ }
+
+ private int toInt(Object value) {
+ if (value == null) return 0;
+ try {
+ return Integer.parseInt(value.toString());
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobSubmissionSupport.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobSubmissionSupport.java
new file mode 100644
index 00000000000..53344a4b699
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/JobSubmissionSupport.java
@@ -0,0 +1,369 @@
+/**
+*
+* 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.compute.resource.submission;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.airavata.compute.resource.model.Job;
+import org.apache.airavata.compute.resource.model.JobState;
+import org.apache.airavata.compute.resource.model.Resource;
+import org.apache.airavata.config.ServerProperties;
+import org.apache.airavata.config.ServiceConditionals.ConditionalOnParticipant;
+import org.apache.airavata.core.model.StatusModel;
+import org.apache.airavata.core.util.IdGenerator;
+import org.apache.airavata.execution.orchestration.ComputeSubmissionTracker;
+import org.apache.airavata.protocol.AgentAdapter;
+import org.apache.airavata.protocol.AgentAdapter.AgentException;
+import org.apache.airavata.protocol.AgentAdapter.CommandOutput;
+import org.apache.airavata.status.service.StatusService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConditionalOnParticipant
+public class JobSubmissionSupport {
+
+ private static final Logger logger = LoggerFactory.getLogger(JobSubmissionSupport.class);
+
+ private final JobFactory jobFactory;
+ private final ServerProperties serverProperties;
+ private final StatusService statusService;
+
+ public JobSubmissionSupport(JobFactory jobFactory, ServerProperties serverProperties, StatusService statusService) {
+ this.jobFactory = jobFactory;
+ this.serverProperties = serverProperties;
+ this.statusService = statusService;
+ }
+
+ public JobSubmissionOutput submitBatchJob(
+ AgentAdapter agentAdapter,
+ JobSubmissionData jobSubmissionData,
+ String workingDirectory,
+ Resource computeResource,
+ String processId,
+ ComputeSubmissionTracker computeSubmissionTracker,
+ String computeResourceId)
+ throws Exception {
+
+ var jobManagerConfiguration = jobFactory.getJobManagerConfiguration(computeResource);
+
+ addMonitoringCommands(jobSubmissionData);
+
+ var scriptAsString = jobSubmissionData.loadFromFile(jobManagerConfiguration.getJobDescriptionTemplateName());
+ logger.info("Generated job submission script : {}", scriptAsString);
+
+ int number = new SecureRandom().nextInt();
+ number = (number < 0 ? -number : number);
+ var localDir = getLocalDataDir(processId);
+ localDir.mkdirs();
+ var tempJobFile =
+ new File(localDir, "job_" + Integer.toString(number) + jobManagerConfiguration.getScriptExtension());
+
+ Files.writeString(
+ tempJobFile.toPath(), scriptAsString, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+ logger.info("Job submission file for process {} was created at : {}", processId, tempJobFile.getAbsolutePath());
+
+ logger.info(
+ "Copying file form {} to remote path {} of compute resource {}",
+ tempJobFile.getAbsolutePath(),
+ workingDirectory,
+ computeResourceId);
+ agentAdapter.uploadFile(tempJobFile.getAbsolutePath(), workingDirectory);
+
+ var submitCommand = jobManagerConfiguration.getSubmitCommand(workingDirectory, tempJobFile.getPath());
+
+ logger.info("Submit command for process id {} : {}", processId, submitCommand.getRawCommand());
+ logger.debug("Working directory for process id {} : {}", processId, workingDirectory);
+
+ var commandOutput =
+ submitCommandWithRecording(submitCommand, agentAdapter, jobSubmissionData, workingDirectory);
+ logger.info("Job {} submitted to compute resource", jobSubmissionData.getJobName());
+ logger.info("Submission stdout: {}, stderr: {}", commandOutput.getStdOut(), commandOutput.getStdError());
+
+ var jsoutput = new JobSubmissionOutput();
+ jsoutput.setDescription(scriptAsString);
+
+ jsoutput.setJobId(jobManagerConfiguration.getParser().parseJobSubmission(commandOutput.getStdOut()));
+ if (jsoutput.getJobId() == null) {
+ if (jobManagerConfiguration.getParser().isJobSubmissionFailed(commandOutput.getStdOut())) {
+ jsoutput.setJobSubmissionFailed(true);
+ jsoutput.setFailureReason(
+ "stdout : " + commandOutput.getStdOut() + "\n stderr : " + commandOutput.getStdError());
+ }
+ }
+ jsoutput.setExitCode(commandOutput.getExitCode());
+ if (jsoutput.getExitCode() != 0) {
+ jsoutput.setJobSubmissionFailed(true);
+ jsoutput.setFailureReason(
+ "stdout : " + commandOutput.getStdOut() + "\n stderr : " + commandOutput.getStdError());
+ }
+ jsoutput.setStdOut(commandOutput.getStdOut());
+ jsoutput.setStdErr(commandOutput.getStdError());
+
+ if (computeSubmissionTracker != null && jsoutput.getJobId() != null && !jsoutput.isJobSubmissionFailed()) {
+ if (computeResourceId != null) {
+ computeSubmissionTracker.recordSubmission(computeResourceId);
+ logger.debug("Recorded job submission for compute resource: {}", computeResourceId);
+ }
+ }
+
+ return jsoutput;
+ }
+
+ public boolean cancelJob(AgentAdapter agentAdapter, String jobId, Resource computeResource) throws Exception {
+ var jobManagerConfiguration = jobFactory.getJobManagerConfiguration(computeResource);
+ var commandOutput = agentAdapter.executeCommand(
+ jobManagerConfiguration.getCancelCommand(jobId).getRawCommand(), null);
+ return commandOutput.getExitCode() == 0;
+ }
+
+ public StatusModel getJobStatus(AgentAdapter agentAdapter, String jobId, Resource computeResource)
+ throws Exception {
+ var jobManagerConfiguration = jobFactory.getJobManagerConfiguration(computeResource);
+
+ var monitorCommand = jobManagerConfiguration.getMonitorCommand(jobId);
+ if (monitorCommand.isEmpty()) {
+ logger.info("No monitor command available for job {}; cannot query status", jobId);
+ return null;
+ }
+
+ var commandOutput = agentAdapter.executeCommand(monitorCommand.get().getRawCommand(), null);
+
+ return jobManagerConfiguration.getParser().parseJobStatus(jobId, commandOutput.getStdOut());
+ }
+
+ public String getJobIdByJobName(
+ AgentAdapter agentAdapter, String jobName, String userName, Resource computeResource) throws Exception {
+ var jobManagerConfiguration = jobFactory.getJobManagerConfiguration(computeResource);
+
+ var jobIdMonitorCommand = jobManagerConfiguration.getJobIdMonitorCommand(jobName, userName);
+ if (jobIdMonitorCommand.isEmpty()) {
+ logger.info("No job ID monitor command available for job name {}; cannot resolve job ID", jobName);
+ return null;
+ }
+ var commandOutput =
+ agentAdapter.executeCommand(jobIdMonitorCommand.get().getRawCommand(), null);
+ return jobManagerConfiguration.getParser().parseJobId(jobName, commandOutput.getStdOut());
+ }
+
+ public File getLocalDataDir(String processId) {
+ String outputPath = serverProperties.localDataLocation();
+ outputPath = (outputPath.endsWith(File.separator) ? outputPath : outputPath + File.separator);
+ return new File(outputPath + processId);
+ }
+
+ /**
+ * Create a new {@link Job} pre-populated with the standard fields shared across
+ * all job submission task implementations.
+ */
+ public Job createJob(String processId, String taskId, JobSubmissionData mapData) {
+ var jobModel = new Job();
+ jobModel.setProcessId(processId);
+ jobModel.setWorkingDir(mapData.getWorkingDirectory());
+ jobModel.setCreatedAt(IdGenerator.getCurrentTimestamp());
+ jobModel.setJobName(mapData.getJobName());
+ return jobModel;
+ }
+
+ /**
+ * Set the job status on the model and persist + publish it in one call.
+ * This eliminates the repeated pattern of creating a StatusModel, wrapping it
+ * in a list, setting it on the job model, then calling saveAndPublishJobStatus.
+ */
+ public void publishJobStatus(Job jobModel, JobState state, String reason) throws Exception {
+ StatusModel jobStatus = StatusModel.of(state, reason);
+ jobModel.setJobStatuses(List.of(jobStatus));
+ saveAndPublishJobStatus(jobModel);
+ }
+
+ public void saveAndPublishJobStatus(Job jobModel) throws Exception {
+ try {
+ StatusModel jobStatus;
+ if (jobModel.getJobStatuses() != null && !jobModel.getJobStatuses().isEmpty()) {
+ jobStatus = jobModel.getJobStatuses().get(0);
+ } else {
+ logger.error("Job statuses can not be empty");
+ return;
+ }
+
+ jobStatus.setTimeOfStateChange(IdGenerator.getCurrentTimestamp().toEpochMilli());
+ statusService.addJobStatus(jobStatus, jobModel.getJobId());
+ } catch (Exception e) {
+ throw new Exception("Error persisting job status " + e.getLocalizedMessage(), e);
+ }
+ }
+
+ public void addMonitoringCommands(JobSubmissionData mapData) {
+ try {
+ String url = serverProperties.services().monitor().compute().jobStatusCallbackUrl();
+ if (serverProperties.services().monitor().realtime().enabled() && url != null && !url.isBlank()) {
+ String payload = "'{\"jobName\":\"" + mapData.getJobName() + "\",\"status\":\"%s\",\"task\":\""
+ + mapData.getTaskId() + "\"}'";
+ if (mapData.getPreJobCommands() == null) {
+ mapData.setPreJobCommands(new ArrayList<>());
+ }
+ mapData.getPreJobCommands()
+ .add(
+ 0,
+ "curl -s -X POST -H \"Content-Type: application/json\" --data "
+ + String.format(payload, "RUNNING") + " \"" + url
+ + "\" > /dev/null || true");
+ if (mapData.getPostJobCommands() == null) {
+ mapData.setPostJobCommands(new ArrayList<>());
+ }
+ mapData.getPostJobCommands()
+ .add("curl -s -X POST -H \"Content-Type: application/json\" --data "
+ + String.format(payload, "COMPLETED") + " \"" + url + "\" > /dev/null || true");
+ }
+ } catch (Exception e) {
+ logger.warn("Could not get properties for monitoring commands", e);
+ }
+ }
+
+ public JobManagerSpec getJobManagerConfiguration(Resource computeResource) throws Exception {
+ return jobFactory.getJobManagerConfiguration(computeResource);
+ }
+
+ private CommandOutput submitCommandWithRecording(
+ RawCommandInfo submitCommand,
+ AgentAdapter agentAdapter,
+ JobSubmissionData jobSubmissionData,
+ String workingDirectory)
+ throws AgentException {
+
+ String modifiedCommand = submitCommand.getCommand() + " | tee " + getJobCommandRecordingFile(jobSubmissionData);
+ logger.info("Modified the submit command to support recording : {}", modifiedCommand);
+
+ CommandOutput commandOutput = agentAdapter.executeCommand(modifiedCommand, workingDirectory);
+
+ if (commandOutput.getStdOut() == null || "".equals(commandOutput.getStdOut())) {
+ logger.warn(
+ "command submission returned empty response so reading recording file at {}",
+ getJobCommandRecordingFile(jobSubmissionData));
+ CommandOutput recordingFileReadCommandOutput = agentAdapter.executeCommand(
+ "cat " + getJobCommandRecordingFile(jobSubmissionData), jobSubmissionData.getWorkingDirectory());
+ if (recordingFileReadCommandOutput.getStdOut() != null
+ && !"".equals(recordingFileReadCommandOutput.getStdOut())) {
+ logger.info(
+ "Received non empty output form recording file : {}",
+ recordingFileReadCommandOutput.getStdOut());
+ return recordingFileReadCommandOutput;
+ } else {
+ return commandOutput;
+ }
+ } else {
+ return commandOutput;
+ }
+ }
+
+ private String getJobCommandRecordingFile(JobSubmissionData mapData) {
+ return (mapData.getWorkingDirectory().endsWith(File.separator)
+ ? mapData.getWorkingDirectory()
+ : mapData.getWorkingDirectory() + File.separator)
+ + mapData.getJobName();
+ }
+
+ public static class JobSubmissionOutput {
+ private int exitCode = Integer.MIN_VALUE;
+ private String stdOut;
+ private String stdErr;
+ private String command;
+ private String jobId;
+ private boolean isJobSubmissionFailed;
+ private String failureReason;
+ private String description;
+
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ public JobSubmissionOutput setExitCode(int exitCode) {
+ this.exitCode = exitCode;
+ return this;
+ }
+
+ public String getStdOut() {
+ return stdOut;
+ }
+
+ public JobSubmissionOutput setStdOut(String stdOut) {
+ this.stdOut = stdOut;
+ return this;
+ }
+
+ public String getStdErr() {
+ return stdErr;
+ }
+
+ public JobSubmissionOutput setStdErr(String stdErr) {
+ this.stdErr = stdErr;
+ return this;
+ }
+
+ public String getCommand() {
+ return command;
+ }
+
+ public JobSubmissionOutput setCommand(String command) {
+ this.command = command;
+ return this;
+ }
+
+ public String getJobId() {
+ return jobId;
+ }
+
+ public JobSubmissionOutput setJobId(String jobId) {
+ this.jobId = jobId;
+ return this;
+ }
+
+ public boolean isJobSubmissionFailed() {
+ return isJobSubmissionFailed;
+ }
+
+ public JobSubmissionOutput setJobSubmissionFailed(boolean jobSubmissionFailed) {
+ isJobSubmissionFailed = jobSubmissionFailed;
+ return this;
+ }
+
+ public String getFailureReason() {
+ return failureReason;
+ }
+
+ public JobSubmissionOutput setFailureReason(String failureReason) {
+ this.failureReason = failureReason;
+ return this;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ }
+}
diff --git a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/config/RawCommandInfo.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/RawCommandInfo.java
similarity index 94%
rename from airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/config/RawCommandInfo.java
rename to airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/RawCommandInfo.java
index b7fb0a15e17..4d2603f1d2e 100644
--- a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/submission/config/RawCommandInfo.java
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/compute/resource/submission/RawCommandInfo.java
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.airavata.helix.impl.task.submission.config;
+package org.apache.airavata.compute.resource.submission;
public class RawCommandInfo {
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/ConfigResolver.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/ConfigResolver.java
new file mode 100644
index 00000000000..4dd75880e44
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/ConfigResolver.java
@@ -0,0 +1,210 @@
+/**
+*
+* 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.config;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for Airavata configuration directory and file access.
+ * Provides static methods for resolving config directory, loading files, and accessing properties.
+ *
+ *
These utilities are used by tools, Dapr tasks, and other non-Spring contexts
+ * that need to access configuration files directly.
+ */
+public class ConfigResolver {
+
+ private static final Logger logger = LoggerFactory.getLogger(ConfigResolver.class);
+ private static final String APPLICATION_PROPERTIES = "application.properties";
+ private static volatile Properties cachedProperties;
+
+ private ConfigResolver() {
+ // Utility class - prevent instantiation
+ }
+
+ /**
+ * Resolve the resources root path in IDE mode.
+ * Attempts to find resources root by locating application.properties on classpath.
+ *
+ * @return Absolute path to resources root, or null if not found
+ */
+ private static String resolveResourcesRoot() {
+ try {
+ // Try to locate application.properties on classpath
+ var resourceUrl = ConfigResolver.class.getClassLoader().getResource(APPLICATION_PROPERTIES);
+ if (resourceUrl != null && "file".equals(resourceUrl.getProtocol())) {
+ var resourcePath = resourceUrl.getPath();
+ // Remove URL encoding if present
+ if (resourcePath.contains("%20")) {
+ resourcePath = java.net.URLDecoder.decode(resourcePath, "UTF-8");
+ }
+ // Remove leading slash on Windows
+ if (resourcePath.startsWith("/")
+ && System.getProperty("os.name").toLowerCase().contains("win")) {
+ resourcePath = resourcePath.substring(1);
+ }
+ // Navigate up from application.properties to resources root
+ var resourceFile = new java.io.File(resourcePath);
+ var resourcesRoot = resourceFile.getParentFile();
+ if (resourcesRoot != null && resourcesRoot.exists() && resourcesRoot.isDirectory()) {
+ return resourcesRoot.getAbsolutePath();
+ }
+ }
+ } catch (Exception e) {
+ logger.debug("Could not resolve resources root", e);
+ }
+ return null;
+ }
+
+ /**
+ * Get the config directory path.
+ * Resolution precedence:
+ *
+ *
System property -Dairavata.config.dir=XXX (if set and non-empty)
+ *
System property -Dairavata.home=XXX (returns {airavataHome}/conf)
+ *
+ *
+ * @return The config directory path
+ * @throws IllegalStateException if config directory cannot be resolved
+ */
+ public static String getConfigDir() {
+ // Explicit config dir (e.g. from --config-dir or -Dairavata.config.dir)
+ var configDirProp = System.getProperty("airavata.config.dir");
+ if (configDirProp != null && !configDirProp.isEmpty()) {
+ var dir = new File(configDirProp);
+ if (dir.exists() && dir.isDirectory()) {
+ return dir.getAbsolutePath();
+ }
+ throw new IllegalStateException("Config directory does not exist: " + configDirProp
+ + ". Please ensure -Dairavata.config.dir points to a valid directory.");
+ }
+
+ // Derive from airavata.home
+ var systemPropertyHome = System.getProperty("airavata.home");
+ if (systemPropertyHome != null && !systemPropertyHome.isEmpty()) {
+ var confDir = new File(systemPropertyHome, "conf");
+ if (confDir.exists() && confDir.isDirectory()) {
+ return confDir.getAbsolutePath();
+ }
+ // Fall back to airavata.home itself if conf doesn't exist
+ var homeDir = new File(systemPropertyHome);
+ if (homeDir.exists() && homeDir.isDirectory()) {
+ return homeDir.getAbsolutePath();
+ }
+ throw new IllegalStateException("Config directory does not exist at " + confDir.getAbsolutePath()
+ + ". Please ensure -Dairavata.home points to the correct Airavata installation directory.");
+ }
+
+ // IDE mode: Try to resolve resources root
+ var resourcesRoot = resolveResourcesRoot();
+ if (resourcesRoot != null) {
+ logger.debug("IDE mode: using resources root configDir: {}", resourcesRoot);
+ return resourcesRoot;
+ }
+
+ throw new IllegalStateException(
+ "airavata.home is not set. Please set -Dairavata.home=XXX or -Dairavata.config.dir=XXX.");
+ }
+
+ /**
+ * Load a config file from configDir.
+ *
+ * @param fileName The filename (e.g., "email-config.yml", "logback.xml")
+ * @return URL to the file
+ * @throws IllegalStateException if configDir cannot be resolved or file is not found
+ */
+ public static URL loadFile(String fileName) {
+ var configDir = getConfigDir();
+ try {
+ var configDirPath = configDir.endsWith(File.separator) ? configDir : configDir + File.separator;
+ var filePath = configDirPath + fileName;
+ var file = new File(filePath);
+ if (file.exists() && file.isFile()) {
+ logger.debug("Loading file from configDir: {}", file.getAbsolutePath());
+ return file.toURI().toURL();
+ }
+ throw new IllegalStateException("Config file not found: " + fileName + " in configDir: " + configDir);
+ } catch (MalformedURLException e) {
+ throw new IllegalStateException("Error parsing the file path from configDir: " + configDir, e);
+ }
+ }
+
+ /**
+ * Load and cache application.properties from classpath or config directory.
+ */
+ public static Properties getProperties() {
+ var props = cachedProperties;
+ if (props != null) {
+ return props;
+ }
+ synchronized (ConfigResolver.class) {
+ if (cachedProperties != null) {
+ return cachedProperties;
+ }
+ var loaded = new Properties();
+ // Try classpath first
+ try (InputStream is = ConfigResolver.class.getClassLoader().getResourceAsStream(APPLICATION_PROPERTIES)) {
+ if (is != null) {
+ loaded.load(is);
+ cachedProperties = loaded;
+ return loaded;
+ }
+ } catch (Exception e) {
+ logger.debug("Could not load application.properties from classpath", e);
+ }
+ // Fall back to file system
+ try {
+ var url = loadFile(APPLICATION_PROPERTIES);
+ try (var is = url.openStream()) {
+ loaded.load(is);
+ }
+ cachedProperties = loaded;
+ return loaded;
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to load application.properties", e);
+ }
+ }
+ }
+
+ /**
+ * Lightweight, non-Spring property access for tools/Dapr tasks.
+ * Order: system props -> env vars -> loaded application.properties.
+ */
+ public static String getSetting(String key, String defaultValue) {
+ var value = System.getProperty(key);
+ if (value == null) {
+ value = System.getenv(key);
+ }
+ if (value == null) {
+ value = getProperties().getProperty(key);
+ }
+ return value != null ? value : defaultValue;
+ }
+
+ public static String getSetting(String key) {
+ return getSetting(key, null);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/CorsConfiguration.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/CorsConfiguration.java
new file mode 100644
index 00000000000..9577bfa6da4
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/CorsConfiguration.java
@@ -0,0 +1,59 @@
+/**
+*
+* 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.config;
+
+import java.util.Arrays;
+import java.util.List;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+/**
+ * CORS configuration for the Airavata API.
+ * Allowed origins are read from {@code airavata.cors.allowed-origins} in application.properties.
+ * Falls back to localhost dev origins if not configured.
+ */
+@Configuration
+public class CorsConfiguration {
+
+ private static final List DEFAULT_ORIGINS = List.of("http://localhost:3000", "http://localhost:5173");
+
+ @Bean
+ public CorsFilter corsFilter(ServerProperties properties) {
+ List origins = (properties.cors() != null
+ && properties.cors().allowedOrigins() != null
+ && !properties.cors().allowedOrigins().isEmpty())
+ ? properties.cors().allowedOrigins()
+ : DEFAULT_ORIGINS;
+
+ org.springframework.web.cors.CorsConfiguration config = new org.springframework.web.cors.CorsConfiguration();
+ config.setAllowedOrigins(origins);
+ config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
+ config.setAllowedHeaders(Arrays.asList("*"));
+ config.setAllowCredentials(true);
+ config.setMaxAge(3600L);
+
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", config);
+
+ return new CorsFilter(source);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/DatabaseVersionConstants.java.template b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/DatabaseVersionConstants.java.template
new file mode 100644
index 00000000000..586ab699b1f
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/DatabaseVersionConstants.java.template
@@ -0,0 +1,49 @@
+/*
+ * 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.config;
+
+/**
+ * Constants for database version numbers.
+ * These versions are inserted into the CONFIGURATION table after Flyway migrations.
+ */
+public final class DatabaseVersionConstants {
+
+ /** App Catalog database version */
+ public static final String APP_CATALOG_VERSION = "${project.version}";
+
+ /** Experiment Catalog database version (registry.version) */
+ public static final String EXPERIMENT_CATALOG_VERSION = "${project.version}";
+
+ /** Credential Store database version */
+ public static final String CREDENTIAL_STORE_VERSION = "${project.version}";
+
+ /** Profile Service database version */
+ public static final String PROFILE_SERVICE_VERSION = "${project.version}";
+
+ /** Replica Catalog database version */
+ public static final String REPLICA_CATALOG_VERSION = "${project.version}";
+
+ /** Sharing Registry database version */
+ public static final String SHARING_REGISTRY_VERSION = "${project.version}";
+
+ private DatabaseVersionConstants() {
+ // Utility class - prevent instantiation
+ }
+}
+
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/EntityMapperConfiguration.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/EntityMapperConfiguration.java
new file mode 100644
index 00000000000..6a7b1588f55
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/EntityMapperConfiguration.java
@@ -0,0 +1,45 @@
+/**
+*
+* 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.config;
+
+import org.mapstruct.CollectionMappingStrategy;
+import org.mapstruct.MapperConfig;
+import org.mapstruct.NullValueMappingStrategy;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * Base mapper configuration for Entity <-> Domain Model mappings.
+ *
+ *
Key configuration:
+ *
+ *
nullValueMappingStrategy = RETURN_DEFAULT: Returns empty collections instead of null
+ *
collectionMappingStrategy = ADDER_PREFERRED: Uses adder methods for collection mapping
+ *
+ */
+@MapperConfig(
+ componentModel = "spring",
+ nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE,
+ nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT,
+ collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED,
+ unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface EntityMapperConfiguration {
+ // Base interface for all entity-to-domain mappers
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/FlywayConfiguration.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/FlywayConfiguration.java
new file mode 100644
index 00000000000..6730240b890
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/FlywayConfiguration.java
@@ -0,0 +1,85 @@
+/**
+*
+* 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.config;
+
+import java.io.File;
+import javax.sql.DataSource;
+import org.flywaydb.core.Flyway;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+
+/**
+ * Configuration for Flyway database migrations.
+ *
+ *
All Airavata data is stored in a single unified 'airavata' database.
+ * Migration files are loaded from {airavataHome}/conf/db/migration/airavata/
+ * where {airavataHome} is resolved from:
+ *
+ *
System property -Dairavata.home=XXX (highest priority)
+ *
airavata.home property in application.properties (if set and non-empty)
Flyway can be disabled via airavata.flyway.enabled=false property.
+ */
+@Configuration
+@ConditionalOnProperty(prefix = "airavata.flyway", name = "enabled", havingValue = "true")
+public class FlywayConfiguration {
+
+ private static final Logger logger = LoggerFactory.getLogger(FlywayConfiguration.class);
+
+ /**
+ * Get the migration location path for the unified database.
+ */
+ private static String getMigrationLocation() {
+ var configDir = ConfigResolver.getConfigDir();
+ var migrationPath =
+ configDir + File.separator + "db" + File.separator + "migration" + File.separator + "airavata";
+ logger.debug("Flyway migration location: {}", migrationPath);
+ return "filesystem:" + migrationPath;
+ }
+
+ /**
+ * Configure Flyway for the unified Airavata database.
+ */
+ @Bean(name = "flyway", initMethod = "migrate")
+ @DependsOn("dataSource")
+ public Flyway flyway(DataSource dataSource) {
+ return Flyway.configure()
+ .dataSource(dataSource)
+ .locations(getMigrationLocation())
+ .baselineOnMigrate(true)
+ .validateOnMigrate(true)
+ .load();
+ }
+
+ /**
+ * Flyway migration strategy bean for Spring Boot autoconfiguration.
+ */
+ @Bean
+ public FlywayMigrationStrategy flywayMigrationStrategy() {
+ return Flyway::migrate;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/JacksonConfiguration.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/JacksonConfiguration.java
new file mode 100644
index 00000000000..cc5b76f146e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/JacksonConfiguration.java
@@ -0,0 +1,58 @@
+/**
+*
+* 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.config;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+/**
+ * Jackson ObjectMapper configuration.
+ * Provides a single, properly configured ObjectMapper bean for the entire application.
+ *
+ * All JSON serialization/deserialization should use this bean instead of creating
+ * new ObjectMapper instances.
+ */
+@Configuration
+public class JacksonConfiguration {
+
+ /**
+ * Primary ObjectMapper bean with standard configuration.
+ * Features:
+ * - Java 8 date/time support
+ * - ISO date format (not timestamps)
+ * - Lenient deserialization (unknown properties ignored)
+ */
+ @Bean
+ @Primary
+ public ObjectMapper objectMapper() {
+ return JsonMapper.builder()
+ .addModule(new JavaTimeModule())
+ .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+ .build();
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/JpaConfiguration.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/JpaConfiguration.java
new file mode 100644
index 00000000000..a0fd38f9d6c
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/JpaConfiguration.java
@@ -0,0 +1,134 @@
+/**
+*
+* 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.config;
+
+import com.zaxxer.hikari.HikariDataSource;
+import jakarta.persistence.EntityManagerFactory;
+import java.util.HashMap;
+import javax.sql.DataSource;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.env.Environment;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * JPA configuration for Airavata using a single unified 'airavata' database.
+ *
+ *
All entities (profiles, registry, sharing, credentials) are stored in
+ * one database, eliminating the need for multiple data sources.
+ */
+@Configuration
+@EnableTransactionManagement
+@EnableJpaAuditing
+@EnableJpaRepositories(
+ basePackages = {
+ "org.apache.airavata.research.experiment.repository",
+ "org.apache.airavata.compute.resource.repository",
+ "org.apache.airavata.accounting.repository",
+ "org.apache.airavata.execution.process",
+ "org.apache.airavata.execution.orchestration",
+ "org.apache.airavata.research.application.repository",
+ "org.apache.airavata.gateway.repository",
+ "org.apache.airavata.iam.repository",
+ "org.apache.airavata.workflow.repository",
+ "org.apache.airavata.status.repository",
+ "org.apache.airavata.credential.repository",
+ "org.apache.airavata.research.artifact.repository",
+ "org.apache.airavata.research.project.repository",
+ "org.apache.airavata.research.session.repository",
+ "org.apache.airavata.agent.repository",
+ "org.apache.airavata.research.repository"
+ })
+public class JpaConfiguration {
+
+ private static final String[] ENTITY_PACKAGES = {
+ "org.apache.airavata.research.experiment.entity",
+ "org.apache.airavata.compute.resource.entity",
+ "org.apache.airavata.accounting.entity",
+ "org.apache.airavata.execution.process",
+ "org.apache.airavata.execution.orchestration",
+ "org.apache.airavata.research.application.entity",
+ "org.apache.airavata.gateway.entity",
+ "org.apache.airavata.iam.entity",
+ "org.apache.airavata.workflow.entity",
+ "org.apache.airavata.status.entity",
+ "org.apache.airavata.credential.entity",
+ "org.apache.airavata.research.artifact.entity",
+ "org.apache.airavata.research.project.entity",
+ "org.apache.airavata.research.session.entity",
+ "org.apache.airavata.agent.entity",
+ "org.apache.airavata.research.entity"
+ };
+
+ @Bean
+ @Primary
+ @ConfigurationProperties("spring.datasource")
+ public DataSourceProperties dataSourceProperties() {
+ return new DataSourceProperties();
+ }
+
+ @Bean
+ @Profile("!test")
+ public DataSource dataSource(DataSourceProperties properties) {
+ return properties
+ .initializeDataSourceBuilder()
+ .type(HikariDataSource.class)
+ .build();
+ }
+
+ @Bean
+ public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, Environment env) {
+ var em = new LocalContainerEntityManagerFactoryBean();
+ em.setDataSource(dataSource);
+ em.setPackagesToScan(ENTITY_PACKAGES);
+ em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
+
+ // Set JPA properties from environment
+ var properties = new HashMap();
+
+ // Core Hibernate properties
+ var ddlAuto = env.getProperty("spring.jpa.hibernate.ddl-auto", "none");
+ properties.put("hibernate.hbm2ddl.auto", ddlAuto);
+
+ boolean showSql = env.getProperty("spring.jpa.show-sql", Boolean.class, false);
+ properties.put("hibernate.show_sql", showSql);
+
+ boolean formatSql = env.getProperty("spring.jpa.properties.hibernate.format_sql", Boolean.class, false);
+ properties.put("hibernate.format_sql", formatSql);
+
+ em.setJpaPropertyMap(properties);
+ return em;
+ }
+
+ @Bean
+ public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
+ return new JpaTransactionManager(entityManagerFactory);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/PropertiesValidationConfiguration.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/PropertiesValidationConfiguration.java
new file mode 100644
index 00000000000..07f7b5e166c
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/PropertiesValidationConfiguration.java
@@ -0,0 +1,155 @@
+/**
+*
+* 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.config;
+
+import jakarta.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Validates all Airavata configuration properties at startup.
+ *
+ *
Provides argparse-like behavior: fails fast with clear error messages
+ * if required properties are missing or invalid.
+ *
+ *
This configuration runs at application startup and logs all property
+ * values (redacting sensitive ones) for debugging purposes.
+ */
+@Configuration
+@org.springframework.context.annotation.Profile("!test")
+@org.springframework.core.annotation.Order(org.springframework.core.Ordered.HIGHEST_PRECEDENCE)
+public class PropertiesValidationConfiguration {
+
+ private static final Logger logger = LoggerFactory.getLogger(PropertiesValidationConfiguration.class);
+
+ private final ServerProperties properties;
+
+ public PropertiesValidationConfiguration(ServerProperties properties) {
+ this.properties = properties;
+ // Validate immediately on construction - fail fast
+ if (properties == null) {
+ throw new IllegalStateException("CRITICAL: ServerProperties bean is null. "
+ + "This usually means @EnableConfigurationProperties is not set or "
+ + "the properties file is not being loaded correctly.");
+ }
+ }
+
+ @PostConstruct
+ public void validateProperties() {
+ logger.info("=== Airavata Configuration Validation ===");
+
+ var errors = new ArrayList();
+ var warnings = new ArrayList();
+
+ // Validate top-level properties (all under airavata.* prefix)
+ validateNotNull(properties.security(), "airavata.security", errors);
+ validateNotNull(properties.services(), "airavata.services", errors);
+
+ // Validate security properties
+ if (properties.security() != null) {
+ if (properties.security().iam() != null
+ && properties.security().iam().enabled()) {
+ validateNotEmpty(properties.security().iam().serverUrl(), "airavata.security.iam.server-url", errors);
+ }
+ }
+
+ // Log all warnings
+ for (String warning : warnings) {
+ logger.warn("Config warning: {}", warning);
+ }
+
+ // If there are validation errors, fail fast
+ if (!errors.isEmpty()) {
+ var sb = new StringBuilder("\n\n");
+ sb.append("=== CONFIGURATION VALIDATION FAILED ===\n");
+ sb.append("The following required properties are missing or invalid:\n\n");
+ for (String error : errors) {
+ sb.append(" - ").append(error).append("\n");
+ }
+ sb.append("\nPlease ensure all required properties are set in application.properties.\n");
+ sb.append("================================================\n");
+
+ throw new IllegalStateException(sb.toString());
+ }
+
+ // Log successful validation
+ logger.info("Configuration validation passed. Logging active configuration:");
+ logConfiguration();
+ }
+
+ private void validateNotNull(Object value, String propertyPath, List errors) {
+ if (value == null) {
+ errors.add(propertyPath + " is required but not set");
+ }
+ }
+
+ private void validateNotEmpty(String value, String propertyPath, List errors) {
+ if (value == null || value.isBlank()) {
+ errors.add(propertyPath + " is required but empty or not set");
+ }
+ }
+
+ private void logConfiguration() {
+ // Log service enablement
+ if (properties.services() != null) {
+ var services = properties.services();
+ logger.info(" Services:");
+ logger.info(
+ " rest.enabled = {}",
+ services.rest() != null && services.rest().enabled());
+ logger.info(
+ " controller.enabled = {}",
+ services.controller() != null && services.controller().enabled());
+ logger.info(
+ " participant.enabled = {}",
+ services.participant() != null && services.participant().enabled());
+ if (services.monitor() != null) {
+ if (services.monitor().email() != null) {
+ logger.info(
+ " monitor.email.enabled = {}",
+ services.monitor().email().enabled());
+ }
+ if (services.monitor().compute() != null) {
+ logger.info(
+ " monitor.compute.enabled = {}",
+ services.monitor().compute().enabled());
+ }
+ }
+ }
+
+ // Log security settings (redacted)
+ if (properties.security() != null) {
+ logger.info(" Security:");
+ if (properties.security().tls() != null) {
+ logger.info(" tls.enabled = {}", properties.security().tls().enabled());
+ }
+ if (properties.security().iam() != null) {
+ logger.info(" iam.enabled = {}", properties.security().iam().enabled());
+ logger.info(
+ " iam.server-url = {}", properties.security().iam().serverUrl());
+ }
+ }
+
+ logger.info("=== Configuration Logging Complete ===");
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/RestClientConfiguration.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/RestClientConfiguration.java
new file mode 100644
index 00000000000..add36bd2f8c
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/RestClientConfiguration.java
@@ -0,0 +1,163 @@
+/**
+*
+* 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.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.security.KeyStore;
+import java.time.Duration;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * REST client configuration for Spring RestTemplate.
+ * Provides a configured RestTemplate bean with proper timeouts and SSL support.
+ *
+ * Configure via application.properties:
+ * security.tls.enabled=true
+ * security.tls.keystore.path=keystores/airavata.p12
+ * security.tls.keystore.password=secret
+ */
+@Configuration
+public class RestClientConfiguration {
+
+ private static final Logger logger = LoggerFactory.getLogger(RestClientConfiguration.class);
+
+ private final ServerProperties properties;
+
+ public RestClientConfiguration(ServerProperties properties) {
+ this.properties = properties;
+ }
+
+ /**
+ * Primary RestTemplate bean with connection pooling and SSL support.
+ */
+ @Bean
+ @Primary
+ public RestTemplate restTemplate(RestTemplateBuilder builder) {
+ var restTemplate = builder.connectTimeout(Duration.ofSeconds(30))
+ .readTimeout(Duration.ofSeconds(60))
+ .build();
+
+ // Configure with Apache HttpClient 5 if SSL is needed
+ try {
+ configureSSL(restTemplate);
+ } catch (Exception e) {
+ logger.warn("Could not configure SSL for RestTemplate: {}", e.getMessage());
+ }
+
+ return restTemplate;
+ }
+
+ /**
+ * Configure SSL for RestTemplate using Java's standard SSLContext.
+ */
+ private void configureSSL(RestTemplate restTemplate) throws Exception {
+ // Check if security configuration is available
+ if (properties.security() == null
+ || properties.security().tls() == null
+ || properties.security().tls().keystore() == null) {
+ // Use default trust store with self-signed certificate support for development/test
+ SSLContext sslContext = createTrustAllSSLContext();
+ configureHttpClient(restTemplate, sslContext);
+ return;
+ }
+
+ var trustStorePath = properties.security().tls().keystore().path();
+ var trustStorePassword = properties.security().tls().keystore().password();
+
+ SSLContext sslContext;
+
+ if (trustStorePath != null && !trustStorePath.isEmpty() && new File(trustStorePath).exists()) {
+ // Use configured trust store
+ var trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ try (var fis = new FileInputStream(trustStorePath)) {
+ trustStore.load(fis, trustStorePassword != null ? trustStorePassword.toCharArray() : null);
+ }
+
+ var trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(trustStore);
+ var trustManagers = trustManagerFactory.getTrustManagers();
+
+ sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, trustManagers, new java.security.SecureRandom());
+ } else {
+ // Use default trust store with self-signed certificate support for development
+ sslContext = createTrustAllSSLContext();
+ }
+
+ configureHttpClient(restTemplate, sslContext);
+ }
+
+ /**
+ * Create SSLContext that trusts all certificates (for development/test only).
+ */
+ private SSLContext createTrustAllSSLContext() throws Exception {
+ var trustAllCerts = new TrustManager[] {
+ new X509TrustManager() {
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+
+ @Override
+ public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
+
+ @Override
+ public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
+ }
+ };
+ var sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
+ return sslContext;
+ }
+
+ /**
+ * Configure RestTemplate with SSL context using Spring's SimpleClientHttpRequestFactory.
+ * Note: For production use with connection pooling, consider using Spring's WebClient
+ * or configuring HttpComponentsClientHttpRequestFactory via Spring Boot's auto-configuration.
+ */
+ private void configureHttpClient(RestTemplate restTemplate, SSLContext sslContext) {
+ javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
+
+ var requestFactory = new SimpleClientHttpRequestFactory();
+ requestFactory.setConnectTimeout(30000);
+ requestFactory.setReadTimeout(60000);
+ restTemplate.setRequestFactory(requestFactory);
+ }
+
+ /**
+ * RestTemplateBuilder bean for customizing RestTemplate instances.
+ */
+ @Bean
+ public RestTemplateBuilder restTemplateBuilder() {
+ return new RestTemplateBuilder().connectTimeout(Duration.ofSeconds(30)).readTimeout(Duration.ofSeconds(60));
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/ServerProperties.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/ServerProperties.java
new file mode 100644
index 00000000000..bd719dd53e4
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/ServerProperties.java
@@ -0,0 +1,237 @@
+/**
+*
+* 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.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Immutable Spring Boot configuration properties for Airavata server.
+ * All properties are loaded atomically at startup via constructor binding.
+ *
+ *
This record mirrors the property file structure exactly. Spring Boot's
+ * relaxed binding handles kebab-case to camelCase conversion automatically.
+ * All properties are prefixed with "airavata." in application.properties.
+ *
+ *
Properties should be set in application.properties. Runtime validation
+ * is performed by PropertiesValidationConfiguration for fail-fast behavior.
+ */
+@ConfigurationProperties(prefix = "airavata")
+public record ServerProperties(
+ // Core Airavata settings (formerly nested under airavata.*)
+ String home,
+ String defaultGateway,
+ boolean validationEnabled,
+ Sharing sharing,
+ int inMemoryCacheSize,
+ String localDataLocation,
+ long maxArchiveSize,
+ StreamingTransfer streamingTransfer,
+ Hibernate hibernate,
+ Cors cors,
+ // Subsystem configuration
+ Security security,
+ Flyway flyway,
+ Services services) {
+ // ==================== Helper Methods ====================
+
+ /**
+ * Safely check if sharing is enabled.
+ * Returns false if sharing configuration is null or not enabled.
+ */
+ public boolean isSharingEnabled() {
+ return sharing != null && sharing.enabled();
+ }
+
+ // ==================== Core Airavata Settings ====================
+ public record Sharing(boolean enabled) {}
+
+ public record StreamingTransfer(boolean enabled) {}
+
+ public record Hibernate(String hbm2ddlAuto) {}
+
+ public record Cors(java.util.List allowedOrigins) {}
+
+ // ==================== Security Configuration ====================
+ public record Security(Tls tls, Authentication authentication, Iam iam, Vault vault) {
+ public record Tls(boolean enabled, int clientTimeout, Keystore keystore) {
+ public record Keystore(String path, String password) {}
+ }
+
+ public record Authentication(boolean enabled) {}
+
+ public record Iam(
+ boolean enabled,
+ String serverUrl,
+ String realm,
+ String oauthClientId,
+ String oauthClientSecret,
+ Super superAdmin) {
+ public record Super(String username, String password) {}
+ }
+
+ public record Vault(Keystore keystore) {
+ public record Keystore(String url, String password, String alias) {}
+ }
+ }
+
+ // ==================== Flyway Configuration ====================
+ public record Flyway(boolean enabled) {}
+
+ // ==================== Services Configuration ====================
+ // Note: HTTP port uses standard server.port, gRPC port uses spring.grpc.server.port.
+ // Vault keystore uses airavata.security.vault.keystore.* (no per-service duplication).
+ public record Services(
+ Rest rest,
+ Participant participant,
+ Controller controller,
+ Scheduler scheduler,
+ Monitor monitor,
+ Sharing sharing,
+ Registry registry,
+ Research research,
+ Agent agent,
+ Fileserver fileserver,
+ Telemetry telemetry,
+ Dbus dbus) {
+
+ public record Rest(boolean enabled) {}
+
+ public record Participant(boolean enabled) {}
+
+ public record Controller(boolean enabled) {}
+
+ public record Scheduler(
+ Interpreter interpreter,
+ Rescheduler rescheduler,
+ double clusterScanningInterval,
+ double jobScanningInterval,
+ int clusterScanningParallelJobs,
+ int maximumReschedulerThreshold,
+ /** Semantic key for @ConditionalOnProperty matching - e.g. "default", "multiple" */
+ String selectionPolicy,
+ /** Semantic key for @ConditionalOnProperty matching - e.g. "exponential-backoff" */
+ String reschedulerPolicy) {
+ public record Interpreter(boolean enabled) {}
+
+ public record Rescheduler(boolean enabled) {}
+ }
+
+ public record Monitor(Email email, Realtime realtime, Compute compute) {
+ public record Email(
+ boolean enabled,
+ String address,
+ String folderName,
+ String host,
+ String password,
+ String storeProtocol,
+ int period,
+ int connectionRetryInterval,
+ int expiryMins) {}
+
+ /** Realtime: In-process delivery via Spring ApplicationEvents. */
+ public record Realtime(boolean enabled) {}
+
+ /** Compute: monitoring configuration; job-status-callback-url for job script curl when set. */
+ public record Compute(
+ boolean enabled,
+ String emailPublisherId,
+ String realtimePublisherId,
+ Notification notification,
+ String jobStatusCallbackUrl,
+ int clusterCheckTimeWindow,
+ int clusterCheckRepeatTime) {
+ public record Notification(String emailIds) {}
+ }
+ }
+
+ public record Sharing(boolean enabled) {}
+
+ public record Registry(boolean enabled) {}
+
+ public record Research(
+ boolean enabled, Grpc grpc, Hub hub, Portal portal, Spring spring, Springdoc springdoc, Openid openid) {
+ public record Grpc(
+ String keepaliveTime,
+ String keepaliveTimeout,
+ boolean permitKeepaliveWithoutCalls,
+ long maxInboundMessageSize) {}
+
+ public record Hub(String adminApiKey, int limit, String url) {}
+
+ public record Portal(String devUrl, String url) {}
+
+ public record Spring(Servlet servlet) {
+ public record Servlet(Multipart multipart) {
+ public record Multipart(String maxFileSize, String maxRequestSize) {}
+ }
+ }
+
+ public record Springdoc(ApiDocs apiDocs, SwaggerUi swaggerUi) {
+ public record ApiDocs(boolean enabled) {}
+
+ public record SwaggerUi(
+ String docExpansion, String operationsSorter, String path, String tagsSorter, Oauth oauth) {
+ public record Oauth(String clientId, boolean usePkceWithAuthorizationCodeGrant) {}
+ }
+ }
+
+ public record Openid(String url) {}
+ }
+
+ public record Agent(
+ boolean enabled,
+ Appinterface appinterface,
+ Grpc grpc,
+ Spring spring,
+ Storage storage,
+ Tunnelserver tunnelserver) {
+ public record Appinterface(String id) {}
+
+ public record Grpc(long maxInboundMessageSize) {}
+
+ public record Spring(Jpa jpa, Servlet servlet) {
+ public record Jpa(Hibernate hibernate, boolean openInView) {
+ public record Hibernate(String ddlAuto) {}
+ }
+
+ public record Servlet(Multipart multipart) {
+ public record Multipart(String maxFileSize, String maxRequestSize) {}
+ }
+ }
+
+ public record Storage(String id, String path) {}
+
+ public record Tunnelserver(String url, String host, int port, String token) {}
+ }
+
+ public record Fileserver(boolean enabled, Spring spring) {
+
+ public record Spring(Servlet servlet) {
+ public record Servlet(Multipart multipart) {
+ public record Multipart(String maxFileSize, String maxRequestSize) {}
+ }
+ }
+ }
+
+ public record Telemetry(boolean enabled) {}
+
+ public record Dbus(boolean enabled) {}
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/ServiceConditionals.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/ServiceConditionals.java
new file mode 100644
index 00000000000..8a6470062f9
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/ServiceConditionals.java
@@ -0,0 +1,71 @@
+/**
+*
+* 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.config;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Profile;
+
+/**
+ * Service role conditionals. Use nested annotations for controller, monitor, and participant.
+ */
+public final class ServiceConditionals {
+
+ private ServiceConditionals() {}
+
+ /**
+ * Composite annotation for Dapr controller components.
+ * Combines @Profile and @ConditionalOnProperty for controller enablement.
+ * Active when "test" profile is NOT active OR "orchestrator-integration" profile IS active.
+ */
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ @Profile({"!test", "orchestrator-integration"})
+ @ConditionalOnProperty(prefix = "airavata.services.controller", name = "enabled", havingValue = "true")
+ public @interface ConditionalOnController {}
+
+ /**
+ * Composite annotation for compute monitor components.
+ * Combines @Profile and @ConditionalOnProperty for monitor enablement.
+ */
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ @Profile("!test")
+ @ConditionalOnProperty(prefix = "airavata.services.monitor.compute", name = "enabled", havingValue = "true")
+ public @interface ConditionalOnMonitor {}
+
+ /**
+ * Composite annotation for Dapr participant components.
+ * Combines @Profile and @ConditionalOnProperty for participant enablement.
+ * Active when "test" profile is NOT active OR "orchestrator-integration" profile IS active.
+ */
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ @Profile({"!test", "orchestrator-integration"})
+ @ConditionalOnProperty(prefix = "airavata.services.participant", name = "enabled", havingValue = "true")
+ public @interface ConditionalOnParticipant {}
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/WebSecurityConfiguration.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/WebSecurityConfiguration.java
new file mode 100644
index 00000000000..6b5893d385e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/config/WebSecurityConfiguration.java
@@ -0,0 +1,92 @@
+/**
+*
+* 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.config;
+
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
+import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
+import org.springframework.security.web.SecurityFilterChain;
+
+/**
+ * Spring Security configuration for the Airavata API.
+ * When a JwtDecoder is available (Keycloak JWKS configured), enforces JWT authentication
+ * on /api/v1/** endpoints. When no JwtDecoder is available (tests), permits all requests.
+ */
+@Configuration
+@EnableWebSecurity
+public class WebSecurityConfiguration {
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http, ObjectProvider jwtDecoderProvider)
+ throws Exception {
+ http.csrf(csrf -> csrf.disable())
+ .cors(cors -> {})
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .formLogin(form -> form.disable())
+ .httpBasic(basic -> basic.disable());
+
+ JwtDecoder decoder = jwtDecoderProvider.getIfAvailable();
+ if (decoder != null) {
+ http.authorizeHttpRequests(auth -> auth.requestMatchers("/actuator/**", "/swagger-ui/**", "/v3/api-docs/**")
+ .permitAll()
+ .requestMatchers("/api/v1/health", "/api/v1/auth/**")
+ .permitAll()
+ .requestMatchers("/api/v1/monitoring/**")
+ .permitAll()
+ .requestMatchers("/api/v1/artifacts/public/**", "/api/v1/artifacts/search")
+ .permitAll()
+ .requestMatchers(
+ "/api/v1/research/artifacts/public/**", "/api/v1/research/artifacts/search")
+ .permitAll()
+ .requestMatchers("/api/v1/config")
+ .permitAll()
+ .requestMatchers("/api/v1/**")
+ .authenticated()
+ .anyRequest()
+ .permitAll())
+ .oauth2ResourceServer(
+ oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(keycloakJwtConverter())));
+ } else {
+ // No JwtDecoder — permit all (tests without Keycloak)
+ http.authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
+ }
+
+ return http.build();
+ }
+
+ @Bean
+ public Converter keycloakJwtConverter() {
+ var converter = new JwtAuthenticationConverter();
+ var authoritiesConverter = new JwtGrantedAuthoritiesConverter();
+ authoritiesConverter.setAuthoritiesClaimName("realm_access.roles");
+ authoritiesConverter.setAuthorityPrefix("ROLE_");
+ converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
+ return converter;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/CoreExceptions.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/CoreExceptions.java
new file mode 100644
index 00000000000..73837c9c3f8
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/CoreExceptions.java
@@ -0,0 +1,171 @@
+/**
+*
+* 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.core.exception;
+
+/**
+ * Core exceptions for Airavata.
+ */
+public final class CoreExceptions {
+
+ private CoreExceptions() {}
+
+ public static class AiravataException extends Exception {
+ private static final long serialVersionUID = -5665822765183116821L;
+
+ public AiravataException() {}
+
+ public AiravataException(String message, Throwable e) {
+ super(message, e);
+ }
+
+ public AiravataException(String message) {
+ super(message);
+ }
+ }
+
+ public static class AiravataClientException extends Exception {
+ private static final long serialVersionUID = 1L;
+ private String parameter;
+
+ public AiravataClientException() {
+ super();
+ }
+
+ public AiravataClientException(String message) {
+ super(message);
+ }
+
+ public AiravataClientException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public AiravataClientException(String message, String parameter) {
+ super(message);
+ this.parameter = parameter;
+ }
+
+ public String getParameter() {
+ return parameter;
+ }
+
+ public void setParameter(String parameter) {
+ this.parameter = parameter;
+ }
+ }
+
+ public static class AiravataConfigurationException extends AiravataException {
+ private static final long serialVersionUID = -9124231436834631249L;
+
+ public AiravataConfigurationException() {}
+
+ public AiravataConfigurationException(String message) {
+ this(message, null);
+ }
+
+ public AiravataConfigurationException(String message, Throwable e) {
+ super(message, e);
+ }
+ }
+
+ public static class AiravataSystemException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public AiravataSystemException() {
+ super();
+ }
+
+ public AiravataSystemException(String message) {
+ super(message);
+ }
+
+ public AiravataSystemException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ public static class AiravataStartupException extends Exception {
+ private static final long serialVersionUID = 495204868100143133L;
+
+ public AiravataStartupException() {
+ super();
+ }
+
+ public AiravataStartupException(String message) {
+ super(message);
+ }
+
+ public AiravataStartupException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public AiravataStartupException(Throwable cause) {
+ super(cause);
+ }
+
+ protected AiravataStartupException(
+ String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+ }
+
+ public static class InvalidRequestException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidRequestException() {
+ super();
+ }
+
+ public InvalidRequestException(String message) {
+ super(message);
+ }
+
+ public InvalidRequestException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ public static class ApplicationSettingsException extends AiravataException {
+ private static final long serialVersionUID = -4901850535475160411L;
+
+ public ApplicationSettingsException(String message) {
+ super(message);
+ }
+
+ public ApplicationSettingsException(String message, Throwable e) {
+ super(message, e);
+ }
+ }
+
+ public static class TimedOutException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public TimedOutException() {
+ super();
+ }
+
+ public TimedOutException(String message) {
+ super(message);
+ }
+
+ public TimedOutException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/DuplicateEntryException.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/DuplicateEntryException.java
new file mode 100644
index 00000000000..05c16964c0c
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/DuplicateEntryException.java
@@ -0,0 +1,39 @@
+/**
+*
+* 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.core.exception;
+
+/**
+ * Domain exception: DuplicateEntryException
+ */
+public class DuplicateEntryException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DuplicateEntryException() {
+ super();
+ }
+
+ public DuplicateEntryException(String message) {
+ super(message);
+ }
+
+ public DuplicateEntryException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/RegistryExceptions.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/RegistryExceptions.java
new file mode 100644
index 00000000000..05ee202b01e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/RegistryExceptions.java
@@ -0,0 +1,110 @@
+/**
+*
+* 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.core.exception;
+
+/**
+ * Registry-related exceptions. Use nested classes for type-safe catch and clarity.
+ */
+public final class RegistryExceptions {
+
+ private RegistryExceptions() {}
+
+ public static class AppRegistryException extends Exception {
+ private static final long serialVersionUID = -2849422320139467602L;
+
+ public AppRegistryException(Throwable e) {
+ super(e);
+ }
+
+ public AppRegistryException(String message) {
+ super(message, null);
+ }
+
+ public AppRegistryException(String message, Throwable e) {
+ super(message, e);
+ }
+ }
+
+ public static class ExperimentRegistryException extends Exception {
+ private static final long serialVersionUID = -2849422320139467602L;
+
+ public ExperimentRegistryException(Throwable e) {
+ super(e);
+ }
+
+ public ExperimentRegistryException(String message) {
+ super(message, null);
+ }
+
+ public ExperimentRegistryException(String message, Throwable e) {
+ super(message, e);
+ }
+ }
+
+ /**
+ * Exception thrown by registry service operations.
+ */
+ public static class RegistryException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public RegistryException() {
+ super();
+ }
+
+ public RegistryException(String message) {
+ super(message);
+ }
+
+ public RegistryException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ public static class ReplicaRegistryException extends RegistryException {
+
+ public ReplicaRegistryException(Throwable e) {
+ super(e.getMessage(), e);
+ }
+
+ public ReplicaRegistryException(String message) {
+ super(message, null);
+ }
+
+ public ReplicaRegistryException(String message, Throwable e) {
+ super(message, e);
+ }
+ }
+
+ public static class WorkflowRegistryException extends Exception {
+ private static final long serialVersionUID = -2849422320139467602L;
+
+ public WorkflowRegistryException(Throwable e) {
+ super(e);
+ }
+
+ public WorkflowRegistryException(String message) {
+ super(message, null);
+ }
+
+ public WorkflowRegistryException(String message, Throwable e) {
+ super(message, e);
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/TaskFailureException.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/TaskFailureException.java
new file mode 100644
index 00000000000..63270ffbf2a
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/TaskFailureException.java
@@ -0,0 +1,46 @@
+/**
+*
+* 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.core.exception;
+
+public class TaskFailureException extends Exception {
+
+ private String reason;
+ private boolean critical;
+ private Throwable e;
+
+ public TaskFailureException(String reason, boolean critical, Throwable e) {
+ super(reason, e);
+ this.reason = reason;
+ this.critical = critical;
+ this.e = e;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+ public boolean isCritical() {
+ return critical;
+ }
+
+ public Throwable getError() {
+ return e;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/ValidationExceptions.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/ValidationExceptions.java
new file mode 100644
index 00000000000..b2a21f517d3
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/exception/ValidationExceptions.java
@@ -0,0 +1,203 @@
+/**
+*
+* 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.core.exception;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.airavata.core.util.LoggingUtil;
+import org.slf4j.Logger;
+
+/**
+ * Validation-related exceptions and utilities (ValidationResults, ValidatorResult,
+ * LaunchValidationException, ExceptionHandlerUtil).
+ */
+public final class ValidationExceptions {
+
+ private ValidationExceptions() {}
+
+ public static class ValidationResults extends Exception {
+ private static final long serialVersionUID = 1L;
+ private boolean validationState;
+ private List validationResultList;
+
+ public ValidationResults() {
+ super();
+ }
+
+ public ValidationResults(String message) {
+ super(message);
+ }
+
+ public ValidationResults(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ValidationResults(String message, boolean validationState, List validationResultList) {
+ super(message);
+ this.validationState = validationState;
+ this.validationResultList = validationResultList;
+ }
+
+ public boolean getValidationState() {
+ return validationState;
+ }
+
+ public void setValidationState(boolean validationState) {
+ this.validationState = validationState;
+ }
+
+ public List getValidationResultList() {
+ return validationResultList;
+ }
+
+ public void setValidationResultList(List validationResultList) {
+ this.validationResultList = validationResultList;
+ }
+ }
+
+ public static class ValidatorResult extends Exception {
+ private static final long serialVersionUID = 1L;
+ private boolean result;
+ private String errorDetails;
+
+ public ValidatorResult() {
+ super();
+ }
+
+ public ValidatorResult(String message) {
+ super(message);
+ }
+
+ public ValidatorResult(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ValidatorResult(String message, boolean result, String errorDetails) {
+ super(message);
+ this.result = result;
+ this.errorDetails = errorDetails;
+ }
+
+ public boolean getResult() {
+ return result;
+ }
+
+ public void setResult(boolean result) {
+ this.result = result;
+ }
+
+ public String getErrorDetails() {
+ return errorDetails;
+ }
+
+ public void setErrorDetails(String errorDetails) {
+ this.errorDetails = errorDetails;
+ }
+ }
+
+ public static class LaunchValidationException extends Exception {
+ private static final long serialVersionUID = 1L;
+ private ValidationResults validationResult;
+ private String errorMessage;
+
+ public LaunchValidationException() {
+ super();
+ }
+
+ public LaunchValidationException(String message) {
+ super(message);
+ }
+
+ public LaunchValidationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public LaunchValidationException(String message, ValidationResults validationResult, String errorMessage) {
+ super(message);
+ this.validationResult = validationResult;
+ this.errorMessage = errorMessage;
+ }
+
+ public ValidationResults getValidationResult() {
+ return validationResult;
+ }
+
+ public void setValidationResult(ValidationResults validationResult) {
+ this.validationResult = validationResult;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+ }
+
+ public static final class ExceptionHandlerUtil {
+ private ExceptionHandlerUtil() {}
+
+ public static CoreExceptions.AiravataSystemException handleException(
+ Logger logger, String operation, Throwable exception) {
+ String message = String.format("[%s] failed: %s", operation, exception.getMessage());
+ LoggingUtil.logError(logger, operation, exception.getMessage(), exception);
+ return new CoreExceptions.AiravataSystemException(message, exception);
+ }
+
+ public static CoreExceptions.AiravataSystemException handleExceptionWithContext(
+ Logger logger, String operation, Throwable exception, Map context) {
+ LoggingUtil.logErrorWithContext(logger, operation, exception.getMessage(), exception, context);
+ var 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);
+ }
+ return new CoreExceptions.AiravataSystemException(message.toString(), exception);
+ }
+
+ public static CoreExceptions.AiravataSystemException handleExceptionWithMDC(
+ Logger logger,
+ String operation,
+ Throwable exception,
+ String experimentId,
+ String gatewayId,
+ String processId) {
+ LoggingUtil.setExperimentContext(experimentId, gatewayId, processId);
+ try {
+ return handleException(logger, operation, exception);
+ } finally {
+ LoggingUtil.clearContext();
+ }
+ }
+
+ public static CoreExceptions.AiravataSystemException wrapAsAiravataException(String message, Throwable cause) {
+ return new CoreExceptions.AiravataSystemException(message, cause);
+ }
+
+ public static CoreExceptions.AiravataSystemException createExceptionWithErrorCode(
+ String message, String errorCode, Throwable cause) {
+ String messageWithCode = String.format("Error Code: %s, %s", errorCode, message);
+ return wrapAsAiravataException(messageWithCode, cause);
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/mapper/EntityMapper.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/mapper/EntityMapper.java
new file mode 100644
index 00000000000..943de0a90b1
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/mapper/EntityMapper.java
@@ -0,0 +1,48 @@
+/**
+*
+* 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.core.mapper;
+
+import java.util.List;
+
+/**
+ * Generic contract for bidirectional entity-model mapping.
+ *
+ *
All domain mappers (whether MapStruct-generated or hand-written) should implement
+ * this interface so that services can depend on a uniform mapping contract.
+ *
+ * @param the JPA entity type
+ * @param the domain model type
+ */
+public interface EntityMapper {
+
+ M toModel(E entity);
+
+ E toEntity(M model);
+
+ default List toModelList(List entities) {
+ if (entities == null) return List.of();
+ return entities.stream().map(this::toModel).toList();
+ }
+
+ default List toEntityList(List models) {
+ if (models == null) return List.of();
+ return models.stream().map(this::toEntity).toList();
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/DagTaskResult.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/DagTaskResult.java
new file mode 100644
index 00000000000..e79f9354320
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/DagTaskResult.java
@@ -0,0 +1,62 @@
+/**
+*
+* 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.core.model;
+
+import java.util.Map;
+
+/**
+ * Result of a {@link org.apache.airavata.execution.dag.DagTask} execution.
+ *
+ *
Sealed to exactly two outcomes: {@link Success} or {@link Failure}.
+ * The DAG engine uses pattern matching to determine whether to follow
+ * the success or failure edge from the current node.
+ */
+public sealed interface DagTaskResult {
+
+ /**
+ * The task completed successfully.
+ *
+ * @param message human-readable description of what happened
+ * @param output key-value pairs to merge into the DAG state map,
+ * making them available to downstream tasks
+ */
+ record Success(String message, Map output) implements DagTaskResult {
+ public Success(String message) {
+ this(message, Map.of());
+ }
+ }
+
+ /**
+ * The task failed.
+ *
+ * @param reason human-readable failure description
+ * @param fatal if {@code true}, the failure is non-retryable
+ * @param cause optional underlying exception
+ */
+ record Failure(String reason, boolean fatal, Throwable cause) implements DagTaskResult {
+ public Failure(String reason, boolean fatal) {
+ this(reason, fatal, null);
+ }
+
+ public Failure(String reason) {
+ this(reason, false, null);
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/EntitySearchField.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/EntitySearchField.java
new file mode 100644
index 00000000000..22358a91a3e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/EntitySearchField.java
@@ -0,0 +1,36 @@
+/**
+*
+* 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.core.model;
+
+/**
+ * Domain enum: EntitySearchField
+ */
+public enum EntitySearchField {
+ NAME,
+ DESCRIPTION,
+ FULL_TEXT,
+ PARRENT_ENTITY_ID,
+ OWNER_ID,
+ PERMISSION_TYPE_ID,
+ CREATED_TIME,
+ UPDATED_TIME,
+ ENTITY_TYPE_ID,
+ SHARED_COUNT
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/ProcessState.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/ProcessState.java
new file mode 100644
index 00000000000..8cda4b8f1f3
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/ProcessState.java
@@ -0,0 +1,43 @@
+/**
+*
+* 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.core.model;
+
+/**
+ * Domain enum: ProcessState
+ */
+public enum ProcessState {
+ CREATED,
+ VALIDATED,
+ LAUNCHED,
+ PRE_PROCESSING,
+ CONFIGURING_WORKSPACE,
+ INPUT_DATA_STAGING,
+ EXECUTING,
+ MONITORING,
+ OUTPUT_DATA_STAGING,
+ POST_PROCESSING,
+ COMPLETED,
+ FAILED,
+ CANCELING,
+ CANCELED,
+ QUEUED,
+ DEQUEUING,
+ REQUEUED;
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/ResourceIdentifier.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/ResourceIdentifier.java
new file mode 100644
index 00000000000..42d9b692d49
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/ResourceIdentifier.java
@@ -0,0 +1,135 @@
+/**
+*
+* 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.core.model;
+
+import java.util.Objects;
+
+/**
+ * Unified identifier for resources in the Airavata hierarchy.
+ *
+ *
Replaces the former ProcessIdentifier, TaskIdentifier, and JobIdentifier matryoshka pattern
+ * with a single class using nullable fields and static factory methods.
+ */
+public class ResourceIdentifier {
+ private String jobId;
+ private String taskId;
+ private String processId;
+ private String experimentId;
+ private String gatewayId;
+
+ public ResourceIdentifier() {}
+
+ public static ResourceIdentifier forProcess(String processId, String experimentId, String gatewayId) {
+ var id = new ResourceIdentifier();
+ id.processId = processId;
+ id.experimentId = experimentId;
+ id.gatewayId = gatewayId;
+ return id;
+ }
+
+ public static ResourceIdentifier forTask(String taskId, String processId, String experimentId, String gatewayId) {
+ var id = new ResourceIdentifier();
+ id.taskId = taskId;
+ id.processId = processId;
+ id.experimentId = experimentId;
+ id.gatewayId = gatewayId;
+ return id;
+ }
+
+ public static ResourceIdentifier forJob(
+ String jobId, String taskId, String processId, String experimentId, String gatewayId) {
+ var id = new ResourceIdentifier();
+ id.jobId = jobId;
+ id.taskId = taskId;
+ id.processId = processId;
+ id.experimentId = experimentId;
+ id.gatewayId = gatewayId;
+ return id;
+ }
+
+ public String getJobId() {
+ return jobId;
+ }
+
+ public void setJobId(String jobId) {
+ this.jobId = jobId;
+ }
+
+ public String getTaskId() {
+ return taskId;
+ }
+
+ public void setTaskId(String taskId) {
+ this.taskId = taskId;
+ }
+
+ public String getProcessId() {
+ return processId;
+ }
+
+ public void setProcessId(String processId) {
+ this.processId = processId;
+ }
+
+ public String getExperimentId() {
+ return experimentId;
+ }
+
+ public void setExperimentId(String experimentId) {
+ this.experimentId = experimentId;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ResourceIdentifier that = (ResourceIdentifier) o;
+ return Objects.equals(jobId, that.jobId)
+ && Objects.equals(taskId, that.taskId)
+ && Objects.equals(processId, that.processId)
+ && Objects.equals(experimentId, that.experimentId)
+ && Objects.equals(gatewayId, that.gatewayId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(jobId, taskId, processId, experimentId, gatewayId);
+ }
+
+ @Override
+ public String toString() {
+ var sb = new StringBuilder("ResourceIdentifier{");
+ if (jobId != null) sb.append("jobId=").append(jobId).append(", ");
+ if (taskId != null) sb.append("taskId=").append(taskId).append(", ");
+ sb.append("processId=").append(processId);
+ sb.append(", experimentId=").append(experimentId);
+ sb.append(", gatewayId=").append(gatewayId);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/SearchCondition.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/SearchCondition.java
new file mode 100644
index 00000000000..3d601ad369c
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/SearchCondition.java
@@ -0,0 +1,32 @@
+/**
+*
+* 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.core.model;
+
+/**
+ * Domain enum: SearchCondition
+ */
+public enum SearchCondition {
+ EQUAL,
+ LIKE,
+ FULL_TEXT,
+ GTE,
+ LTE,
+ NOT
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/SearchCriteria.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/SearchCriteria.java
new file mode 100644
index 00000000000..61755b5e6af
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/SearchCriteria.java
@@ -0,0 +1,80 @@
+/**
+*
+* 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.core.model;
+
+import java.util.Objects;
+
+/**
+ * Domain model: SearchCriteria
+ */
+public class SearchCriteria {
+ private EntitySearchField searchField;
+ private String value;
+ private SearchCondition searchCondition;
+
+ public SearchCriteria() {}
+
+ public EntitySearchField getSearchField() {
+ return searchField;
+ }
+
+ public void setSearchField(EntitySearchField searchField) {
+ this.searchField = searchField;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public SearchCondition getSearchCondition() {
+ return searchCondition;
+ }
+
+ public void setSearchCondition(SearchCondition searchCondition) {
+ this.searchCondition = searchCondition;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SearchCriteria that = (SearchCriteria) o;
+ return Objects.equals(searchField, that.searchField)
+ && Objects.equals(value, that.value)
+ && Objects.equals(searchCondition, that.searchCondition);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(searchField, value, searchCondition);
+ }
+
+ @Override
+ public String toString() {
+ return "SearchCriteria{" + "searchField="
+ + searchField + ", " + "value="
+ + value + ", " + "searchCondition="
+ + searchCondition + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/StatusModel.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/StatusModel.java
new file mode 100644
index 00000000000..56ee1215f5e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/StatusModel.java
@@ -0,0 +1,118 @@
+/**
+*
+* 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.core.model;
+
+import java.util.Objects;
+import org.apache.airavata.core.util.IdGenerator;
+
+/**
+ * Generic domain model for status records.
+ *
+ *
Replaces the four type-specific status classes:
+ *
+ *
{@code ExperimentStatus} → {@code StatusModel}
+ *
{@code ProcessStatus} → {@code StatusModel}
+ *
{@code TaskStatus} → {@code StatusModel}
+ *
{@code JobStatus} → {@code StatusModel}
+ *
+ *
+ * @param the specific state enum type (must be an enum)
+ */
+public class StatusModel> {
+
+ private S state;
+ private long timeOfStateChange;
+ private String reason;
+ private String statusId;
+
+ public StatusModel() {}
+
+ public StatusModel(S state) {
+ this.state = state;
+ this.timeOfStateChange = System.currentTimeMillis();
+ }
+
+ public static > StatusModel of(S state) {
+ StatusModel m = new StatusModel<>();
+ m.state = state;
+ m.timeOfStateChange = IdGenerator.getCurrentTimestamp().toEpochMilli();
+ return m;
+ }
+
+ public static > StatusModel of(S state, String reason) {
+ StatusModel m = of(state);
+ m.reason = reason;
+ return m;
+ }
+
+ public S getState() {
+ return state;
+ }
+
+ public void setState(S state) {
+ this.state = state;
+ }
+
+ public long getTimeOfStateChange() {
+ return timeOfStateChange;
+ }
+
+ public void setTimeOfStateChange(long timeOfStateChange) {
+ this.timeOfStateChange = timeOfStateChange;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+
+ public String getStatusId() {
+ return statusId;
+ }
+
+ public void setStatusId(String statusId) {
+ this.statusId = statusId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ StatusModel> that = (StatusModel>) o;
+ return timeOfStateChange == that.timeOfStateChange
+ && Objects.equals(state, that.state)
+ && Objects.equals(reason, that.reason)
+ && Objects.equals(statusId, that.statusId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(state, timeOfStateChange, reason, statusId);
+ }
+
+ @Override
+ public String toString() {
+ return "StatusModel{" + "state=" + state + ", timeOfStateChange=" + timeOfStateChange + ", reason=" + reason
+ + ", statusId=" + statusId + "}";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/TaskState.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/TaskState.java
new file mode 100644
index 00000000000..49427ae019e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/model/TaskState.java
@@ -0,0 +1,31 @@
+/**
+*
+* 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.core.model;
+
+/**
+ * Domain enum: TaskState
+ */
+public enum TaskState {
+ CREATED,
+ EXECUTING,
+ COMPLETED,
+ FAILED,
+ CANCELED;
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/service/AbstractCrudService.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/service/AbstractCrudService.java
new file mode 100644
index 00000000000..43f9dd38f22
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/service/AbstractCrudService.java
@@ -0,0 +1,104 @@
+/**
+*
+* 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.core.service;
+
+import java.util.List;
+import org.apache.airavata.core.mapper.EntityMapper;
+import org.apache.airavata.core.util.IdGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Base implementation of {@link CrudService} that eliminates per-domain CRUD boilerplate.
+ *
+ *
Subclasses provide the concrete entity mapper, repository, and ID accessor hooks.
+ * Standard create/get/update/delete/list operations are handled here; domain-specific
+ * methods are added in the concrete service.
+ *
+ * @param the JPA entity type
+ * @param the domain model type
+ */
+@Transactional
+public abstract class AbstractCrudService implements CrudService {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ protected final JpaRepository repository;
+ protected final EntityMapper mapper;
+
+ protected AbstractCrudService(JpaRepository repository, EntityMapper mapper) {
+ this.repository = repository;
+ this.mapper = mapper;
+ }
+
+ /** Return the current ID value from the model (e.g. model.getResourceId()). */
+ protected abstract String getId(M model);
+
+ /** Set the ID on the model (e.g. model.setResourceId(id)). */
+ protected abstract void setId(M model, String id);
+
+ /** Return entities scoped to a gateway. Typically delegates to repository.findByGatewayId(). */
+ protected abstract List findByGateway(String gatewayId);
+
+ /** Return a human-readable name for this domain (e.g. "Resource", "Application"). Used in log messages. */
+ protected abstract String entityName();
+
+ @Override
+ public String create(M model) {
+ String id = IdGenerator.ensureId(getId(model));
+ setId(model, id);
+ E entity = mapper.toEntity(model);
+ repository.save(entity);
+ logger.debug("Created {} id={}", entityName(), id);
+ return id;
+ }
+
+ @Override
+ public M get(String id) {
+ return repository.findById(id).map(mapper::toModel).orElse(null);
+ }
+
+ @Override
+ public void update(String id, M model) {
+ if (!repository.existsById(id)) {
+ throw new IllegalArgumentException(entityName() + " not found: " + id);
+ }
+ setId(model, id);
+ E entity = mapper.toEntity(model);
+ repository.save(entity);
+ logger.debug("Updated {} id={}", entityName(), id);
+ }
+
+ @Override
+ public void delete(String id) {
+ if (!repository.existsById(id)) {
+ throw new IllegalArgumentException(entityName() + " not found: " + id);
+ }
+ repository.deleteById(id);
+ logger.debug("Deleted {} id={}", entityName(), id);
+ }
+
+ @Override
+ public List listByGateway(String gatewayId) {
+ return mapper.toModelList(findByGateway(gatewayId));
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/service/CrudService.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/service/CrudService.java
new file mode 100644
index 00000000000..fa1d6daaf7d
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/service/CrudService.java
@@ -0,0 +1,44 @@
+/**
+*
+* 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.core.service;
+
+import java.util.List;
+
+/**
+ * Generic CRUD contract for domain services that manage a single model type
+ * scoped to a gateway.
+ *
+ *
Services with richer APIs should extend this interface with additional
+ * domain-specific methods.
+ *
+ * @param the domain model type
+ */
+public interface CrudService {
+
+ String create(M model);
+
+ M get(String id);
+
+ void update(String id, M model);
+
+ void delete(String id);
+
+ List listByGateway(String gatewayId);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/telemetry/CounterMetric.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/telemetry/CounterMetric.java
new file mode 100644
index 00000000000..ff5f20c41cf
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/telemetry/CounterMetric.java
@@ -0,0 +1,117 @@
+/**
+*
+* 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.core.telemetry;
+
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tags;
+
+/**
+ * Counter metric wrapper using Micrometer.
+ * Spring Boot Actuator auto-configures the MeterRegistry.
+ */
+public class CounterMetric {
+
+ private final Counter counter;
+ private final MeterRegistry meterRegistry;
+ private final String monitorName;
+ private final String[] labelNames;
+
+ /**
+ * Legacy constructor for static field initialization.
+ * Uses the global MeterRegistry from MetricsFactory.
+ */
+ public CounterMetric(String monitorName) {
+ this(monitorName, MetricsFactory.getRegistry());
+ }
+
+ /**
+ * Legacy constructor with labels for static field initialization.
+ * Uses the global MeterRegistry from MetricsFactory.
+ */
+ public CounterMetric(String monitorName, String... labelNames) {
+ this(monitorName, MetricsFactory.getRegistry(), labelNames);
+ }
+
+ /**
+ * Constructor with explicit MeterRegistry.
+ */
+ public CounterMetric(String monitorName, MeterRegistry meterRegistry) {
+ this.meterRegistry = meterRegistry;
+ this.monitorName = monitorName;
+ this.labelNames = new String[0];
+ this.counter = Counter.builder(monitorName).description(monitorName).register(meterRegistry);
+ }
+
+ /**
+ * Constructor with explicit MeterRegistry and labels.
+ */
+ public CounterMetric(String monitorName, MeterRegistry meterRegistry, String... labelNames) {
+ this.meterRegistry = meterRegistry;
+ this.monitorName = monitorName;
+ this.labelNames = labelNames;
+ // For labeled counters, we don't pre-register - we create on demand
+ this.counter = null;
+ }
+
+ public void inc() {
+ if (counter != null) {
+ counter.increment();
+ }
+ }
+
+ public void inc(String... labelValues) {
+ if (labelNames.length > 0 && labelValues.length == labelNames.length) {
+ Tags tags = Tags.empty();
+ for (int i = 0; i < labelNames.length; i++) {
+ tags = tags.and(labelNames[i], labelValues[i]);
+ }
+ Counter.builder(monitorName)
+ .description(monitorName)
+ .tags(tags)
+ .register(meterRegistry)
+ .increment();
+ } else if (counter != null) {
+ counter.increment();
+ }
+ }
+
+ public void inc(double amount) {
+ if (counter != null) {
+ counter.increment(amount);
+ }
+ }
+
+ public void inc(double amount, String... labelValues) {
+ if (labelNames.length > 0 && labelValues.length == labelNames.length) {
+ Tags tags = Tags.empty();
+ for (int i = 0; i < labelNames.length; i++) {
+ tags = tags.and(labelNames[i], labelValues[i]);
+ }
+ Counter.builder(monitorName)
+ .description(monitorName)
+ .tags(tags)
+ .register(meterRegistry)
+ .increment(amount);
+ } else if (counter != null) {
+ counter.increment(amount);
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/telemetry/GaugeMetric.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/telemetry/GaugeMetric.java
new file mode 100644
index 00000000000..f289df4f453
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/telemetry/GaugeMetric.java
@@ -0,0 +1,74 @@
+/**
+*
+* 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.core.telemetry;
+
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Gauge metric wrapper using Micrometer.
+ * Spring Boot Actuator auto-configures the MeterRegistry.
+ */
+public class GaugeMetric {
+
+ private final AtomicLong value = new AtomicLong(0);
+
+ /**
+ * Legacy constructor for static field initialization.
+ * Uses the global MeterRegistry from MetricsFactory.
+ */
+ public GaugeMetric(String monitorName) {
+ this(monitorName, MetricsFactory.getRegistry());
+ }
+
+ /**
+ * Constructor with explicit MeterRegistry.
+ */
+ public GaugeMetric(String monitorName, MeterRegistry meterRegistry) {
+ Gauge.builder(monitorName, value, AtomicLong::get)
+ .description(monitorName)
+ .register(meterRegistry);
+ }
+
+ public void inc() {
+ value.addAndGet(1);
+ }
+
+ public void inc(long amount) {
+ value.addAndGet(amount);
+ }
+
+ public void dec() {
+ value.addAndGet(-1);
+ }
+
+ public void dec(long amount) {
+ value.addAndGet(-amount);
+ }
+
+ public void set(long newValue) {
+ value.set(newValue);
+ }
+
+ public double get() {
+ return value.get();
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/telemetry/MetricsFactory.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/telemetry/MetricsFactory.java
new file mode 100644
index 00000000000..2f4a221faf5
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/telemetry/MetricsFactory.java
@@ -0,0 +1,58 @@
+/**
+*
+* 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.core.telemetry;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+import jakarta.annotation.PostConstruct;
+import org.springframework.stereotype.Component;
+
+/**
+ * Factory for creating Micrometer-based metrics.
+ * Provides a global MeterRegistry that can be accessed from static contexts.
+ *
+ * Spring Boot auto-configures a CompositeMeterRegistry which is injected here
+ * and made available globally for legacy static metric usage.
+ */
+@Component
+public class MetricsFactory {
+
+ private static MeterRegistry globalRegistry = new SimpleMeterRegistry();
+
+ private final MeterRegistry meterRegistry;
+
+ public MetricsFactory(MeterRegistry meterRegistry) {
+ this.meterRegistry = meterRegistry;
+ }
+
+ @PostConstruct
+ public void init() {
+ // Set the Spring-managed registry as the global registry
+ globalRegistry = meterRegistry;
+ }
+
+ /**
+ * Get the global MeterRegistry.
+ * For static contexts where dependency injection is not available.
+ */
+ public static MeterRegistry getRegistry() {
+ return globalRegistry;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/Constants.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/Constants.java
new file mode 100644
index 00000000000..3105030f937
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/Constants.java
@@ -0,0 +1,93 @@
+/**
+*
+* 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.core.util;
+
+public class Constants {
+ public static final String USER_NAME = "userName";
+ public static final String GATEWAY_ID = "gatewayId";
+ public static final String API_METHOD_NAME = "apiMethodName";
+
+ public final class FieldConstants {
+ public final class ProjectConstants {
+ public static final String PROJECT_ID = "projectID";
+ public static final String GATEWAY_ID = "gateway_id";
+ public static final String OWNER = "owner";
+ public static final String PROJECT_NAME = "name";
+ public static final String DESCRIPTION = "description";
+ public static final String CREATION_TIME = "createdAt";
+ }
+
+ public final class ExperimentConstants {
+ public static final String EXPERIMENT_ID = "experimentId";
+ public static final String PROJECT_ID = "projectId";
+ public static final String GATEWAY_ID = "gatewayId";
+ public static final String EXPERIMENT_TYPE = "experimentType";
+ public static final String USER_NAME = "userName";
+ public static final String EXPERIMENT_NAME = "experimentName";
+ public static final String CREATION_TIME = "createdAt";
+ public static final String DESCRIPTION = "description";
+ public static final String EXECUTION_ID = "executionId";
+ public static final String GATEWAY_EXECUTION_ID = "gatewayExecutionId";
+ public static final String ENABLE_EMAIL_NOTIFICATION = "enableEmailNotification";
+ public static final String EMAIL_ADDRESSES = "emailAddresses";
+ public static final String EXPERIMENT_INPUTS = "experimentInputs";
+ public static final String EXPERIMENT_OUTPUTS = "experimentOutputs";
+ public static final String EXPERIMENT_STATUS = "experimentStatus";
+ public static final String EXPERIMENT_ERRORS = "experimentErrors";
+ public static final String USER_CONFIGURATION_DATA = "userConfigurationData";
+ public static final String FROM_DATE = "fromDate";
+ public static final String TO_DATE = "toDate";
+ public static final String RESOURCE_HOST_ID = "resourceHostId";
+ }
+
+ public final class UserConfigurationDataConstants {
+ public static final String EXPERIMENT_ID = "experimentId";
+ public static final String AIRAVATA_AUTO_SCHEDULE = "airavataAutoSchedule";
+ public static final String OVERRIDE_MANUAL_PARAMS = "overrideManualScheduledParams";
+ public static final String SHARE_EXP = "shareExperimentPublicly";
+ public static final String COMPUTATIONAL_RESOURCE_SCHEDULING = "computationalResourceScheduling";
+ }
+
+ public final class ProcessConstants {
+ public static final String EXPERIMENT_ID = "experimentId";
+ public static final String PROCESS_ID = "processId";
+ public static final String PROCESS_STATUS = "processStatus";
+ public static final String PROCESS_ERROR = "processError";
+ public static final String PROCESS_INPUTS = "processInputs";
+ public static final String PROCESS_OUTPUTS = "processOutputs";
+ public static final String PROCESS_RESOURCE_SCHEDULE = "processResourceSchedule";
+ public static final String PROCESS_WORKFLOW = "processWorkflow";
+ }
+
+ public final class TaskConstants {
+ public static final String PARENT_PROCESS_ID = "parentProcessId";
+ public static final String TASK_ID = "taskId";
+ public static final String TASK_STATUS = "taskStatus";
+ public static final String TASK_ERROR = "taskError";
+ }
+
+ public final class JobConstants {
+ public static final String JOB_ID = "jobId";
+ public static final String PROCESS_ID = "processId";
+ public static final String TASK_ID = "taskId";
+ public static final String JOB_STATUS = "taskStatus";
+ }
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/DBConstants.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/DBConstants.java
new file mode 100644
index 00000000000..c041be902f7
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/DBConstants.java
@@ -0,0 +1,46 @@
+/**
+*
+* 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.core.util;
+
+public class DBConstants {
+
+ public static class Experiment {
+ public static final String USER_NAME = "userName";
+ public static final String PROJECT_ID = "projectId";
+ public static final String GATEWAY_ID = "gatewayId";
+ public static final String EXPERIMENT_ID = "experimentId";
+ public static final String EXPERIMENT_NAME = "experimentName";
+ public static final String DESCRIPTION = "description";
+ public static final String EXECUTION_ID = "executionId";
+ public static final String CREATION_TIME = "createdAt";
+ public static final String RESOURCE_HOST_ID = "resourceHostId";
+ public static final String ACCESSIBLE_EXPERIMENT_IDS = "accessibleExperimentIds";
+ }
+
+ public static class ExperimentSummary {
+ public static final String EXPERIMENT_STATUS = "experimentStatus";
+ public static final String FROM_DATE = "fromDate";
+ public static final String TO_DATE = "toDate";
+ }
+
+ public static class Process {
+ public static final String EXPERIMENT_ID = "experimentId";
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/EnumUtil.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/EnumUtil.java
new file mode 100644
index 00000000000..76cfdc573c1
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/EnumUtil.java
@@ -0,0 +1,50 @@
+/**
+*
+* 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.core.util;
+
+/**
+ * Utility for safe enum conversions, replacing scattered try-catch valueOf patterns.
+ */
+public final class EnumUtil {
+
+ private EnumUtil() {}
+
+ /**
+ * Safely converts a string to an enum value, returning a default if the string
+ * is null, blank, or does not match any enum constant.
+ */
+ public static > E safeValueOf(Class enumClass, String value, E defaultValue) {
+ if (value == null || value.isBlank()) {
+ return defaultValue;
+ }
+ try {
+ return Enum.valueOf(enumClass, value);
+ } catch (IllegalArgumentException e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Safely converts a string to an enum value, returning null if no match.
+ */
+ public static > E safeValueOf(Class enumClass, String value) {
+ return safeValueOf(enumClass, value, null);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/IdGenerator.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/IdGenerator.java
new file mode 100644
index 00000000000..c233c636a02
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/IdGenerator.java
@@ -0,0 +1,123 @@
+/**
+*
+* 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.core.util;
+
+import java.time.Instant;
+import java.util.UUID;
+
+public class IdGenerator {
+
+ private static volatile long lastTimestampMillis = 0;
+ private static volatile int lastMicrosecondFraction = 0;
+ private static final Object timestampLock = new Object();
+
+ /**
+ * Gets the current timestamp with microsecond precision.
+ * This method uses getUniqueTimestamp() internally to ensure consistency
+ * and maximum precision across the codebase.
+ *
+ * @return An Instant with microsecond precision
+ */
+ public static Instant getCurrentTimestamp() {
+ return getUniqueTimestamp();
+ }
+
+ /**
+ * Converts a long time value to an Instant.
+ * If the time is 0 or negative, returns the current unique timestamp.
+ * Otherwise, creates an Instant from the provided time value.
+ *
+ * @param time Time in milliseconds since epoch
+ * @return An Instant object
+ */
+ public static Instant getTime(long time) {
+ if (time == 0 || time < 0) {
+ return getUniqueTimestamp();
+ }
+ return Instant.ofEpochMilli(time);
+ }
+
+ /**
+ * Gets a unique timestamp with microsecond precision that ensures each call returns a different value.
+ * Uses System.currentTimeMillis() with System.nanoTime() for precise microsecond tracking.
+ * The database column is TIMESTAMP(6) which supports microsecond precision.
+ *
+ * @return An Instant with microsecond precision that is guaranteed to be unique and monotonically increasing
+ */
+ public static Instant getUniqueTimestamp() {
+ synchronized (timestampLock) {
+ long currentTimeMillis = System.currentTimeMillis();
+ // Use nanoTime to get precise sub-millisecond timing
+ // nanoTime is relative, but we can use it to ensure uniqueness within the same millisecond
+ long nanoTime = System.nanoTime();
+ // Extract microsecond fraction (0-999) from nanoseconds
+ // We use modulo to get a value between 0 and 999999, then divide by 1000 to get microseconds
+ int microsecondFraction = (int) ((nanoTime % 1_000_000) / 1000);
+
+ // Ensure timestamp is always increasing, even if called in rapid succession
+ if (currentTimeMillis <= lastTimestampMillis) {
+ // Same millisecond, or real time hasn't caught up to our incremented value.
+ // Either way, keep incrementing from where we are.
+ if (currentTimeMillis == lastTimestampMillis && microsecondFraction > lastMicrosecondFraction) {
+ // nanoTime advanced within the same millisecond, use it
+ lastMicrosecondFraction = microsecondFraction;
+ } else {
+ // Increment manually to stay monotonic
+ lastMicrosecondFraction = (lastMicrosecondFraction + 1) % 1000;
+ if (lastMicrosecondFraction == 0) {
+ // Wrapped around all 1000 microseconds, increment millisecond
+ lastTimestampMillis++;
+ }
+ }
+ } else {
+ // New millisecond - use current microsecond fraction
+ lastTimestampMillis = currentTimeMillis;
+ lastMicrosecondFraction = microsecondFraction;
+ }
+
+ // Create Instant with microsecond precision
+ // Instant.ofEpochSecond takes seconds + nanoAdjustment
+ long epochSeconds = lastTimestampMillis / 1000;
+ int remainderMillisNanos = (int) (lastTimestampMillis % 1000) * 1_000_000;
+ int microsNanos = lastMicrosecondFraction * 1000;
+ return Instant.ofEpochSecond(epochSeconds, remainderMillisNanos + microsNanos);
+ }
+ }
+
+ /**
+ * Returns the given ID if it is non-null and non-blank, otherwise generates a new UUID.
+ *
+ *
Consolidates the repeated null-check + UUID pattern used across service create methods.
+ *
+ * @param id the candidate ID (may be null or blank)
+ * @return the original ID or a freshly generated UUID string
+ */
+ public static String ensureId(String id) {
+ if (id == null || id.isBlank()) {
+ return UUID.randomUUID().toString();
+ }
+ return id;
+ }
+
+ public static String getId(String name) {
+ String id = name.trim().replaceAll("\\s|\\.|/|\\\\", "_");
+ return id + "_" + UUID.randomUUID();
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/LoggingUtil.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/LoggingUtil.java
new file mode 100644
index 00000000000..7c97eb718c5
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/LoggingUtil.java
@@ -0,0 +1,166 @@
+/**
+*
+* 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.core.util;
+
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.MDC;
+
+/**
+ * MDC key constants for log correlation and tracing.
+ */
+final class MDCKeys {
+ static final String EXPERIMENT_ID = "experiment_id";
+ static final String GATEWAY_ID = "gateway_id";
+ static final String EXPERIMENT_NAME = "experiment_name";
+ static final String PROCESS_ID = "process_id";
+ static final String TOKEN_ID = "token_id";
+
+ private MDCKeys() {}
+}
+
+/**
+ * Utility class for standardized logging patterns across the Airavata API.
+ *
+ *
This class provides helper methods to ensure consistent logging:
+ *
+ *
Always include exceptions in error logs
+ *
Use structured logging with MDC for correlation IDs
+ */
+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 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, Map context) {
+ var 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());
+ }
+ }
+
+ /**
+ * 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(MDCKeys.EXPERIMENT_ID, experimentId);
+ }
+ if (gatewayId != null) {
+ MDC.put(MDCKeys.GATEWAY_ID, gatewayId);
+ }
+ if (processId != null) {
+ MDC.put(MDCKeys.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 mdc = MDC.getCopyOfContextMap();
+ return () -> {
+ Map 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/modules/airavata-api/src/main/java/org/apache/airavata/core/util/PaginationUtil.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/PaginationUtil.java
new file mode 100644
index 00000000000..56aaa3aa2d4
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/core/util/PaginationUtil.java
@@ -0,0 +1,46 @@
+/**
+*
+* 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.core.util;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * Consolidates the repeated limit/offset-to-{@link Pageable} conversion pattern
+ * used across services that support paginated listing.
+ */
+public final class PaginationUtil {
+
+ private PaginationUtil() {}
+
+ /**
+ * Converts a limit/offset pair to a Spring Data {@link Pageable}.
+ *
+ *
Guards against zero or negative limits to prevent division by zero.
+ *
+ * @param limit the maximum number of results per page (clamped to at least 1)
+ * @param offset the zero-based item offset
+ * @return a PageRequest suitable for Spring Data repository methods
+ */
+ public static Pageable toPageRequest(int limit, int offset) {
+ int safeLimit = Math.max(limit, 1);
+ return PageRequest.of(offset / safeLimit, safeLimit);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/entity/CredentialEntity.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/entity/CredentialEntity.java
new file mode 100644
index 00000000000..20b32a3909e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/entity/CredentialEntity.java
@@ -0,0 +1,144 @@
+/**
+*
+* 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.credential.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.Id;
+import jakarta.persistence.Index;
+import jakarta.persistence.Lob;
+import jakarta.persistence.Table;
+import java.io.Serializable;
+import java.time.Instant;
+import org.apache.airavata.credential.model.CredentialType;
+
+/**
+ * JPA entity for CREDENTIAL table.
+ *
+ *
Stores SSH keys and other credentials used for accessing compute and storage resources.
+ * The {@code credentialId} is the token string (UUID) used throughout the system as the PK.
+ * The {@code userId} field tracks who owns/created this credential.
+ */
+@Entity
+@Table(
+ name = "credential",
+ indexes = {
+ @Index(name = "idx_credential_gateway", columnList = "gateway_id"),
+ @Index(name = "idx_credential_user", columnList = "user_id"),
+ @Index(name = "idx_credential_gateway_user", columnList = "gateway_id, user_id")
+ })
+public class CredentialEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @Column(name = "credential_id", length = 255)
+ private String credentialId;
+
+ @Column(name = "gateway_id", length = 255, nullable = false)
+ private String gatewayId;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "type", length = 20, nullable = false)
+ private CredentialType type;
+
+ @Lob
+ @Column(name = "credential_data", nullable = false, columnDefinition = "LONGBLOB")
+ private byte[] credentialData;
+
+ @Column(name = "user_id", length = 255, nullable = false)
+ private String userId;
+
+ @Column(name = "name", length = 255)
+ private String name;
+
+ @Column(name = "created_at", nullable = false)
+ private Instant createdAt;
+
+ @Column(name = "description")
+ private String description;
+
+ public CredentialEntity() {}
+
+ public String getCredentialId() {
+ return credentialId;
+ }
+
+ public void setCredentialId(String credentialId) {
+ this.credentialId = credentialId;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ public CredentialType getType() {
+ return type;
+ }
+
+ public void setType(CredentialType type) {
+ this.type = type;
+ }
+
+ public byte[] getCredentialData() {
+ return credentialData;
+ }
+
+ public void setCredentialData(byte[] credentialData) {
+ this.credentialData = credentialData;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Instant createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/exception/CredentialStoreException.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/exception/CredentialStoreException.java
new file mode 100644
index 00000000000..080a7f0d0a6
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/exception/CredentialStoreException.java
@@ -0,0 +1,40 @@
+/**
+*
+* 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.credential.exception;
+
+/**
+ * Domain exception: CredentialStoreException
+ * Exception thrown by credential store operations.
+ */
+public class CredentialStoreException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public CredentialStoreException() {
+ super();
+ }
+
+ public CredentialStoreException(String message) {
+ super(message);
+ }
+
+ public CredentialStoreException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CertificateCredential.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CertificateCredential.java
new file mode 100644
index 00000000000..47849a3c5d0
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CertificateCredential.java
@@ -0,0 +1,216 @@
+/**
+*
+* 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.credential.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Objects;
+
+/**
+ * X.509 certificate-based credential.
+ */
+public final class CertificateCredential implements Credential {
+ private static final long serialVersionUID = 2L;
+
+ // Base credential fields
+ private String userId;
+ private long createdAt;
+ private String token;
+ private String gatewayId;
+ private String name;
+ private String description;
+
+ // Certificate-specific fields
+ private CommunityUser communityUser;
+ private String x509Cert;
+ private String notAfter;
+ private String privateKey;
+ private Long lifeTime;
+ private String notBefore;
+
+ @JsonIgnore
+ private X509Certificate[] certificates;
+
+ @JsonIgnore
+ private PrivateKey privateKeyObject;
+
+ public CertificateCredential() {}
+
+ @Override
+ public String getUserId() {
+ return userId;
+ }
+
+ @Override
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ @Override
+ public long getCreatedAt() {
+ return createdAt;
+ }
+
+ @Override
+ public void setCreatedAt(long createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ @Override
+ public String getToken() {
+ return token;
+ }
+
+ @Override
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ @Override
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public CommunityUser getCommunityUser() {
+ return communityUser;
+ }
+
+ public void setCommunityUser(CommunityUser communityUser) {
+ this.communityUser = communityUser;
+ }
+
+ public String getX509Cert() {
+ return x509Cert;
+ }
+
+ public void setX509Cert(String x509Cert) {
+ this.x509Cert = x509Cert;
+ }
+
+ public String getNotAfter() {
+ return notAfter;
+ }
+
+ public void setNotAfter(String notAfter) {
+ this.notAfter = notAfter;
+ }
+
+ public String getPrivateKey() {
+ return privateKey;
+ }
+
+ public void setPrivateKey(String privateKey) {
+ this.privateKey = privateKey;
+ }
+
+ public Long getLifeTime() {
+ return lifeTime;
+ }
+
+ public void setLifeTime(Long lifeTime) {
+ this.lifeTime = lifeTime;
+ }
+
+ public String getNotBefore() {
+ return notBefore;
+ }
+
+ public void setNotBefore(String notBefore) {
+ this.notBefore = notBefore;
+ }
+
+ public X509Certificate[] getCertificates() {
+ return certificates;
+ }
+
+ public void setCertificates(X509Certificate[] certificates) {
+ this.certificates = certificates;
+ }
+
+ public PrivateKey getPrivateKeyObject() {
+ return privateKeyObject;
+ }
+
+ public void setPrivateKeyObject(PrivateKey privateKeyObject) {
+ this.privateKeyObject = privateKeyObject;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CertificateCredential that = (CertificateCredential) o;
+ return Objects.equals(userId, that.userId)
+ && Objects.equals(gatewayId, that.gatewayId)
+ && Objects.equals(x509Cert, that.x509Cert)
+ && Objects.equals(notAfter, that.notAfter)
+ && Objects.equals(privateKey, that.privateKey)
+ && Objects.equals(lifeTime, that.lifeTime)
+ && Objects.equals(notBefore, that.notBefore)
+ && createdAt == that.createdAt
+ && Objects.equals(token, that.token);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(userId, gatewayId, x509Cert, notAfter, privateKey, lifeTime, notBefore, createdAt, token);
+ }
+
+ @Override
+ public String toString() {
+ return "CertificateCredential{"
+ + "userId='" + userId + '\''
+ + ", gatewayId='" + gatewayId + '\''
+ + ", x509Cert='" + (x509Cert != null ? "***" : null) + '\''
+ + ", notAfter='" + notAfter + '\''
+ + ", privateKey='" + (privateKey != null ? "***" : null) + '\''
+ + ", lifeTime=" + lifeTime
+ + ", notBefore='" + notBefore + '\''
+ + ", createdAt=" + createdAt
+ + ", token='" + token + '\''
+ + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CommunityUser.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CommunityUser.java
new file mode 100644
index 00000000000..41bf8ff3175
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CommunityUser.java
@@ -0,0 +1,90 @@
+/**
+*
+* 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.credential.model;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Represents a community user for certificate credentials.
+ */
+public class CommunityUser implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private String gatewayName;
+ private String username;
+ private String userEmail;
+
+ public CommunityUser() {}
+
+ public CommunityUser(String gatewayName, String username, String userEmail) {
+ this.gatewayName = gatewayName;
+ this.username = username;
+ this.userEmail = userEmail;
+ }
+
+ public String getGatewayName() {
+ return gatewayName;
+ }
+
+ public void setGatewayName(String gatewayName) {
+ this.gatewayName = gatewayName;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getUserEmail() {
+ return userEmail;
+ }
+
+ public void setUserEmail(String userEmail) {
+ this.userEmail = userEmail;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CommunityUser that = (CommunityUser) o;
+ return Objects.equals(gatewayName, that.gatewayName)
+ && Objects.equals(username, that.username)
+ && Objects.equals(userEmail, that.userEmail);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(gatewayName, username, userEmail);
+ }
+
+ @Override
+ public String toString() {
+ return "CommunityUser{"
+ + "gatewayName='" + gatewayName + '\''
+ + ", username='" + username + '\''
+ + ", userEmail='" + userEmail + '\''
+ + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/Credential.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/Credential.java
new file mode 100644
index 00000000000..8029d7b4b41
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/Credential.java
@@ -0,0 +1,56 @@
+/**
+*
+* 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.credential.model;
+
+import java.io.Serializable;
+
+/**
+ * Sealed interface for all credential types.
+ *
+ *
The credential can be a certificate, user name password or a SSH key.
+ * The {@code userId} field tracks who owns/created this credential.
+ */
+public sealed interface Credential extends Serializable
+ permits SSHCredential, PasswordCredential, CertificateCredential {
+
+ String getUserId();
+
+ void setUserId(String userId);
+
+ long getCreatedAt();
+
+ void setCreatedAt(long createdAt);
+
+ String getToken();
+
+ void setToken(String token);
+
+ String getGatewayId();
+
+ void setGatewayId(String gatewayId);
+
+ String getName();
+
+ void setName(String name);
+
+ String getDescription();
+
+ void setDescription(String description);
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CredentialSummary.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CredentialSummary.java
new file mode 100644
index 00000000000..b29030f629c
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CredentialSummary.java
@@ -0,0 +1,140 @@
+/**
+*
+* 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.credential.model;
+
+import java.util.Objects;
+
+/**
+ * Summary view of a stored credential for listing and access-control.
+ * Credentials do not store a login username; that is set per resource in RESOURCE_ACCESS.
+ * The optional {@code username} field is only populated from resource context (e.g. access grant) when applicable.
+ */
+public class CredentialSummary {
+ private SummaryType type;
+ private String gatewayId;
+ /** User-given name to identify this credential. */
+ private String name;
+ /** Optional; when present, from resource context (e.g. access grant). Credentials do not store login username. */
+ private String username;
+
+ private String publicKey;
+ private Long createdAt;
+ private String token;
+ private String description;
+
+ public CredentialSummary() {}
+
+ public SummaryType getType() {
+ return type;
+ }
+
+ public void setType(SummaryType type) {
+ this.type = type;
+ }
+
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPublicKey() {
+ return publicKey;
+ }
+
+ public void setPublicKey(String publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ public Long getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Long createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CredentialSummary that = (CredentialSummary) o;
+ return type == that.type
+ && Objects.equals(gatewayId, that.gatewayId)
+ && Objects.equals(name, that.name)
+ && Objects.equals(username, that.username)
+ && Objects.equals(publicKey, that.publicKey)
+ && Objects.equals(createdAt, that.createdAt)
+ && Objects.equals(token, that.token)
+ && Objects.equals(description, that.description);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, gatewayId, name, username, publicKey, createdAt, token, description);
+ }
+
+ @Override
+ public String toString() {
+ return "CredentialSummary{" + "type="
+ + type + ", gatewayId='"
+ + gatewayId + '\'' + ", name='"
+ + name + '\'' + ", username='"
+ + username + '\'' + ", publicKey='"
+ + (publicKey != null ? "***" : null) + '\'' + ", createdAt="
+ + createdAt + ", token='"
+ + token + '\'' + ", description='"
+ + description + '\'' + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CredentialType.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CredentialType.java
new file mode 100644
index 00000000000..047e7f9cc52
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/CredentialType.java
@@ -0,0 +1,29 @@
+/**
+*
+* 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.credential.model;
+
+/**
+ * Type discriminator for credential storage.
+ */
+public enum CredentialType {
+ SSH,
+ PASSWORD,
+ CERTIFICATE
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/PasswordCredential.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/PasswordCredential.java
new file mode 100644
index 00000000000..ba071d60ed0
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/PasswordCredential.java
@@ -0,0 +1,154 @@
+/**
+*
+* 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.credential.model;
+
+import java.util.Objects;
+
+/**
+ * Password-based credential for authenticating to resources or IAM/AWS.
+ * For compute/storage resource login, use RESOURCE_ACCESS.loginUsername (per assignment).
+ * This field is used for IAM/Keycloak admin and AWS access key ID when stored in gateway config.
+ */
+public final class PasswordCredential implements Credential {
+ private static final long serialVersionUID = 2L;
+
+ // Base credential fields
+ private String userId;
+ private long createdAt;
+ private String token;
+ private String gatewayId;
+ private String name;
+ private String description;
+
+ // Password-specific fields
+ /** Used for IAM/Keycloak username or AWS access key ID; not for resource login (use RESOURCE_ACCESS). */
+ private String loginUserName;
+
+ private String password;
+
+ public PasswordCredential() {}
+
+ @Override
+ public String getUserId() {
+ return userId;
+ }
+
+ @Override
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ @Override
+ public long getCreatedAt() {
+ return createdAt;
+ }
+
+ @Override
+ public void setCreatedAt(long createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ @Override
+ public String getToken() {
+ return token;
+ }
+
+ @Override
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ @Override
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getLoginUserName() {
+ return loginUserName;
+ }
+
+ public void setLoginUserName(String loginUserName) {
+ this.loginUserName = loginUserName;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PasswordCredential that = (PasswordCredential) o;
+ return Objects.equals(gatewayId, that.gatewayId)
+ && Objects.equals(userId, that.userId)
+ && Objects.equals(password, that.password)
+ && Objects.equals(description, that.description)
+ && createdAt == that.createdAt
+ && Objects.equals(token, that.token);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(gatewayId, userId, loginUserName, password, description, createdAt, token);
+ }
+
+ @Override
+ public String toString() {
+ return "PasswordCredential{"
+ + "gatewayId='" + gatewayId + '\''
+ + ", userId='" + userId + '\''
+ + ", loginUserName='" + loginUserName + '\''
+ + ", password='" + (password != null ? "***" : null) + '\''
+ + ", description='" + description + '\''
+ + ", createdAt=" + createdAt
+ + ", token='" + token + '\''
+ + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/SSHCredential.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/SSHCredential.java
new file mode 100644
index 00000000000..81b52dd0f9e
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/SSHCredential.java
@@ -0,0 +1,163 @@
+/**
+*
+* 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.credential.model;
+
+import java.util.Objects;
+
+/**
+ * SSH key-based credential for authenticating to resources.
+ * Login username is not stored here; it is set per resource via RESOURCE_ACCESS (credential assignment).
+ */
+public final class SSHCredential implements Credential {
+ private static final long serialVersionUID = 2L;
+
+ // Base credential fields
+ private String userId;
+ private long createdAt;
+ private String token;
+ private String gatewayId;
+ private String name;
+ private String description;
+
+ // SSH-specific fields
+ private String passphrase;
+ private String publicKey;
+ private String privateKey;
+
+ public SSHCredential() {}
+
+ @Override
+ public String getUserId() {
+ return userId;
+ }
+
+ @Override
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ @Override
+ public long getCreatedAt() {
+ return createdAt;
+ }
+
+ @Override
+ public void setCreatedAt(long createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ @Override
+ public String getToken() {
+ return token;
+ }
+
+ @Override
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public String getGatewayId() {
+ return gatewayId;
+ }
+
+ @Override
+ public void setGatewayId(String gatewayId) {
+ this.gatewayId = gatewayId;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getPassphrase() {
+ return passphrase;
+ }
+
+ public void setPassphrase(String passphrase) {
+ this.passphrase = passphrase;
+ }
+
+ public String getPublicKey() {
+ return publicKey;
+ }
+
+ public void setPublicKey(String publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ public String getPrivateKey() {
+ return privateKey;
+ }
+
+ public void setPrivateKey(String privateKey) {
+ this.privateKey = privateKey;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SSHCredential that = (SSHCredential) o;
+ return Objects.equals(gatewayId, that.gatewayId)
+ && Objects.equals(userId, that.userId)
+ && Objects.equals(passphrase, that.passphrase)
+ && Objects.equals(publicKey, that.publicKey)
+ && Objects.equals(privateKey, that.privateKey)
+ && createdAt == that.createdAt
+ && Objects.equals(token, that.token)
+ && Objects.equals(description, that.description);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(gatewayId, userId, passphrase, publicKey, privateKey, createdAt, token, description);
+ }
+
+ @Override
+ public String toString() {
+ return "SSHCredential{"
+ + "gatewayId='" + gatewayId + '\''
+ + ", userId='" + userId + '\''
+ + ", passphrase='" + (passphrase != null ? "***" : null) + '\''
+ + ", publicKey='" + (publicKey != null ? "***" : null) + '\''
+ + ", privateKey='" + (privateKey != null ? "***" : null) + '\''
+ + ", createdAt=" + createdAt
+ + ", token='" + token + '\''
+ + ", description='" + description + '\''
+ + '}';
+ }
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/SummaryType.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/SummaryType.java
new file mode 100644
index 00000000000..0ca0931a915
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/model/SummaryType.java
@@ -0,0 +1,30 @@
+/**
+*
+* 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.credential.model;
+
+/**
+ * Domain enum: SummaryType
+ * Types of credential summaries.
+ */
+public enum SummaryType {
+ SSH,
+ PASSWD,
+ CERT
+}
diff --git a/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/repository/CredentialRepository.java b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/repository/CredentialRepository.java
new file mode 100644
index 00000000000..261f63811af
--- /dev/null
+++ b/airavata-api/modules/airavata-api/src/main/java/org/apache/airavata/credential/repository/CredentialRepository.java
@@ -0,0 +1,82 @@
+/**
+*
+* 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.credential.repository;
+
+import java.util.List;
+import java.util.Optional;
+import org.apache.airavata.credential.entity.CredentialEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Spring Data JPA repository for CredentialEntity.
+ *
+ *
The PK is now {@code credentialId} (the token string), so {@code findById()} can be
+ * used directly for single-credential lookups.
+ */
+@Repository
+public interface CredentialRepository extends JpaRepository {
+
+ /**
+ * Find credential by gateway id and credential ID.
+ * Provides a gateway-scoped lookup for access-control purposes.
+ */
+ Optional findByGatewayIdAndCredentialId(String gatewayId, String credentialId);
+
+ /**
+ * Find all credentials for a gateway.
+ */
+ List findByGatewayId(String gatewayId);
+
+ /**
+ * Find all credentials for a gateway owned by a specific user.
+ */
+ List findByGatewayIdAndUserId(String gatewayId, String userId);
+
+ /**
+ * Find credentials for a gateway with specific credential IDs.
+ */
+ @Query("SELECT c FROM CredentialEntity c WHERE c.gatewayId = :gatewayId AND c.credentialId IN :credentialIds")
+ List