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

dimuthuupe pushed a commit to branch file-server
in repository https://gitbox.apache.org/repos/asf/airavata.git

commit e18975880ce038d205a9046b406fea11e8b58079
Author: DImuthuUpe <[email protected]>
AuthorDate: Mon Dec 16 08:11:04 2024 -0500

    File server initial implementation
---
 modules/agent-framework/connection-service/pom.xml |   3 +-
 .../airavata/helix/impl/task/AiravataTask.java     |   2 +-
 modules/file-server/pom.xml                        |  81 +++++++++++++++
 .../main/assembly/file-service-bin-assembly.xml    |  90 +++++++++++++++++
 .../file/server/FileServerApplication.java         |  16 +++
 .../file/server/FileServerConfiguration.java       |  47 +++++++++
 .../file/server/controller/FileController.java     |  87 ++++++++++++++++
 .../file/server/model/AiravataDirectory.java       |  49 +++++++++
 .../airavata/file/server/model/AiravataFile.java   |  47 +++++++++
 .../file/server/model/FileUploadResponse.java      |  47 +++++++++
 .../file/server/service/AirvataFileService.java    |  88 ++++++++++++++++
 .../file/server/service/ProcessDataManager.java    |  57 +++++++++++
 .../src/main/resources/application.properties      |  12 +++
 .../distribution/bin/file-service-daemon.sh        | 112 +++++++++++++++++++++
 .../resources/distribution/bin/file-service.sh     |  70 +++++++++++++
 .../src/main/resources/distribution/bin/setenv.sh  |  46 +++++++++
 .../distribution/conf/application.properties       |  12 +++
 .../main/resources/distribution/conf/log4j2.xml    |  53 ++++++++++
 pom.xml                                            |   2 +
 19 files changed, 918 insertions(+), 3 deletions(-)

diff --git a/modules/agent-framework/connection-service/pom.xml 
b/modules/agent-framework/connection-service/pom.xml
index 619c872692..3ee06a2ccb 100644
--- a/modules/agent-framework/connection-service/pom.xml
+++ b/modules/agent-framework/connection-service/pom.xml
@@ -15,7 +15,6 @@
         <maven.compiler.source>11</maven.compiler.source>
         <maven.compiler.target>11</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <spring.boot.version>3.2.4</spring.boot.version>
         <jul-to-slf4j.version>1.7.0</jul-to-slf4j.version>
         <json.version>20240303</json.version>
         <javax.version>2.0.1.Final</javax.version>
@@ -159,4 +158,4 @@
         </plugins>
     </build>
 
-</project>
\ No newline at end of file
+</project>
diff --git 
a/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/AiravataTask.java
 
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/AiravataTask.java
index d2208d5293..0715a17bdb 100644
--- 
a/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/AiravataTask.java
+++ 
b/modules/airavata-helix/helix-spectator/src/main/java/org/apache/airavata/helix/impl/task/AiravataTask.java
@@ -496,7 +496,7 @@ public abstract class AiravataTask extends AbstractTask {
         }
     }
 
-    private void loadContext() throws TaskOnFailException {
+    protected void loadContext() throws TaskOnFailException {
         try {
             logger.info("Loading context for task " + getTaskId());
             processModel = getRegistryServiceClient().getProcess(processId);
diff --git a/modules/file-server/pom.xml b/modules/file-server/pom.xml
new file mode 100644
index 0000000000..a7065bdff3
--- /dev/null
+++ b/modules/file-server/pom.xml
@@ -0,0 +1,81 @@
+<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/maven-v4_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>file-server</artifactId>
+    <name>airavata-file-server</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <version>${spring.boot.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-to-slf4j</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.airavata</groupId>
+            <artifactId>task-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.airavata</groupId>
+            <artifactId>helix-spectator</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <compilerArgs>
+                        <arg>-parameters</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>${maven.assembly.plugin}</version>
+                <executions>
+                    <execution>
+                        <id>file-service-distribution-package</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <tarLongFileMode>posix</tarLongFileMode>
+                            <finalName>${file.service.dist.name}</finalName>
+                            <descriptors>
+                                
<descriptor>src/main/assembly/file-service-bin-assembly.xml</descriptor>
+                            </descriptors>
+                            <attach>false</attach>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <file.service.dist.name>File-Service-0.01</file.service.dist.name>
+    </properties>
+</project>
diff --git 
a/modules/file-server/src/main/assembly/file-service-bin-assembly.xml 
b/modules/file-server/src/main/assembly/file-service-bin-assembly.xml
new file mode 100644
index 0000000000..8d72d47391
--- /dev/null
+++ b/modules/file-server/src/main/assembly/file-service-bin-assembly.xml
@@ -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.
+
+-->
+<!DOCTYPE assembly [
+        <!ELEMENT assembly 
(id|includeBaseDirectory|baseDirectory|formats|fileSets|dependencySets)*>
+        <!ELEMENT id (#PCDATA)>
+        <!ELEMENT includeBaseDirectory (#PCDATA)>
+        <!ELEMENT baseDirectory (#PCDATA)>
+        <!ELEMENT formats (format)*>
+        <!ELEMENT format (#PCDATA)>
+        <!ELEMENT fileSets (fileSet)*>
+        <!ELEMENT fileSet (directory|outputDirectory|fileMode|includes)*>
+        <!ELEMENT directory (#PCDATA)>
+        <!ELEMENT outputDirectory (#PCDATA)>
+        <!ELEMENT includes (include)*>
+        <!ELEMENT include (#PCDATA)>
+        <!ELEMENT dependencySets (dependencySet)*>
+        <!ELEMENT dependencySet 
(outputDirectory|outputFileNameMapping|includes)*>
+        ]>
+<assembly>
+    <id>bin</id>
+    <includeBaseDirectory>true</includeBaseDirectory>
+    <baseDirectory>${file.service.dist.name}</baseDirectory>
+    <formats>
+        <format>tar.gz</format>
+        <format>zip</format>
+    </formats>
+
+    <fileSets>
+        <fileSet>
+            <directory>src/main/resources/distribution/bin</directory>
+            <outputDirectory>bin</outputDirectory>
+            <fileMode>777</fileMode>
+            <includes>
+                <include>*.sh</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>src/main/resources/distribution/conf</directory>
+            <outputDirectory>conf</outputDirectory>
+            <includes>
+                <include>application.properties</include>
+                <include>log4j2.xml</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>./</directory>
+            <outputDirectory>logs</outputDirectory>
+            <excludes>
+                <exclude>*/**</exclude>
+            </excludes>
+        </fileSet>
+        <fileSet>
+            <directory>target</directory>
+            <outputDirectory>lib</outputDirectory>
+            <includes>
+                <include>*.jar</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+
+    <dependencySets>
+        <dependencySet>
+            <useProjectArtifact>false</useProjectArtifact>
+            <outputDirectory>lib</outputDirectory>
+            <includes>
+                <include>*</include>
+            </includes>
+        </dependencySet>
+    </dependencySets>
+
+</assembly>
diff --git 
a/modules/file-server/src/main/java/org/apache/airavata/file/server/FileServerApplication.java
 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/FileServerApplication.java
new file mode 100644
index 0000000000..42bf099184
--- /dev/null
+++ 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/FileServerApplication.java
@@ -0,0 +1,16 @@
+package org.apache.airavata.file.server;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import 
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration;
+import 
org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication(scanBasePackages = "org.apache.airavata.file.server",
+        exclude = {GroovyTemplateAutoConfiguration.class})
+@EnableConfigurationProperties(FileServerConfiguration.class)
+public class FileServerApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(FileServerApplication.class, args);
+    }
+}
diff --git 
a/modules/file-server/src/main/java/org/apache/airavata/file/server/FileServerConfiguration.java
 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/FileServerConfiguration.java
new file mode 100644
index 0000000000..e4b6df32d8
--- /dev/null
+++ 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/FileServerConfiguration.java
@@ -0,0 +1,47 @@
+package org.apache.airavata.file.server;
+
+import org.apache.airavata.common.utils.ServerSettings;
+import org.apache.airavata.common.utils.ThriftClientPool;
+import org.apache.airavata.helix.core.support.adaptor.AdaptorSupportImpl;
+import org.apache.airavata.helix.task.api.support.AdaptorSupport;
+import org.apache.airavata.registry.api.RegistryService;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties
+public class FileServerConfiguration {
+
+    @Bean
+    public AdaptorSupport adaptorSupport() {
+        return AdaptorSupportImpl.getInstance();
+    }
+
+    //regserver.server.host
+    @Value("${regserver.server.host:localhost}")
+    private String registryServerHost;
+    //regserver.server.port
+
+    @Value("${regserver.server.port:8970}")
+    private int registryServerPort;
+
+    @Bean
+    public ThriftClientPool<RegistryService.Client> registryClientPool() {
+        GenericObjectPoolConfig<RegistryService.Client> poolConfig = new 
GenericObjectPoolConfig<>();
+        poolConfig.setMaxTotal(100);
+        poolConfig.setMinIdle(5);
+        poolConfig.setBlockWhenExhausted(true);
+        poolConfig.setTestOnBorrow(true);
+        poolConfig.setTestWhileIdle(true);
+        // must set timeBetweenEvictionRunsMillis since eviction doesn't run 
unless that is positive
+        poolConfig.setTimeBetweenEvictionRunsMillis(5L * 60L * 1000L);
+        poolConfig.setNumTestsPerEvictionRun(10);
+        poolConfig.setMaxWaitMillis(3000);
+
+        return new ThriftClientPool<>(
+                RegistryService.Client::new, poolConfig, registryServerHost, 
registryServerPort);
+    }
+}
diff --git 
a/modules/file-server/src/main/java/org/apache/airavata/file/server/controller/FileController.java
 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/controller/FileController.java
new file mode 100644
index 0000000000..b735da7765
--- /dev/null
+++ 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/controller/FileController.java
@@ -0,0 +1,87 @@
+package org.apache.airavata.file.server.controller;
+
+import org.apache.airavata.file.server.model.AiravataDirectory;
+import org.apache.airavata.file.server.model.FileUploadResponse;
+import org.apache.airavata.file.server.service.AirvataFileService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+
+import java.io.File;
+import java.nio.file.Path;
+
+@Controller
+public class FileController {
+
+    private final static Logger logger = 
LoggerFactory.getLogger(FileController.class);
+
+    @Autowired
+    private AirvataFileService fileService;
+
+    @GetMapping("/list/{live}/{processId}")
+    @ResponseBody
+    public Object listFilesRoot(@PathVariable String live,
+                            @PathVariable String processId) throws Exception {
+        try {
+            return fileService.listFiles(processId, "");
+        } catch (Exception e) {
+            logger.error("Failed to list files for path {} in process {}", 
"root path", processId, e);
+            throw e;
+        }
+    }
+    @GetMapping("/list/{live}/{processId}/{subPath}")
+    @ResponseBody
+    public Object listFiles(@PathVariable String live,
+                            @PathVariable String processId,
+                            @PathVariable String subPath) throws Exception {
+        try {
+            return fileService.listFiles(processId, subPath);
+        } catch (Exception e) {
+            logger.error("Failed to list files for path {} in process {}", 
subPath, processId, e);
+            throw e;
+        }
+    }
+
+    @GetMapping("/download/{isLive}/{processId}/{subPath}")
+    @ResponseBody
+    public ResponseEntity downloadFile(@PathVariable String isLive,
+                                                 @PathVariable String 
processId,
+                                                 @PathVariable String subPath) 
{
+
+        try {
+            Path localPath = fileService.downloadFile(processId, subPath);
+            Resource resource = new UrlResource(localPath.toUri());
+            return ResponseEntity.ok()
+                    .header(HttpHeaders.CONTENT_DISPOSITION,
+                            "attachment; filename=\"" + new 
File(subPath).getName() + "\"")
+                    .body(resource);
+        } catch (Exception e) {
+            return ResponseEntity.internalServerError()
+                    .body("An internal server error occurred: " + 
e.getMessage());
+        }
+    }
+
+
+    @PostMapping("/upload-file")
+    @ResponseBody
+    public FileUploadResponse uploadFile(@RequestParam("file") MultipartFile 
file) {
+        String name = "";
+
+        String uri = ServletUriComponentsBuilder.fromCurrentContextPath()
+                .path("/download/")
+                .path(name)
+                .toUriString();
+
+        return new FileUploadResponse(name, uri, file.getContentType(), 
file.getSize());
+    }
+
+
+}
diff --git 
a/modules/file-server/src/main/java/org/apache/airavata/file/server/model/AiravataDirectory.java
 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/model/AiravataDirectory.java
new file mode 100644
index 0000000000..bed98f1ffb
--- /dev/null
+++ 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/model/AiravataDirectory.java
@@ -0,0 +1,49 @@
+package org.apache.airavata.file.server.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AiravataDirectory {
+    private String directoryName;
+    private long createdTime;
+
+    private List<AiravataFile> innerFiles = new ArrayList<>();
+    private List<AiravataDirectory> innerDirectories = new ArrayList<>();
+
+    public AiravataDirectory(String directoryName, long createdTime) {
+        this.directoryName = directoryName;
+        this.createdTime = createdTime;
+    }
+
+    public String getDirectoryName() {
+        return directoryName;
+    }
+
+    public void setDirectoryName(String directoryName) {
+        this.directoryName = directoryName;
+    }
+
+    public long getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setCreatedTime(long createdTime) {
+        this.createdTime = createdTime;
+    }
+
+    public List<AiravataFile> getInnerFiles() {
+        return innerFiles;
+    }
+
+    public void setInnerFiles(List<AiravataFile> innerFiles) {
+        this.innerFiles = innerFiles;
+    }
+
+    public List<AiravataDirectory> getInnerDirectories() {
+        return innerDirectories;
+    }
+
+    public void setInnerDirectories(List<AiravataDirectory> innerDirectories) {
+        this.innerDirectories = innerDirectories;
+    }
+}
diff --git 
a/modules/file-server/src/main/java/org/apache/airavata/file/server/model/AiravataFile.java
 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/model/AiravataFile.java
new file mode 100644
index 0000000000..f2a174f6a7
--- /dev/null
+++ 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/model/AiravataFile.java
@@ -0,0 +1,47 @@
+package org.apache.airavata.file.server.model;
+
+public class AiravataFile {
+    private String fileName;
+    private long fileSize;
+    private long createdTime;
+    private long updatedTime;
+
+    public AiravataFile(String fileName, long fileSize, long createdTime, long 
updatedTime) {
+        this.fileName = fileName;
+        this.fileSize = fileSize;
+        this.createdTime = createdTime;
+        this.updatedTime = updatedTime;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    public long getFileSize() {
+        return fileSize;
+    }
+
+    public void setFileSize(long fileSize) {
+        this.fileSize = fileSize;
+    }
+
+    public long getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setCreatedTime(long createdTime) {
+        this.createdTime = createdTime;
+    }
+
+    public long getUpdatedTime() {
+        return updatedTime;
+    }
+
+    public void setUpdatedTime(long updatedTime) {
+        this.updatedTime = updatedTime;
+    }
+}
diff --git 
a/modules/file-server/src/main/java/org/apache/airavata/file/server/model/FileUploadResponse.java
 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/model/FileUploadResponse.java
new file mode 100644
index 0000000000..0f061cad2b
--- /dev/null
+++ 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/model/FileUploadResponse.java
@@ -0,0 +1,47 @@
+package org.apache.airavata.file.server.model;
+
+public class FileUploadResponse {
+    private String name;
+    private String uri;
+    private String type;
+    private long size;
+
+    public FileUploadResponse(String name, String uri, String type, long size) 
{
+        this.name = name;
+        this.uri = uri;
+        this.type = type;
+        this.size = size;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public long getSize() {
+        return size;
+    }
+
+    public void setSize(long size) {
+        this.size = size;
+    }
+}
diff --git 
a/modules/file-server/src/main/java/org/apache/airavata/file/server/service/AirvataFileService.java
 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/service/AirvataFileService.java
new file mode 100644
index 0000000000..949d59c77b
--- /dev/null
+++ 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/service/AirvataFileService.java
@@ -0,0 +1,88 @@
+package org.apache.airavata.file.server.service;
+
+import org.apache.airavata.agents.api.AgentAdaptor;
+import org.apache.airavata.agents.api.FileMetadata;
+import org.apache.airavata.common.utils.ThriftClientPool;
+import org.apache.airavata.file.server.model.AiravataDirectory;
+import org.apache.airavata.file.server.model.AiravataFile;
+import org.apache.airavata.helix.task.api.support.AdaptorSupport;
+import org.apache.airavata.registry.api.RegistryService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+@Service
+public class AirvataFileService {
+
+    private final static Logger logger = 
LoggerFactory.getLogger(AirvataFileService.class);
+
+    @Autowired
+    private AdaptorSupport adaptorSupport;
+
+    @Autowired
+    ThriftClientPool<RegistryService.Client> registryClientPool;
+
+    private AgentAdaptor getAgentAdaptor(ProcessDataManager dataManager, 
String processId) throws Exception {
+        AgentAdaptor agentAdaptor;
+        try {
+            agentAdaptor = dataManager.getAgentAdaptor();
+        } catch (Exception e) {
+            logger.error("Failed to fetch adaptor for process {}", processId, 
e);
+            throw new Exception("Failed to fetch adaptor for process " + 
processId, e);
+        }
+        return agentAdaptor;
+    }
+    public AiravataDirectory listFiles(String processId, String subPath) 
throws Exception {
+        ProcessDataManager dataManager = new 
ProcessDataManager(registryClientPool, processId, adaptorSupport);
+
+        AgentAdaptor agentAdaptor = getAgentAdaptor(dataManager, processId);
+
+        AiravataDirectory airavataDirectory = new AiravataDirectory("root", 
System.currentTimeMillis()); // TODO: set dir name
+
+        subPath = dataManager.getBaseDir() + (subPath.isEmpty()? "" : "/" + 
subPath);
+        logger.info("Listing files in path {}", subPath);
+        List<String> fileList = agentAdaptor.listDirectory(subPath); // TODO: 
Validate if this is a file or dir
+        for (String fileOrDir : fileList) {
+            logger.info("Processing file {}", fileOrDir);
+            FileMetadata fileMetadata = agentAdaptor.getFileMetadata(subPath + 
"/" + fileOrDir);
+            airavataDirectory.getInnerFiles().add(new 
AiravataFile(fileMetadata.getName(),
+                    fileMetadata.getSize(), System.currentTimeMillis(), 
System.currentTimeMillis())); // TODO: update created and updated time
+        }
+
+        return airavataDirectory;
+    }
+    public Path downloadFile(String processId, String subPath) throws 
Exception {
+
+        ProcessDataManager dataManager = new 
ProcessDataManager(registryClientPool, processId, adaptorSupport);
+
+        AgentAdaptor agentAdaptor = getAgentAdaptor(dataManager, processId);
+        subPath = dataManager.getBaseDir() + (subPath.isEmpty()? "" : "/" + 
subPath);
+
+        if (agentAdaptor.doesFileExist(subPath)) {
+            Path tempFile = Files.createTempFile("tempfile_", ".data");
+            tempFile.toFile().deleteOnExit();
+            try {
+                agentAdaptor.downloadFile(subPath, 
tempFile.toFile().getAbsolutePath());
+                return tempFile;
+            } catch (Exception e) {
+                logger.error("Failed to download file {} from process {} to 
local path {}",
+                        subPath, processId, 
tempFile.toFile().getAbsolutePath());
+                try {
+                    tempFile.toFile().delete();
+                } catch (Exception ignore) {
+                    // Ignore
+                }
+                throw e;
+            }
+        } else {
+            throw new Exception("File " + subPath + "  does not exist in 
process " + processId);
+        }
+    }
+}
diff --git 
a/modules/file-server/src/main/java/org/apache/airavata/file/server/service/ProcessDataManager.java
 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/service/ProcessDataManager.java
new file mode 100644
index 0000000000..b253ed11ee
--- /dev/null
+++ 
b/modules/file-server/src/main/java/org/apache/airavata/file/server/service/ProcessDataManager.java
@@ -0,0 +1,57 @@
+package org.apache.airavata.file.server.service;
+
+import org.apache.airavata.agents.api.AgentAdaptor;
+import org.apache.airavata.common.utils.ThriftClientPool;
+import org.apache.airavata.helix.impl.task.TaskOnFailException;
+import org.apache.airavata.helix.impl.task.staging.OutputDataStagingTask;
+import org.apache.airavata.helix.task.api.support.AdaptorSupport;
+import org.apache.airavata.model.experiment.ExperimentModel;
+import org.apache.airavata.model.process.ProcessModel;
+import org.apache.airavata.registry.api.RegistryService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.UUID;
+
+public class ProcessDataManager extends OutputDataStagingTask {
+
+    private final static Logger logger = 
LoggerFactory.getLogger(ProcessDataManager.class);
+
+    private String processId;
+    private AdaptorSupport adaptorSupport;
+
+    private ProcessModel process;
+    ExperimentModel experiment;
+
+    public ProcessDataManager(ThriftClientPool<RegistryService.Client> 
registryClientPool,
+                              String processId, AdaptorSupport adaptorSupport) 
throws Exception {
+
+        this.adaptorSupport = adaptorSupport;
+        RegistryService.Client regClient = registryClientPool.getResource();
+        try {
+            process = regClient.getProcess(processId);
+            experiment = regClient.getExperiment(process.getExperimentId());
+
+            setTaskId(UUID.randomUUID().toString());
+            setProcessId(processId);
+            setExperimentId(process.getExperimentId());
+            setGatewayId(experiment.getGatewayId());
+            loadContext();
+
+            registryClientPool.returnResource(regClient);
+        } catch (Exception e) {
+            logger.error("Failed to initialize the output data mover for 
process {}", processId, e);
+            registryClientPool.returnBrokenResource(regClient);
+            throw e;
+        }
+        this.processId = processId;
+    }
+
+    public AgentAdaptor getAgentAdaptor() throws TaskOnFailException {
+        return getComputeResourceAdaptor(adaptorSupport);
+    }
+
+    public String getBaseDir() throws Exception {
+        return getTaskContext().getWorkingDir();
+    }
+}
diff --git a/modules/file-server/src/main/resources/application.properties 
b/modules/file-server/src/main/resources/application.properties
new file mode 100644
index 0000000000..3d5477b4dd
--- /dev/null
+++ b/modules/file-server/src/main/resources/application.properties
@@ -0,0 +1,12 @@
+spring.servlet.multipart.max-file-size=10MB
+# max request size
+spring.servlet.multipart.max-request-size=10MB
+# files storage location (stores all files uploaded via REST API)
+storage.location=./uploads
+
+regserver.server.host=localhost
+regserver.server.port=8970
+credential.store.server.host=localhost
+credential.store.server.port=8960
+
+server.port=8050
diff --git 
a/modules/file-server/src/main/resources/distribution/bin/file-service-daemon.sh
 
b/modules/file-server/src/main/resources/distribution/bin/file-service-daemon.sh
new file mode 100644
index 0000000000..131d99df54
--- /dev/null
+++ 
b/modules/file-server/src/main/resources/distribution/bin/file-service-daemon.sh
@@ -0,0 +1,112 @@
+#!/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.
+
+. `dirname $0`/setenv.sh
+# Capture user's working dir before changing directory
+CWD="$PWD"
+cd ${AIRAVATA_HOME}/bin
+LOGO_FILE="logo.txt"
+
+JAVA_OPTS=" -Dspring.config.location=${AIRAVATA_HOME}/conf/ 
-Dairavata.home=${AIRAVATA_HOME} 
-Dlog4j.configurationFile=file:${AIRAVATA_HOME}/conf/log4j2.xml"
+AIRAVATA_COMMAND=""
+EXTRA_ARGS=""
+SERVERS=""
+LOGO=true
+IS_SUBSET=false
+SUBSET=""
+DEFAULT_LOG_FILE="${AIRAVATA_HOME}/logs/airavata-daemon.out"
+LOG_FILE=$DEFAULT_LOG_FILE
+
+SERVICE_NAME="File Service"
+PID_PATH_NAME="${AIRAVATA_HOME}/bin/service-pid"
+
+case $1 in
+    start)
+        echo "Starting $SERVICE_NAME ..."
+        if [ ! -f $PID_PATH_NAME ]; then
+            nohup java ${JAVA_OPTS} -classpath "${AIRAVATA_CLASSPATH}" \
+            org.apache.airavata.file.server.FileServerApplication 
${AIRAVATA_COMMAND} $* > $LOG_FILE 2>&1 &
+            echo $! > $PID_PATH_NAME
+            echo "$SERVICE_NAME started ..."
+        else
+            echo "$SERVICE_NAME is already running ..."
+        fi
+    ;;
+    stop)
+        if [ -f $PID_PATH_NAME ]; then
+            PID=$(cat $PID_PATH_NAME);
+            echo "$SERVICE_NAME stopping ..."
+            kill $PID;
+            RETRY=0
+            while kill -0 $PID 2> /dev/null; do
+                echo "Waiting for the process $PID to be stopped"
+                RETRY=`expr ${RETRY} + 1`
+                if [ "${RETRY}" -gt "20" ]
+                then
+                    echo "Forcefully killing the process as it is not 
responding ..."
+                    kill -9 $PID
+                fi
+                sleep 1
+            done
+            echo "$SERVICE_NAME stopped ..."
+            rm $PID_PATH_NAME
+        else
+            echo "$SERVICE_NAME is not running ..."
+        fi
+    ;;
+    restart)
+        if [ -f $PID_PATH_NAME ]; then
+            PID=$(cat $PID_PATH_NAME);
+            echo "$SERVICE_NAME stopping ...";
+            kill $PID;
+            RETRY=0
+            while kill -0 $PID 2> /dev/null; do
+                echo "Waiting for the process $PID to be stopped"
+                RETRY=`expr ${RETRY} + 1`
+                if [ "${RETRY}" -gt "20" ]
+                then
+                    echo "Forcefully killing the process as it is not 
responding ..."
+                    kill -9 $PID
+                fi
+                sleep 1
+            done
+            echo "$SERVICE_NAME stopped ...";
+            rm $PID_PATH_NAME
+            echo "$SERVICE_NAME starting ..."
+            nohup java ${JAVA_OPTS} -classpath "${AIRAVATA_CLASSPATH}" \
+            org.apache.airavata.file.server.FileServerApplication 
${AIRAVATA_COMMAND} $* > $LOG_FILE 2>&1 &
+            echo $! > $PID_PATH_NAME
+            echo "$SERVICE_NAME started ..."
+        else
+            echo "$SERVICE_NAME is not running ..."
+        fi
+    ;;
+    -h)
+        echo "Usage: file-service-daemon.sh"
+
+        echo "command options:"
+        echo "  start               Start server in daemon mode"
+        echo "  stop                Stop server running in daemon mode"
+        echo "  restart             Restart server in daemon mode"
+           echo "  -log <LOG_FILE>     Where to redirect stdout/stderr 
(defaults to $DEFAULT_LOG_FILE)"
+        echo "  -h                  Display this help and exit"
+        shift
+        exit 0
+    ;;
+esac
diff --git 
a/modules/file-server/src/main/resources/distribution/bin/file-service.sh 
b/modules/file-server/src/main/resources/distribution/bin/file-service.sh
new file mode 100644
index 0000000000..231f5dfa26
--- /dev/null
+++ b/modules/file-server/src/main/resources/distribution/bin/file-service.sh
@@ -0,0 +1,70 @@
+#!/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.
+
+. `dirname $0`/setenv.sh
+# Capture user's working dir before changing directory
+CWD="$PWD"
+cd ${AIRAVATA_HOME}/bin
+LOGO_FILE="logo.txt"
+
+JAVA_OPTS="-Dspring.config.location=${AIRAVATA_HOME}/conf/ 
-Dairavata.home=${AIRAVATA_HOME} 
-Dlog4j.configurationFile=file:${AIRAVATA_HOME}/conf/log4j2.xml"
+AIRAVATA_COMMAND=""
+EXTRA_ARGS=""
+SERVERS=""
+IS_SUBSET=false
+SUBSET=""
+DEFAULT_LOG_FILE="${AIRAVATA_HOME}/logs/airavata.out"
+LOG_FILE=$DEFAULT_LOG_FILE
+
+# parse command arguments
+for var in "$@"
+do
+    case ${var} in
+        -xdebug)
+               AIRAVATA_COMMAND="${AIRAVATA_COMMAND}"
+            JAVA_OPTS="$JAVA_OPTS -Xdebug -Xnoagent 
-Xrunjdwp:transport=dt_socket,server=y,address=*:8000"
+            shift
+           ;;
+        -log)
+            shift
+            LOG_FILE="$1"
+            shift
+            # If relative path, expand to absolute path using the user's $CWD
+            if [ -z "`echo "$LOG_FILE" | egrep "^/"`" ]; then
+                LOG_FILE="${CWD}/${LOG_FILE}"
+            fi
+        ;;
+        -h)
+            echo "Usage: file-service.sh"
+
+            echo "command options:"
+            echo "  -xdebug             Start Connection Service under JPDA 
debugger"
+            echo "  -h                  Display this help and exit"
+            shift
+            exit 0
+        ;;
+           *)
+               EXTRA_ARGS="${EXTRA_ARGS} ${var}"
+            shift
+        ;;
+    esac
+done
+
+java ${JAVA_OPTS} -classpath "${AIRAVATA_CLASSPATH}" \
+    org.apache.airavata.file.server.FileServerApplication ${AIRAVATA_COMMAND} 
$*
diff --git a/modules/file-server/src/main/resources/distribution/bin/setenv.sh 
b/modules/file-server/src/main/resources/distribution/bin/setenv.sh
new file mode 100644
index 0000000000..3f1d7632bf
--- /dev/null
+++ b/modules/file-server/src/main/resources/distribution/bin/setenv.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# 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.
+
+# resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ]; do
+  ls=`ls -ld "$PRG"`
+  link=`expr "$ls" : '.*-> \(.*\)$'`
+  if expr "$link" : '.*/.*' > /dev/null; then
+    PRG="$link"
+  else
+    PRG=`dirname "$PRG"`/"$link"
+  fi
+done
+
+PRGDIR=`dirname "$PRG"`
+
+# Only set AIRAVATA_HOME if not already set
+[ -z "$AIRAVATA_HOME" ] && AIRAVATA_HOME=`cd "$PRGDIR/.." ; pwd`
+
+AIRAVATA_CLASSPATH=""
+
+for f in "$AIRAVATA_HOME"/lib/*.jar
+do
+  AIRAVATA_CLASSPATH="$AIRAVATA_CLASSPATH":$f
+done
+
+export AIRAVATA_HOME
+export AIRAVATA_CLASSPATH
\ No newline at end of file
diff --git 
a/modules/file-server/src/main/resources/distribution/conf/application.properties
 
b/modules/file-server/src/main/resources/distribution/conf/application.properties
new file mode 100644
index 0000000000..3d5477b4dd
--- /dev/null
+++ 
b/modules/file-server/src/main/resources/distribution/conf/application.properties
@@ -0,0 +1,12 @@
+spring.servlet.multipart.max-file-size=10MB
+# max request size
+spring.servlet.multipart.max-request-size=10MB
+# files storage location (stores all files uploaded via REST API)
+storage.location=./uploads
+
+regserver.server.host=localhost
+regserver.server.port=8970
+credential.store.server.host=localhost
+credential.store.server.port=8960
+
+server.port=8050
diff --git 
a/modules/file-server/src/main/resources/distribution/conf/log4j2.xml 
b/modules/file-server/src/main/resources/distribution/conf/log4j2.xml
new file mode 100644
index 0000000000..e38f7ff011
--- /dev/null
+++ b/modules/file-server/src/main/resources/distribution/conf/log4j2.xml
@@ -0,0 +1,53 @@
+<?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>
+        <RollingFile name="RollingFileAppender" fileName="../logs/airavata.log"
+                     
filePattern="logs/${date:yyyy-MM}/airavata-log-%d{MM-dd-yyyy}-%i.log.gz">
+            <PatternLayout>
+                <Pattern>%d [%t] %-5p %c{30} %X - %m%n</Pattern>
+            </PatternLayout>
+            <Policies>
+                <OnStartupTriggeringPolicy />
+                <TimeBasedTriggeringPolicy />
+                <SizeBasedTriggeringPolicy size="50 MB" />
+            </Policies>
+            <DefaultRolloverStrategy max="20" />
+        </RollingFile>
+    </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"/>
+            <AppenderRef ref="RollingFileAppender"/>
+        </Root>
+    </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 3f57e2f519..f9ff579af0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,6 +102,7 @@
         <module>modules/agent-framework</module>
         <module>tools</module>
         <module>modules/ide-integration</module>
+        <module>modules/file-server</module>
     </modules>
 
     <properties>
@@ -151,6 +152,7 @@
         
<org.apache.commons.pool2.version>2.7.0</org.apache.commons.pool2.version>
         <helix.version>0.9.7</helix.version>
         <httpclient.version>4.5.14</httpclient.version>
+        <spring.boot.version>3.2.4</spring.boot.version>
     </properties>
 
     <dependencies>


Reply via email to