This is an automated email from the ASF dual-hosted git repository.

yasith pushed a commit to branch resource-mgmt-rest-api
in repository https://gitbox.apache.org/repos/asf/airavata.git

commit bf7a091b719d2b2937c8e65ed00c81c6cce45933
Author: yasithdev <[email protected]>
AuthorDate: Thu Nov 6 13:48:00 2025 -0500

    initial plumbing
---
 modules/airavata-rest-api/README.md                | 416 +++++++++++++++++++++
 modules/airavata-rest-api/pom.xml                  | 124 ++++++
 .../rest/api/AiravataRestApiApplication.java       |  31 ++
 .../airavata/rest/api/config/AuthzTokenFilter.java | 143 +++++++
 .../airavata/rest/api/config/OpenApiConfig.java    |  85 +++++
 .../api/controller/ComputeResourceController.java  | 215 +++++++++++
 .../api/controller/StorageResourceController.java  | 124 ++++++
 .../rest/api/exception/GlobalExceptionHandler.java | 140 +++++++
 .../rest/api/service/ComputeResourceService.java   |  98 +++++
 .../rest/api/service/StorageResourceService.java   |  73 ++++
 .../src/main/resources/application.properties      |  21 ++
 .../src/main/resources/log4j2.xml                  |  37 ++
 pom.xml                                            |   1 +
 13 files changed, 1508 insertions(+)

diff --git a/modules/airavata-rest-api/README.md 
b/modules/airavata-rest-api/README.md
new file mode 100644
index 0000000000..b0af20cba4
--- /dev/null
+++ b/modules/airavata-rest-api/README.md
@@ -0,0 +1,416 @@
+# Airavata REST API
+
+REST API module for managing compute and storage resources in Airavata. This 
module provides REST endpoints that replace the Thrift API for resource 
management operations.
+
+## Overview
+
+The Airavata REST API provides HTTP/REST endpoints for:
+- **Compute Resources**: CRUD operations for compute resources, resource job 
managers (RJM), and batch queues
+- **Storage Resources**: CRUD operations for storage resources
+
+All endpoints use the same authorization logic as the Thrift servers and call 
handlers directly (no network calls).
+
+## Prerequisites
+
+- Java 17 or higher
+- Maven 3.8 or higher
+- Airavata API module built and available
+- Database configured (reuses OpenJPA from airavata-api)
+
+## Building
+
+Build the module from the root directory:
+
+```bash
+cd /path/to/airavata
+mvn clean install -pl modules/airavata-rest-api -am -DskipTests
+```
+
+Or build from the module directory:
+
+```bash
+cd modules/airavata-rest-api
+mvn clean install -DskipTests
+```
+
+## Running
+
+### Using Maven
+
+```bash
+cd modules/airavata-rest-api
+mvn spring-boot:run
+```
+
+The service will start on port 8080 by default.
+
+### Using JAR
+
+After building, run the generated JAR:
+
+```bash
+java -jar target/airavata-rest-api-0.21-SNAPSHOT.jar
+```
+
+### Configuration
+
+Server port and other settings can be configured in 
`src/main/resources/application.properties`:
+
+```properties
+server.port=8080
+spring.application.name=airavata-rest-api
+```
+
+## API Endpoints
+
+### Base URL
+
+All endpoints are prefixed with `/api/v1`
+
+### Compute Resources
+
+#### List all compute resources
+```http
+GET /api/v1/compute
+```
+
+Returns a map of compute resource IDs and hostnames.
+
+#### Get compute resource by ID
+```http
+GET /api/v1/compute/{id}
+```
+
+Returns detailed compute resource information.
+
+#### Register compute resource
+```http
+POST /api/v1/compute
+Content-Type: application/json
+
+{
+  "hostName": "example.host.edu",
+  "hostAliases": ["alias1.example.edu", "alias2.example.edu"],
+  "ipAddresses": ["192.168.1.1"],
+  "resourceDescription": "Example compute resource",
+  "enabled": true,
+  "cpusPerNode": 24,
+  "defaultNodeCount": 1,
+  "defaultCPUCount": 24,
+  "defaultWalltime": 3600,
+  "maxMemoryPerNode": 128
+}
+```
+
+Returns the created compute resource ID.
+
+#### Update compute resource
+```http
+PUT /api/v1/compute/{id}
+Content-Type: application/json
+
+{
+  "computeResourceId": "existing-resource-id",
+  "hostName": "example.host.edu",
+  "hostAliases": ["alias1.example.edu"],
+  "ipAddresses": ["192.168.1.1"],
+  "resourceDescription": "Updated description",
+  "enabled": true,
+  "cpusPerNode": 24,
+  "defaultNodeCount": 1,
+  "defaultCPUCount": 24,
+  "defaultWalltime": 3600
+}
+```
+
+#### Delete compute resource
+```http
+DELETE /api/v1/compute/{id}
+```
+
+### Resource Job Managers (RJM)
+
+#### Register resource job manager
+```http
+POST /api/v1/compute/{id}/rjm
+Content-Type: application/json
+
+{
+  "resourceJobManagerId": "DO_NOT_SET_AT_CLIENTS",
+  "resourceJobManagerType": "SLURM",
+  "jobManagerBinPath": "/usr/bin",
+  "jobManagerCommands": {
+    "SUBMISSION": "sbatch",
+    "JOB_MONITORING": "squeue",
+    "DELETION": "scancel"
+  },
+  "pushMonitoringEndpoint": null
+}
+```
+
+#### Get resource job manager
+```http
+GET /api/v1/compute/{id}/rjm/{rjmId}
+```
+
+#### Update resource job manager
+```http
+PUT /api/v1/compute/{id}/rjm/{rjmId}
+Content-Type: application/json
+
+{
+  "resourceJobManagerId": "existing-rjm-id",
+  "resourceJobManagerType": "SLURM",
+  "jobManagerBinPath": "/usr/bin",
+  "jobManagerCommands": {
+    "SUBMISSION": "sbatch",
+    "JOB_MONITORING": "squeue",
+    "DELETION": "scancel",
+    "CHECK_JOB": "scontrol show job"
+  },
+  "pushMonitoringEndpoint": null
+}
+```
+
+#### Delete resource job manager
+```http
+DELETE /api/v1/compute/{id}/rjm/{rjmId}
+```
+
+### Batch Queues
+
+#### Delete batch queue
+```http
+DELETE /api/v1/compute/{id}/queue/{queueName}
+```
+
+### Storage Resources
+
+#### List all storage resources
+```http
+GET /api/v1/storage
+```
+
+Returns a map of storage resource IDs and hostnames.
+
+#### Get storage resource by ID
+```http
+GET /api/v1/storage/{id}
+```
+
+Returns detailed storage resource information.
+
+#### Register storage resource
+```http
+POST /api/v1/storage
+Content-Type: application/json
+
+{
+  "storageResourceId": "DO_NOT_SET_AT_CLIENTS",
+  "hostName": "storage.host.edu",
+  "storageResourceDescription": "Example storage resource",
+  "enabled": true,
+  "dataMovementInterfaces": []
+}
+```
+
+Returns the created storage resource ID.
+
+#### Update storage resource
+```http
+PUT /api/v1/storage/{id}
+Content-Type: application/json
+
+{
+  "storageResourceId": "existing-storage-resource-id",
+  "hostName": "storage.host.edu",
+  "storageResourceDescription": "Updated description",
+  "enabled": true,
+  "dataMovementInterfaces": []
+}
+```
+
+#### Delete storage resource
+```http
+DELETE /api/v1/storage/{id}
+```
+
+## Authentication
+
+The API uses Bearer token authentication with claims. Include the following 
headers:
+
+```http
+Authorization: Bearer <access_token>
+X-Claims: {"userName": "username", "gatewayID": "gateway-id"}
+```
+
+The `X-Claims` header should be a JSON object containing:
+- `userName`: The username
+- `gatewayID`: The gateway identifier
+
+### Example with curl
+
+```bash
+curl -X GET http://localhost:8080/api/v1/compute \
+  -H "Authorization: Bearer your-token-here" \
+  -H "X-Claims: {\"userName\": \"user\", \"gatewayID\": \"gateway\"}"
+```
+
+**Note**: If TLS is not enabled in Airavata configuration, authentication may 
be optional. When TLS is enabled, authentication is required for all endpoints.
+
+## Swagger UI
+
+Interactive API documentation is available via Swagger UI:
+
+- **Swagger UI**: http://localhost:8080/swagger-ui/index.html
+- **OpenAPI Docs**: http://localhost:8080/v3/api-docs/public
+
+Swagger UI provides:
+- Interactive API testing
+- Complete endpoint documentation
+- Request/response schema definitions
+- Authentication testing
+
+## Error Handling
+
+The API returns standard HTTP status codes:
+
+- `200 OK` - Successful GET request
+- `201 Created` - Successful POST request
+- `204 No Content` - Successful PUT/DELETE request
+- `400 Bad Request` - Invalid request
+- `401 Unauthorized` - Authentication required or failed
+- `404 Not Found` - Resource not found
+- `500 Internal Server Error` - Server error
+
+Error responses include a JSON body with error details:
+
+```json
+{
+  "status": 400,
+  "error": "Invalid request",
+  "message": "Error details here",
+  "timestamp": 1234567890
+}
+```
+
+## Example Usage
+
+### Create a compute resource
+
+```bash
+curl -X POST http://localhost:8080/api/v1/compute \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Bearer token" \
+  -H "X-Claims: {\"userName\": \"user\", \"gatewayID\": \"gateway\"}" \
+  -d '{
+    "hostName": "compute.example.edu",
+    "hostAliases": ["compute-alias.example.edu"],
+    "ipAddresses": ["192.168.1.100"],
+    "resourceDescription": "Example HPC cluster",
+    "enabled": true,
+    "cpusPerNode": 24,
+    "defaultNodeCount": 1,
+    "defaultCPUCount": 24,
+    "defaultWalltime": 3600,
+    "maxMemoryPerNode": 128
+  }'
+```
+
+### Get all compute resources
+
+```bash
+curl -X GET http://localhost:8080/api/v1/compute \
+  -H "Authorization: Bearer token" \
+  -H "X-Claims: {\"userName\": \"user\", \"gatewayID\": \"gateway\"}"
+```
+
+### Create a resource job manager
+
+```bash
+curl -X POST http://localhost:8080/api/v1/compute/{computeResourceId}/rjm \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Bearer token" \
+  -H "X-Claims: {\"userName\": \"user\", \"gatewayID\": \"gateway\"}" \
+  -d '{
+    "resourceJobManagerId": "DO_NOT_SET_AT_CLIENTS",
+    "resourceJobManagerType": "SLURM",
+    "jobManagerBinPath": "/usr/bin",
+    "jobManagerCommands": {
+      "SUBMISSION": "sbatch",
+      "JOB_MONITORING": "squeue",
+      "DELETION": "scancel",
+      "CHECK_JOB": "scontrol show job"
+    }
+  }'
+```
+
+## Development
+
+### Project Structure
+
+```
+modules/airavata-rest-api/
+├── src/
+│   ├── main/
+│   │   ├── java/
+│   │   │   └── org/apache/airavata/rest/api/
+│   │   │       ├── AiravataRestApiApplication.java
+│   │   │       ├── config/
+│   │   │       │   ├── AuthzTokenFilter.java
+│   │   │       │   └── OpenApiConfig.java
+│   │   │       ├── controller/
+│   │   │       │   ├── ComputeResourceController.java
+│   │   │       │   └── StorageResourceController.java
+│   │   │       ├── service/
+│   │   │       │   ├── ComputeResourceService.java
+│   │   │       │   └── StorageResourceService.java
+│   │   │       └── exception/
+│   │   │           └── GlobalExceptionHandler.java
+│   │   └── resources/
+│   │       ├── application.properties
+│   │       └── log4j2.xml
+│   └── test/
+└── pom.xml
+```
+
+### Key Components
+
+- **Controllers**: REST endpoints for compute and storage resources
+- **Services**: Business logic that calls RegistryServerHandler directly
+- **AuthzTokenFilter**: Authentication and authorization filter
+- **OpenApiConfig**: Swagger/OpenAPI configuration
+- **GlobalExceptionHandler**: Exception to HTTP status code mapping
+
+## Compatibility
+
+This REST API is designed to be fully compatible with the existing Thrift API:
+- Same request/response models
+- Same authorization logic
+- Same database layer (reuses OpenJPA from airavata-api)
+- Direct handler calls (no network overhead)
+
+## Troubleshooting
+
+### Service won't start
+
+- Check that port 8080 is available
+- Verify airavata-api module is built
+- Check database configuration in airavata-api
+
+### Authentication errors
+
+- Verify TLS settings in Airavata configuration
+- Check that Authorization and X-Claims headers are properly formatted
+- Ensure the access token is valid
+
+### Endpoints not found
+
+- Verify the service is running on the correct port
+- Check that the path starts with `/api/v1`
+- Review Swagger UI to see available endpoints
+
+## License
+
+Licensed to the Apache Software Foundation (ASF) under the Apache License, 
Version 2.0.
+
diff --git a/modules/airavata-rest-api/pom.xml 
b/modules/airavata-rest-api/pom.xml
new file mode 100644
index 0000000000..b11c191aec
--- /dev/null
+++ b/modules/airavata-rest-api/pom.xml
@@ -0,0 +1,124 @@
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.airavata</groupId>
+    <artifactId>airavata</artifactId>
+    <version>0.21-SNAPSHOT</version>
+    <relativePath>../../../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>airavata-rest-api</artifactId>
+  <name>Airavata REST API</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.logging.log4j</groupId>
+          <artifactId>log4j-to-slf4j</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-classic</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-core</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-log4j2</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-validation</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.logging.log4j</groupId>
+          <artifactId>log4j-to-slf4j</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-classic</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-core</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.springdoc</groupId>
+      <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.airavata</groupId>
+      <artifactId>airavata-api</artifactId>
+      <version>${project.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-classic</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>ch.qos.logback</groupId>
+          <artifactId>logback-core</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.slf4j</groupId>
+          <artifactId>slf4j-log4j12</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <release>17</release>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <systemPropertyVariables>
+            
<log4j2.configurationFile>src/main/resources/log4j2.xml</log4j2.configurationFile>
+          </systemPropertyVariables>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git 
a/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/AiravataRestApiApplication.java
 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/AiravataRestApiApplication.java
new file mode 100644
index 0000000000..ef651a4e04
--- /dev/null
+++ 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/AiravataRestApiApplication.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.rest.api;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication(scanBasePackages = {"org.apache.airavata.rest.api"})
+public class AiravataRestApiApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(AiravataRestApiApplication.class, args);
+    }
+}
+
diff --git 
a/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/config/AuthzTokenFilter.java
 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/config/AuthzTokenFilter.java
new file mode 100644
index 0000000000..766a9c0256
--- /dev/null
+++ 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/config/AuthzTokenFilter.java
@@ -0,0 +1,143 @@
+/**
+ * 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.rest.api.config;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.airavata.common.exception.ApplicationSettingsException;
+import org.apache.airavata.common.utils.Constants;
+import org.apache.airavata.common.utils.ServerSettings;
+import org.apache.airavata.model.error.AuthorizationException;
+import org.apache.airavata.model.security.AuthzToken;
+import org.apache.airavata.security.AiravataSecurityException;
+import org.apache.airavata.service.security.AiravataSecurityManager;
+import org.apache.airavata.service.security.IdentityContext;
+import org.apache.airavata.service.security.SecurityManagerFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+@Component
+public class AuthzTokenFilter extends OncePerRequestFilter {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(AuthzTokenFilter.class);
+
+    @Override
+    protected boolean shouldNotFilter(HttpServletRequest request) throws 
ServletException {
+        String path = request.getRequestURI();
+        return path.startsWith("/swagger")
+                || path.startsWith("/v2/api-docs")
+                || path.startsWith("/v3/api-docs")
+                || path.startsWith("/swagger-ui")
+                || path.startsWith("/swagger-ui.html")
+                || path.startsWith("/swagger-resources")
+                || path.startsWith("/webjars/")
+                || path.equals("/actuator/health")
+                || path.equals("/");
+    }
+
+    @Override
+    protected void doFilterInternal(
+            HttpServletRequest request, HttpServletResponse response, 
FilterChain filterChain)
+            throws ServletException, IOException {
+        try {
+            String authorizationHeader = request.getHeader("Authorization");
+            String xClaimsHeader = request.getHeader("X-Claims");
+
+            if (request.getMethod().equals("OPTIONS")) {
+                filterChain.doFilter(request, response);
+                return;
+            }
+
+            try {
+                boolean isAPISecured = ServerSettings.isTLSEnabled();
+                
+                if (isAPISecured) {
+                    // If API is secured, require authentication
+                    if (authorizationHeader == null || 
!authorizationHeader.startsWith("Bearer ") || xClaimsHeader == null) {
+                        
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication 
required");
+                        return;
+                    }
+                }
+                
+                if (authorizationHeader != null && 
authorizationHeader.startsWith("Bearer ") && xClaimsHeader != null) {
+                    String accessToken = authorizationHeader.substring(7); // 
Remove "Bearer " prefix
+                    ObjectMapper objectMapper = new ObjectMapper();
+                    Map<String, String> claimsMap = 
objectMapper.readValue(xClaimsHeader, new TypeReference<>() {});
+
+                    AuthzToken authzToken = new AuthzToken();
+                    authzToken.setAccessToken(accessToken);
+                    authzToken.setClaimsMap(claimsMap);
+
+                    // Validate authorization using SecurityInterceptor logic
+                    authorize(authzToken, request.getMethod() + " " + 
request.getRequestURI());
+
+                    // Set the user identity info in thread local for 
downstream execution
+                    IdentityContext.set(authzToken);
+                }
+            } catch (AuthorizationException e) {
+                LOGGER.error("Authorization failed", e);
+                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "User 
is not authenticated or authorized.");
+                return;
+            } catch (ApplicationSettingsException e) {
+                LOGGER.error("Error checking security settings", e);
+                
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal 
server error");
+                return;
+            } catch (Exception e) {
+                LOGGER.error("Invalid authorization data", e);
+                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, 
"Invalid authorization data");
+                return;
+            }
+
+            filterChain.doFilter(request, response);
+        } finally {
+            IdentityContext.unset();
+        }
+    }
+
+    private void authorize(AuthzToken authzToken, String action) throws 
AuthorizationException {
+        try {
+            boolean isAPISecured = ServerSettings.isTLSEnabled();
+            if (isAPISecured) {
+                AiravataSecurityManager securityManager = 
SecurityManagerFactory.getSecurityManager();
+                HashMap<String, String> metaDataMap = new HashMap<>();
+                metaDataMap.put(Constants.API_METHOD_NAME, action);
+                boolean isAuthz = securityManager.isUserAuthorized(authzToken, 
metaDataMap);
+                if (!isAuthz) {
+                    throw new AuthorizationException("User is not 
authenticated or authorized.");
+                }
+            }
+        } catch (AiravataSecurityException e) {
+            LOGGER.error(e.getMessage(), e);
+            throw new AuthorizationException("Error in authenticating or 
authorizing user.");
+        } catch (ApplicationSettingsException e) {
+            LOGGER.error(e.getMessage(), e);
+            throw new AuthorizationException("Internal error in authenticating 
or authorizing user.");
+        }
+    }
+}
+
diff --git 
a/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/config/OpenApiConfig.java
 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/config/OpenApiConfig.java
new file mode 100644
index 0000000000..eac864e371
--- /dev/null
+++ 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/config/OpenApiConfig.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.rest.api.config;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.media.StringSchema;
+import io.swagger.v3.oas.models.parameters.Parameter;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import io.swagger.v3.oas.models.security.SecurityScheme;
+import org.springdoc.core.customizers.OpenApiCustomizer;
+import org.springdoc.core.models.GroupedOpenApi;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class OpenApiConfig {
+
+    @Bean
+    public GroupedOpenApi publicApi() {
+        return GroupedOpenApi.builder()
+                .group("public")
+                .pathsToMatch("/api/**")
+                .addOpenApiCustomizer(globalHeaderCustomizer())
+                .build();
+    }
+
+    @Bean
+    public OpenAPI airavataRestApiOpenAPI() {
+        return new OpenAPI()
+                .info(new Info()
+                        .title("Airavata REST API")
+                        .description("REST API for managing compute and 
storage resources in Airavata")
+                        .version("1.0.0")
+                        .contact(new Contact()
+                                .name("Apache Airavata")
+                                .url("https://airavata.apache.org";)))
+                .addSecurityItem(new SecurityRequirement().addList("Bearer"))
+                .components(new io.swagger.v3.oas.models.Components()
+                        .addSecuritySchemes(
+                                "Bearer",
+                                new SecurityScheme()
+                                        .type(SecurityScheme.Type.HTTP)
+                                        .scheme("bearer")
+                                        .bearerFormat("JWT")
+                                        .description("Bearer token 
authentication. Include 'Bearer ' prefix.")));
+    }
+
+    @Bean
+    public OpenApiCustomizer globalHeaderCustomizer() {
+        return openApi -> {
+            Parameter claimsHeader = new Parameter()
+                    .in("header")
+                    .schema(new StringSchema())
+                    .name("X-Claims")
+                    .description("JSON object containing user claims: 
{\"userName\": \"...\", \"gatewayID\": \"...\"}")
+                    .required(false);
+
+            openApi.getPaths().values().forEach(pathItem -> {
+                pathItem.readOperations().forEach(operation -> {
+                    operation.addParametersItem(claimsHeader);
+                });
+            });
+        };
+    }
+}
+
+
diff --git 
a/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/controller/ComputeResourceController.java
 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/controller/ComputeResourceController.java
new file mode 100644
index 0000000000..e1ba90d229
--- /dev/null
+++ 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/controller/ComputeResourceController.java
@@ -0,0 +1,215 @@
+/**
+ * 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.rest.api.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Map;
+import 
org.apache.airavata.model.appcatalog.computeresource.ComputeResourceDescription;
+import org.apache.airavata.model.appcatalog.computeresource.ResourceJobManager;
+import org.apache.airavata.rest.api.service.ComputeResourceService;
+import org.apache.thrift.TException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.apache.airavata.registry.api.exception.RegistryServiceException;
+
+@RestController
+@RequestMapping("/api/v1/compute")
+@Tag(name = "Compute Resources", description = "API for managing compute 
resources, resource job managers, and batch queues")
+public class ComputeResourceController {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(ComputeResourceController.class);
+    private final ComputeResourceService computeResourceService;
+
+    public ComputeResourceController(ComputeResourceService 
computeResourceService) {
+        this.computeResourceService = computeResourceService;
+    }
+
+    @PostMapping
+    @Operation(summary = "Register a compute resource", description = "Creates 
a new compute resource and returns its ID")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "201", description = "Compute resource 
created successfully"),
+            @ApiResponse(responseCode = "400", description = "Invalid 
request"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<String> registerComputeResource(
+            @Parameter(description = "Compute resource description", required 
= true)
+            @RequestBody ComputeResourceDescription computeResourceDescription)
+            throws RegistryServiceException, TException {
+        String computeResourceId = 
computeResourceService.registerComputeResource(computeResourceDescription);
+        return 
ResponseEntity.status(HttpStatus.CREATED).body(computeResourceId);
+    }
+
+    @GetMapping
+    @Operation(summary = "Get all compute resource names", description = 
"Returns a map of all compute resource IDs and their hostnames")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "Successfully 
retrieved compute resources"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<Map<String, String>> getAllComputeResourceNames()
+            throws RegistryServiceException, TException {
+        Map<String, String> computeResources = 
computeResourceService.getAllComputeResourceNames();
+        return ResponseEntity.ok(computeResources);
+    }
+
+    @GetMapping("/{id}")
+    @Operation(summary = "Get compute resource by ID", description = 
"Retrieves detailed information about a specific compute resource")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "Successfully 
retrieved compute resource"),
+            @ApiResponse(responseCode = "404", description = "Compute resource 
not found"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<ComputeResourceDescription> getComputeResource(
+            @Parameter(description = "Compute resource ID", required = true)
+            @PathVariable("id") String computeResourceId)
+            throws RegistryServiceException, TException {
+        ComputeResourceDescription computeResource = 
computeResourceService.getComputeResource(computeResourceId);
+        return ResponseEntity.ok(computeResource);
+    }
+
+    @PutMapping("/{id}")
+    @Operation(summary = "Update compute resource", description = "Updates an 
existing compute resource")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "204", description = "Compute resource 
updated successfully"),
+            @ApiResponse(responseCode = "400", description = "Invalid 
request"),
+            @ApiResponse(responseCode = "404", description = "Compute resource 
not found"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<Void> updateComputeResource(
+            @Parameter(description = "Compute resource ID", required = true)
+            @PathVariable("id") String computeResourceId,
+            @Parameter(description = "Updated compute resource description", 
required = true)
+            @RequestBody ComputeResourceDescription computeResourceDescription)
+            throws RegistryServiceException, TException {
+        computeResourceService.updateComputeResource(computeResourceId, 
computeResourceDescription);
+        return ResponseEntity.noContent().build();
+    }
+
+    @DeleteMapping("/{id}")
+    @Operation(summary = "Delete compute resource", description = "Deletes a 
compute resource by ID")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "204", description = "Compute resource 
deleted successfully"),
+            @ApiResponse(responseCode = "404", description = "Compute resource 
not found"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<Void> deleteComputeResource(
+            @Parameter(description = "Compute resource ID", required = true)
+            @PathVariable("id") String computeResourceId)
+            throws RegistryServiceException, TException {
+        computeResourceService.deleteComputeResource(computeResourceId);
+        return ResponseEntity.noContent().build();
+    }
+
+    @PostMapping("/{id}/rjm")
+    @Operation(summary = "Register resource job manager", description = 
"Creates a new resource job manager for a compute resource")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "201", description = "Resource job 
manager created successfully"),
+            @ApiResponse(responseCode = "400", description = "Invalid 
request"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<String> registerResourceJobManager(
+            @Parameter(description = "Compute resource ID", required = true)
+            @PathVariable("id") String computeResourceId,
+            @Parameter(description = "Resource job manager description", 
required = true)
+            @RequestBody ResourceJobManager resourceJobManager)
+            throws RegistryServiceException, TException {
+        String rjmId = 
computeResourceService.registerResourceJobManager(resourceJobManager);
+        return ResponseEntity.status(HttpStatus.CREATED).body(rjmId);
+    }
+
+    @GetMapping("/{id}/rjm/{rjmId}")
+    @Operation(summary = "Get resource job manager", description = "Retrieves 
a resource job manager by ID")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "Successfully 
retrieved resource job manager"),
+            @ApiResponse(responseCode = "404", description = "Resource job 
manager not found"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<ResourceJobManager> getResourceJobManager(
+            @Parameter(description = "Compute resource ID", required = true)
+            @PathVariable("id") String computeResourceId,
+            @Parameter(description = "Resource job manager ID", required = 
true)
+            @PathVariable("rjmId") String resourceJobManagerId)
+            throws RegistryServiceException, TException {
+        ResourceJobManager rjm = 
computeResourceService.getResourceJobManager(resourceJobManagerId);
+        return ResponseEntity.ok(rjm);
+    }
+
+    @PutMapping("/{id}/rjm/{rjmId}")
+    @Operation(summary = "Update resource job manager", description = "Updates 
an existing resource job manager")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "204", description = "Resource job 
manager updated successfully"),
+            @ApiResponse(responseCode = "400", description = "Invalid 
request"),
+            @ApiResponse(responseCode = "404", description = "Resource job 
manager not found"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<Void> updateResourceJobManager(
+            @Parameter(description = "Compute resource ID", required = true)
+            @PathVariable("id") String computeResourceId,
+            @Parameter(description = "Resource job manager ID", required = 
true)
+            @PathVariable("rjmId") String resourceJobManagerId,
+            @Parameter(description = "Updated resource job manager 
description", required = true)
+            @RequestBody ResourceJobManager updatedResourceJobManager)
+            throws RegistryServiceException, TException {
+        computeResourceService.updateResourceJobManager(resourceJobManagerId, 
updatedResourceJobManager);
+        return ResponseEntity.noContent().build();
+    }
+
+    @DeleteMapping("/{id}/rjm/{rjmId}")
+    @Operation(summary = "Delete resource job manager", description = "Deletes 
a resource job manager by ID")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "204", description = "Resource job 
manager deleted successfully"),
+            @ApiResponse(responseCode = "404", description = "Resource job 
manager not found"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<Void> deleteResourceJobManager(
+            @Parameter(description = "Compute resource ID", required = true)
+            @PathVariable("id") String computeResourceId,
+            @Parameter(description = "Resource job manager ID", required = 
true)
+            @PathVariable("rjmId") String resourceJobManagerId)
+            throws RegistryServiceException, TException {
+        computeResourceService.deleteResourceJobManager(resourceJobManagerId);
+        return ResponseEntity.noContent().build();
+    }
+
+    @DeleteMapping("/{id}/queue/{queueName}")
+    @Operation(summary = "Delete batch queue", description = "Deletes a batch 
queue from a compute resource")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "204", description = "Batch queue 
deleted successfully"),
+            @ApiResponse(responseCode = "404", description = "Batch queue not 
found"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<Void> deleteBatchQueue(
+            @Parameter(description = "Compute resource ID", required = true)
+            @PathVariable("id") String computeResourceId,
+            @Parameter(description = "Queue name", required = true)
+            @PathVariable("queueName") String queueName)
+            throws RegistryServiceException, TException {
+        computeResourceService.deleteBatchQueue(computeResourceId, queueName);
+        return ResponseEntity.noContent().build();
+    }
+}
+
diff --git 
a/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/controller/StorageResourceController.java
 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/controller/StorageResourceController.java
new file mode 100644
index 0000000000..608d09d972
--- /dev/null
+++ 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/controller/StorageResourceController.java
@@ -0,0 +1,124 @@
+/**
+ * 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.rest.api.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.Map;
+import 
org.apache.airavata.model.appcatalog.storageresource.StorageResourceDescription;
+import org.apache.airavata.rest.api.service.StorageResourceService;
+import org.apache.thrift.TException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.apache.airavata.registry.api.exception.RegistryServiceException;
+
+@RestController
+@RequestMapping("/api/v1/storage")
+@Tag(name = "Storage Resources", description = "API for managing storage 
resources")
+public class StorageResourceController {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(StorageResourceController.class);
+    private final StorageResourceService storageResourceService;
+
+    public StorageResourceController(StorageResourceService 
storageResourceService) {
+        this.storageResourceService = storageResourceService;
+    }
+
+    @PostMapping
+    @Operation(summary = "Register a storage resource", description = "Creates 
a new storage resource and returns its ID")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "201", description = "Storage resource 
created successfully"),
+            @ApiResponse(responseCode = "400", description = "Invalid 
request"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<String> registerStorageResource(
+            @Parameter(description = "Storage resource description", required 
= true)
+            @RequestBody StorageResourceDescription storageResourceDescription)
+            throws RegistryServiceException, TException {
+        String storageResourceId = 
storageResourceService.registerStorageResource(storageResourceDescription);
+        return 
ResponseEntity.status(HttpStatus.CREATED).body(storageResourceId);
+    }
+
+    @GetMapping
+    @Operation(summary = "Get all storage resource names", description = 
"Returns a map of all storage resource IDs and their hostnames")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "Successfully 
retrieved storage resources"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<Map<String, String>> getAllStorageResourceNames()
+            throws RegistryServiceException, TException {
+        Map<String, String> storageResources = 
storageResourceService.getAllStorageResourceNames();
+        return ResponseEntity.ok(storageResources);
+    }
+
+    @GetMapping("/{id}")
+    @Operation(summary = "Get storage resource by ID", description = 
"Retrieves detailed information about a specific storage resource")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "Successfully 
retrieved storage resource"),
+            @ApiResponse(responseCode = "404", description = "Storage resource 
not found"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<StorageResourceDescription> getStorageResource(
+            @Parameter(description = "Storage resource ID", required = true)
+            @PathVariable("id") String storageResourceId)
+            throws RegistryServiceException, TException {
+        StorageResourceDescription storageResource = 
storageResourceService.getStorageResource(storageResourceId);
+        return ResponseEntity.ok(storageResource);
+    }
+
+    @PutMapping("/{id}")
+    @Operation(summary = "Update storage resource", description = "Updates an 
existing storage resource")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "204", description = "Storage resource 
updated successfully"),
+            @ApiResponse(responseCode = "400", description = "Invalid 
request"),
+            @ApiResponse(responseCode = "404", description = "Storage resource 
not found"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<Void> updateStorageResource(
+            @Parameter(description = "Storage resource ID", required = true)
+            @PathVariable("id") String storageResourceId,
+            @Parameter(description = "Updated storage resource description", 
required = true)
+            @RequestBody StorageResourceDescription storageResourceDescription)
+            throws RegistryServiceException, TException {
+        storageResourceService.updateStorageResource(storageResourceId, 
storageResourceDescription);
+        return ResponseEntity.noContent().build();
+    }
+
+    @DeleteMapping("/{id}")
+    @Operation(summary = "Delete storage resource", description = "Deletes a 
storage resource by ID")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "204", description = "Storage resource 
deleted successfully"),
+            @ApiResponse(responseCode = "404", description = "Storage resource 
not found"),
+            @ApiResponse(responseCode = "500", description = "Internal server 
error")
+    })
+    public ResponseEntity<Void> deleteStorageResource(
+            @Parameter(description = "Storage resource ID", required = true)
+            @PathVariable("id") String storageResourceId)
+            throws RegistryServiceException, TException {
+        storageResourceService.deleteStorageResource(storageResourceId);
+        return ResponseEntity.noContent().build();
+    }
+}
+
diff --git 
a/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/exception/GlobalExceptionHandler.java
 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000000..5d28c6735e
--- /dev/null
+++ 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/exception/GlobalExceptionHandler.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.rest.api.exception;
+
+import org.apache.airavata.model.error.AuthorizationException;
+import org.apache.airavata.model.error.InvalidRequestException;
+import org.apache.airavata.registry.api.exception.RegistryServiceException;
+import org.apache.thrift.TException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+@ControllerAdvice
+public class GlobalExceptionHandler {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+    @ExceptionHandler(AuthorizationException.class)
+    public ResponseEntity<ErrorResponse> 
handleAuthorizationException(AuthorizationException ex) {
+        logger.error("Authorization error", ex);
+        ErrorResponse errorResponse = new ErrorResponse(
+                HttpStatus.UNAUTHORIZED.value(), "Authorization failed", 
ex.getMessage(), System.currentTimeMillis());
+        return new ResponseEntity<>(errorResponse, HttpStatus.UNAUTHORIZED);
+    }
+
+    @ExceptionHandler(InvalidRequestException.class)
+    public ResponseEntity<ErrorResponse> 
handleInvalidRequestException(InvalidRequestException ex) {
+        logger.error("Invalid request", ex);
+        ErrorResponse errorResponse = new ErrorResponse(
+                HttpStatus.BAD_REQUEST.value(), "Invalid request", 
ex.getMessage(), System.currentTimeMillis());
+        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
+    }
+
+    @ExceptionHandler(RegistryServiceException.class)
+    public ResponseEntity<ErrorResponse> 
handleRegistryServiceException(RegistryServiceException ex) {
+        logger.error("Registry service error", ex);
+        ErrorResponse errorResponse = new ErrorResponse(
+                HttpStatus.INTERNAL_SERVER_ERROR.value(),
+                "Registry service error",
+                ex.getMessage(),
+                System.currentTimeMillis());
+        return new ResponseEntity<>(errorResponse, 
HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+
+    @ExceptionHandler(TException.class)
+    public ResponseEntity<ErrorResponse> handleTException(TException ex) {
+        logger.error("Thrift exception", ex);
+        ErrorResponse errorResponse = new ErrorResponse(
+                HttpStatus.INTERNAL_SERVER_ERROR.value(),
+                "Internal server error",
+                ex.getMessage(),
+                System.currentTimeMillis());
+        return new ResponseEntity<>(errorResponse, 
HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+
+    @ExceptionHandler(IllegalStateException.class)
+    public ResponseEntity<ErrorResponse> 
handleIllegalStateException(IllegalStateException ex) {
+        logger.error("Illegal state", ex);
+        ErrorResponse errorResponse = new ErrorResponse(
+                HttpStatus.UNAUTHORIZED.value(), "Authentication required", 
ex.getMessage(), System.currentTimeMillis());
+        return new ResponseEntity<>(errorResponse, HttpStatus.UNAUTHORIZED);
+    }
+
+    @ExceptionHandler(Exception.class)
+    public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
+        logger.error("Unexpected error", ex);
+        ErrorResponse errorResponse = new ErrorResponse(
+                HttpStatus.INTERNAL_SERVER_ERROR.value(),
+                "Internal server error",
+                ex.getMessage(),
+                System.currentTimeMillis());
+        return new ResponseEntity<>(errorResponse, 
HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+
+    public static class ErrorResponse {
+        private int status;
+        private String error;
+        private String message;
+        private long timestamp;
+
+        public ErrorResponse(int status, String error, String message, long 
timestamp) {
+            this.status = status;
+            this.error = error;
+            this.message = message;
+            this.timestamp = timestamp;
+        }
+
+        public int getStatus() {
+            return status;
+        }
+
+        public void setStatus(int status) {
+            this.status = status;
+        }
+
+        public String getError() {
+            return error;
+        }
+
+        public void setError(String error) {
+            this.error = error;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public void setMessage(String message) {
+            this.message = message;
+        }
+
+        public long getTimestamp() {
+            return timestamp;
+        }
+
+        public void setTimestamp(long timestamp) {
+            this.timestamp = timestamp;
+        }
+    }
+}
+
diff --git 
a/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/service/ComputeResourceService.java
 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/service/ComputeResourceService.java
new file mode 100644
index 0000000000..8dc1724b7f
--- /dev/null
+++ 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/service/ComputeResourceService.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.rest.api.service;
+
+import java.util.Map;
+import 
org.apache.airavata.model.appcatalog.computeresource.ComputeResourceDescription;
+import org.apache.airavata.model.appcatalog.computeresource.ResourceJobManager;
+import org.apache.airavata.model.security.AuthzToken;
+import org.apache.airavata.registry.api.exception.RegistryServiceException;
+import org.apache.airavata.registry.api.service.handler.RegistryServerHandler;
+import org.apache.airavata.service.security.IdentityContext;
+import org.apache.thrift.TException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ComputeResourceService {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(ComputeResourceService.class);
+    private final RegistryServerHandler registryServerHandler;
+
+    public ComputeResourceService() {
+        this.registryServerHandler = new RegistryServerHandler();
+    }
+
+    public String registerComputeResource(ComputeResourceDescription 
computeResourceDescription)
+            throws RegistryServiceException, TException {
+        return 
registryServerHandler.registerComputeResource(computeResourceDescription);
+    }
+
+    public ComputeResourceDescription getComputeResource(String 
computeResourceId)
+            throws RegistryServiceException, TException {
+        return registryServerHandler.getComputeResource(computeResourceId);
+    }
+
+    public Map<String, String> getAllComputeResourceNames() throws 
RegistryServiceException, TException {
+        return registryServerHandler.getAllComputeResourceNames();
+    }
+
+    public boolean updateComputeResource(String computeResourceId, 
ComputeResourceDescription computeResourceDescription)
+            throws RegistryServiceException, TException {
+        return registryServerHandler.updateComputeResource(computeResourceId, 
computeResourceDescription);
+    }
+
+    public boolean deleteComputeResource(String computeResourceId) throws 
RegistryServiceException, TException {
+        return registryServerHandler.deleteComputeResource(computeResourceId);
+    }
+
+    public String registerResourceJobManager(ResourceJobManager 
resourceJobManager)
+            throws RegistryServiceException, TException {
+        return 
registryServerHandler.registerResourceJobManager(resourceJobManager);
+    }
+
+    public ResourceJobManager getResourceJobManager(String 
resourceJobManagerId)
+            throws RegistryServiceException, TException {
+        return 
registryServerHandler.getResourceJobManager(resourceJobManagerId);
+    }
+
+    public boolean updateResourceJobManager(String resourceJobManagerId, 
ResourceJobManager updatedResourceJobManager)
+            throws RegistryServiceException, TException {
+        return 
registryServerHandler.updateResourceJobManager(resourceJobManagerId, 
updatedResourceJobManager);
+    }
+
+    public boolean deleteResourceJobManager(String resourceJobManagerId) 
throws RegistryServiceException, TException {
+        return 
registryServerHandler.deleteResourceJobManager(resourceJobManagerId);
+    }
+
+    public boolean deleteBatchQueue(String computeResourceId, String queueName)
+            throws RegistryServiceException, TException {
+        return registryServerHandler.deleteBatchQueue(computeResourceId, 
queueName);
+    }
+
+    private AuthzToken getAuthzToken() {
+        AuthzToken authzToken = IdentityContext.get();
+        if (authzToken == null) {
+            throw new IllegalStateException("AuthzToken not found in 
IdentityContext");
+        }
+        return authzToken;
+    }
+}
+
diff --git 
a/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/service/StorageResourceService.java
 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/service/StorageResourceService.java
new file mode 100644
index 0000000000..34f13343b5
--- /dev/null
+++ 
b/modules/airavata-rest-api/src/main/java/org/apache/airavata/rest/api/service/StorageResourceService.java
@@ -0,0 +1,73 @@
+/**
+ * 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.rest.api.service;
+
+import java.util.Map;
+import 
org.apache.airavata.model.appcatalog.storageresource.StorageResourceDescription;
+import org.apache.airavata.model.security.AuthzToken;
+import org.apache.airavata.registry.api.exception.RegistryServiceException;
+import org.apache.airavata.registry.api.service.handler.RegistryServerHandler;
+import org.apache.airavata.service.security.IdentityContext;
+import org.apache.thrift.TException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+@Service
+public class StorageResourceService {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(StorageResourceService.class);
+    private final RegistryServerHandler registryServerHandler;
+
+    public StorageResourceService() {
+        this.registryServerHandler = new RegistryServerHandler();
+    }
+
+    public String registerStorageResource(StorageResourceDescription 
storageResourceDescription)
+            throws RegistryServiceException, TException {
+        return 
registryServerHandler.registerStorageResource(storageResourceDescription);
+    }
+
+    public StorageResourceDescription getStorageResource(String 
storageResourceId)
+            throws RegistryServiceException, TException {
+        return registryServerHandler.getStorageResource(storageResourceId);
+    }
+
+    public Map<String, String> getAllStorageResourceNames() throws 
RegistryServiceException, TException {
+        return registryServerHandler.getAllStorageResourceNames();
+    }
+
+    public boolean updateStorageResource(String storageResourceId, 
StorageResourceDescription storageResourceDescription)
+            throws RegistryServiceException, TException {
+        return registryServerHandler.updateStorageResource(storageResourceId, 
storageResourceDescription);
+    }
+
+    public boolean deleteStorageResource(String storageResourceId) throws 
RegistryServiceException, TException {
+        return registryServerHandler.deleteStorageResource(storageResourceId);
+    }
+
+    private AuthzToken getAuthzToken() {
+        AuthzToken authzToken = IdentityContext.get();
+        if (authzToken == null) {
+            throw new IllegalStateException("AuthzToken not found in 
IdentityContext");
+        }
+        return authzToken;
+    }
+}
+
diff --git 
a/modules/airavata-rest-api/src/main/resources/application.properties 
b/modules/airavata-rest-api/src/main/resources/application.properties
new file mode 100644
index 0000000000..53c573ec0e
--- /dev/null
+++ b/modules/airavata-rest-api/src/main/resources/application.properties
@@ -0,0 +1,21 @@
+# Airavata REST API Configuration
+
+# Server Configuration
+server.port=8080
+
+# Application Configuration
+spring.application.name=airavata-rest-api
+
+# Logging Configuration
+logging.level.org.apache.airavata=DEBUG
+logging.level.org.springframework=INFO
+
+# SpringDoc OpenAPI Configuration
+springdoc.api-docs.enabled=true
+springdoc.api-docs.path=/v3/api-docs
+springdoc.swagger-ui.enabled=true
+springdoc.swagger-ui.path=/swagger-ui.html
+springdoc.swagger-ui.operationsSorter=alpha
+springdoc.swagger-ui.tagsSorter=alpha
+springdoc.swagger-ui.doc-expansion=none
+
diff --git a/modules/airavata-rest-api/src/main/resources/log4j2.xml 
b/modules/airavata-rest-api/src/main/resources/log4j2.xml
new file mode 100644
index 0000000000..d28e859bc9
--- /dev/null
+++ b/modules/airavata-rest-api/src/main/resources/log4j2.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+
+-->
+<Configuration status="WARN">
+  <Appenders>
+    <Console name="Console" target="SYSTEM_OUT">
+      <PatternLayout pattern="%d [%t] %-5p %c{30} %X - %m%n" />
+    </Console>
+  </Appenders>
+  <Loggers>
+    <logger name="ch.qos.logback" level="WARN" />
+    <logger name="org.apache.helix" level="WARN" />
+    <logger name="org.apache.zookeeper" level="ERROR" />
+    <logger name="org.apache.airavata" level="INFO" />
+    <logger name="org.hibernate" level="ERROR" />
+    <Root level="INFO">
+      <AppenderRef ref="Console" />
+    </Root>
+  </Loggers>
+</Configuration>
diff --git a/pom.xml b/pom.xml
index 2d860dc471..c778b2632c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -76,6 +76,7 @@ under the License.
         <module>modules/registry-db-migrator</module>
         <module>modules/registry-jpa-generator</module>
         <module>modules/ide-integration</module>
+        <module>modules/airavata-rest-api</module>
     </modules>
 
     <properties>

Reply via email to