All Products
Search
Document Center

Object Storage Service:Best Practices for Jindo CLI Integration

Last Updated:Jun 20, 2026

Jindo CLI is an Alibaba Cloud command-line interface (CLI) for managing the OSS-HDFS service. This guide explains how to wrap Jindo CLI commands into API operations with Java Spring Boot to programmatically manage the OSS-HDFS service. We cover two architectures, centralized deployment and decoupled deployment, suitable for operations and maintenance (O&M) control systems, automation scripts, and CI/CD tools.

Deployment options

Option 1: Centralized deployment

The business application and the Jindo CLI tool are deployed on the same server and integrated through local process calls. As shown in the following figure, the business application on the business server directly calls Jindo CLI to access the OSS-HDFS service.

image
  • Advantages: Simple implementation with no network overhead.

  • Disadvantages: The CLI execution consumes resources on the business application's server.

  • Use cases: This option is suitable for quick verification, internal automation scripts, and single management platforms.

Option 2: Decoupled deployment

The business application (Server A) is separated from Jindo CLI (Server B). The CLI is wrapped as an independent HTTP Agent service, and the two components communicate through RESTful APIs. As shown in the following figure, the business application remotely calls the HTTP Agent. The Agent then calls Jindo CLI to access the OSS-HDFS service.

image
  • Advantages: The AccessKey is isolated on the tool server. The business application and the tool are decoupled, which supports independent upgrades and cross-language calls. The tool can serve multiple business platforms at the same time.

  • Disadvantages: This option requires additional maintenance for the Agent service and introduces network overhead. By default, RESTful API calls do not require authentication. You must implement security checks, such as an authentication token mechanism.

  • Use cases: This option is suitable for production environments, especially for systems with high scalability requirements and scenarios that serve multiple business platforms.

Prerequisites

Before you begin, make sure the following prerequisites are met.

  1. OSS-HDFS service: You have a bucket with the OSS-HDFS service enabled. For more information, see Enable the OSS-HDFS service.

  2. Permissions: A RAM user has permissions to access the OSS-HDFS service. Follow the principle of least privilege. For example, if you only need read permissions, do not grant write or delete permissions. For more information, see Grant permissions to access the OSS-HDFS service.

  3. Server environment: You have at least one server, such as an ECS instance or a self-managed server. The server must be in the same region as the bucket, be able to access OSS over the internal network, and have the Java Development Kit (JDK) installed.

  4. Jindo SDK: The Jindo SDK is installed and configured.

    • Centralized deployment: Install the SDK on the business server.

    • Decoupled deployment: Install the SDK only on the Agent server (Server B).

    Jindo SDK installation

    1. Download and decompress the SDK

      This guide uses Jindo SDK 6.10.0 on the Linux x86 platform as an example.

      # Download the Jindo SDK package.
      wget https://jindodata-binary.oss-cn-shanghai.aliyuncs.com/release/6.10.0/jindosdk-6.10.0-linux.tar.gz
      # Decompress the package to the current directory.
      tar zxvf jindosdk-6.10.0-linux.tar.gz
    2. Create a configuration file

      Go to the /conf directory of the decompressed SDK.

      cd jindosdk-6.10.0-linux/conf/
    3. Create a jindosdk.cfg file with the following content. Replace the placeholder values with your actual configuration.

      [common]
      # Use the default settings.
      logger.dir = /tmp/jindo/
      logger.sync = false
      logger.consolelogger = false
      [jindosdk]
      # Replace with the OSS-HDFS endpoint of the region where your bucket is located.
      fs.oss.endpoint = cn-hangzhou.oss-dls.aliyuncs.com
      # Replace with your AccessKey ID.
      fs.oss.accessKeyId = yourAccessKeyId
      # Replace with your AccessKey secret.
      fs.oss.accessKeySecret = yourAccessKeySecret
    4. Configure Jindo CLI environment variables

      The following example assumes that Jindo CLI is installed in the /usr/lib/jindosdk-6.10.0-linux directory:

      export JINDOSDK_HOME=/usr/lib/jindosdk-6.10.0-linux
      export JINDOSDK_CONF_DIR=${JINDOSDK_HOME}/conf
      export PATH=${PATH}:${JINDOSDK_HOME}/bin
      Note

      To make the environment variables persistent, add these export commands to the ~/.bashrc file.

Option 1: Centralized deployment

The business application and Jindo CLI are on the same server. The business application directly calls Jindo CLI commands. This option is suitable for quick verification.

Step 1: Develop the backend API operation

Create a Spring Boot API operation that wraps the jindo fs -ls command.

Note

The main command can vary based on the Jindo SDK version. Some recent versions use jindo, while some earlier versions use jindofs.

Before you copy the code, we recommend that you manually run jindo -v or jindofs -v in the command line on your server to confirm the correct command for your environment. All examples in this article use the jindo command.

package com.example.jindotest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
@RestController
public class JindoController {
    @PostMapping("/jindo-ls")
    public ResponseEntity<String> executeJindoLs(@RequestParam("path") String path) {
        // Validate the path format.
        if (path == null || !path.startsWith("oss://")) {
            return ResponseEntity.badRequest().body("Invalid OSS path");
        }
        try {
            // Build the Jindo CLI command.
            ProcessBuilder processBuilder = new ProcessBuilder("jindo", "fs", "-ls", path);
            processBuilder.redirectErrorStream(true);
            // Start the process.
            Process process = processBuilder.start();
            // Read the command output.
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringBuilder result = new StringBuilder();  
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line).append("\n");
            }
            // Wait for the process to finish and check the exit code.
            int exitCode = process.waitFor();
            if (exitCode == 0) {
                // Return the command execution result.
                return ResponseEntity.ok(result.toString());
            } else {
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("Command failed with exit code: " + exitCode);
            }
        } catch (IOException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("Error executing command: " + e.getMessage());
        }
    }
}

Step 2: Verify the results

After deployment, use one of the following methods to verify the integration.

  1. Verify on the web interface

    Refer to the sample web interface code in the Appendix. Change the request path in the code to /jindo-ls. Access http://<public IP of the business server>:8080 to test the integration. You must open port 8080 for inbound traffic on the business server. Enter a file path, such as oss://my-bucket.cn-hangzhou.oss-dls.aliyuncs.com/, and click "List Files". The integration is successful if the interface displays the OSS file list.

  2. Verify with an API call (alternative)

    If you cannot deploy a frontend, you can use curl to test the backend API operation directly.

    curl -X POST "http://localhost:8080/jindo-ls?path=oss://<bucket-name>.<oss-hdfs-endpoint>/"

    If a list of files is returned, the integration is successful.

After verification, you can integrate other commands. For more information, see Common Jindo CLI commands in the appendix.

Option 2: Decoupled deployment

This option is recommended for a production environment. In this architecture, Jindo CLI is wrapped as an independent Agent service to separate responsibilities. This architecture is suitable for an internal network or scenarios with relatively low security requirements. For scenarios with high security requirements, you must implement an API access verification mechanism.

Step 1: Start the Agent service on Server B

  1. Create the Agent script

    Create a JindoCliHttpAgent.py file. The script is implemented in Python 3. You can use a similar approach for other technology stacks.

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import http.server
    import json
    import subprocess
    import socketserver
    import shlex  
    class CommandHandler(http.server.SimpleHTTPRequestHandler):
        def do_POST(self):
            if self.path == '/execute':
                # Read the request body.
                content_length = int(self.headers.get('Content-Length', 0))
                post_data = self.rfile.read(content_length)
                try:
                    # Parse the JSON request.
                    data = json.loads(post_data)
                    command = data.get('command', '')
                    # Validate the command parameter.
                    if not command:
                        self.send_response(400)
                        self.send_header('Content-Type', 'application/json')
                        self.end_headers()
                        self.wfile.write(json.dumps({'error': 'Missing "command" parameter'}).encode('utf-8'))
                        return
                    # Securely execute the command.
                    command_list = shlex.split(command)
                    output = subprocess.check_output(command_list, stderr=subprocess.STDOUT)
                    output_str = output.decode('utf-8').strip()
                    # Return the execution result.
                    self.send_response(200)
                    self.send_header('Content-Type', 'application/json')
                    self.end_headers()
                    self.wfile.write(json.dumps({'output': output_str}).encode('utf-8'))
                except Exception as e:
                    # Return the error message.
                    self.send_response(500)
                    self.send_header('Content-Type', 'application/json')
                    self.end_headers()
                    self.wfile.write(json.dumps({'error': str(e)}).encode('utf-8'))
            else:
                self.send_error(404, 'Not Found')
    # Multi-threaded HTTP server.
    class ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
        pass
    if __name__ == '__main__':
        # Start the HTTP Agent service.
        server_address = ('0.0.0.0', 8000)
        httpd = ThreadedHTTPServer(server_address, CommandHandler)
        print('Starting secure HTTP Agent on port 8000...')
        httpd.serve_forever()
    
  2. Configure the firewall

    Open port 8000 for inbound traffic on Server B. Allow access only from the private IP of the business server (Server A) and deny public network access.

  3. Add execute permissions

    chmod +x JindoCliHttpAgent.py
  4. Start the HTTP Agent service

    python3 JindoCliHttpAgent.py

    After the service starts, it listens for requests on port 8000. The startup output will be similar to the following:

    $ python JindoCliHttpAgent.py
    Starting HTTP Agent on port 8000...
  5. Verify the Agent service

    Open a new terminal to test whether the Agent is working properly. Replace <Bucketname> and <EndPoint> with the actual values. For example: oss://my-bucket.cn-hangzhou.oss-dls.aliyuncs.com/

    curl -X POST http://localhost:8000/execute \
       -H "Content-Type: application/json" \
       -d '{"command": "jindo fs -ls oss://<bucket-name>.<oss-hdfs-endpoint>/"}'

    If a file list in JSON format is returned, as shown in the following example, the Agent is deployed successfully.

    $curl -X POST http://localhost:8000/execute \
    >    -H "Content-Type: application/json" \
    >    -d '{"command": "jindo fs -ls oss://xxx.cn-hangzhou.oss-dls.aliyuncs.com/"}'
    {"output": "drwxrwxrwx\tadmin          \t2025-08-21 15:35\toss://xxx.cn-hangzhou.oss-dls.aliyuncs.com/.sysinfo\n-rwxrwxrwx\tadmin          \t2025-08-21 15:51\toss://xxx.cn-hangzhou.oss-dls.aliyuncs.com/a.txt\n-rwxrwxrwx\tadmin   \t       \t2025-08-21 16:41\toss://xxx.cn-hangzhou.oss-dls.aliyuncs.com/local_file.txt\ndrwxrwxrwx\tadmin          \t2025-08-21 17:30\toss://xxx.cn-hangzhou.oss-dls.aliyuncs.com/test/"}

Step 2: Integrate with the business system on Server A

Note

The main command can vary based on the Jindo SDK version. Some recent versions use jindo, while some earlier versions use jindofs.

Before you copy the code, we recommend that you first manually run jindo -v or jindofs -v on the command line of your server to confirm the correct command for your environment. All examples in this topic use jindo.

Create a Java API operation to call the Agent service. Replace <private IP of Server B> in the code with the actual private IP address of Server B.

package com.example.jindotest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
@RestController
public class JindoAgentController {
    @PostMapping("/jindo-agent-ls")
    public ResponseEntity<String> executeJindoLs(@RequestParam String path) {
        // Validate the path format.
        if (path == null || !path.startsWith("oss://")) {
            return ResponseEntity.badRequest().body("Invalid OSS path");
        }
        try {
            // Construct the JSON request body.
            String requestBody = String.format("{\"command\": \"jindo fs -ls %s\"}", path);
            // Call the Agent service by using curl.
            ProcessBuilder processBuilder = new ProcessBuilder(
                "curl", "-X", "POST", "http://<private IP of Server B>:8000/execute", 
                "-H", "Content-Type: application/json",
                "-d", requestBody
            );
            // Start the process.
            Process process = processBuilder.start();
            // Read the output stream.
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringBuilder result = new StringBuilder();  
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line).append("\n");
            }
            // Check the execution result.
            int exitCode = process.waitFor();
            if (exitCode == 0) {
                return ResponseEntity.ok(result.toString());
            } else {
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Command failed with exit code: " + exitCode);
            }
        } catch (IOException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error executing command: " + e.getMessage());
        }
    }
}
Note

In a production environment, use an HTTP client library, such as RestTemplate or WebClient, instead of curl to achieve better performance and error handling.

Step 3: Verify the results

  1. Check network connectivity

    If you use a decoupled deployment, you must ensure network connectivity between the servers. On Server A, execute ping <private IP of Server B>. A stable ping response with no packet loss proves that the two servers are connected.

    [ecs-assist-user@ixxx ~]$ ping 1xxx
    PING xxx (xxx) 56(84) bytes of data.
    64 bytes from 1xxx: icmp_seq=1 ttl=64 time=0.212 ms
    64 bytes from 1xxx: icmp_seq=2 ttl=64 time=0.138 ms
    64 bytes from 1xxx: icmp_seq=3 ttl=64 time=0.117 ms
    64 bytes from 1xxx: icmp_seq=4 ttl=64 time=0.130 ms
    ^C
    --- 1xxx ping statistics ---
    4 packets transmitted, 4 received, 0% packet loss, time 3000ms
    rtt min/avg/max/mdev = 0.117/0.149/0.212/0.038 ms
  2. Verify on the web interface

    Refer to the sample web interface code in the Appendix. Change the request path in the code to /jindo-agent-ls. Access http://<public IP of Server A>:8080 to test the integration. You must open port 8080 for inbound traffic on Server A. Enter a file path, such as oss://my-bucket.cn-hangzhou.oss-dls.aliyuncs.com/, and click "List Files". The integration is successful if the interface displays the OSS file list.

  3. Verify with an API call (alternative)

    If you cannot deploy a frontend, you can use curl to test the backend API operation directly.

    curl -X POST "http://localhost:8080/jindo-agent-ls?path=oss://<bucket-name>.<oss-hdfs-endpoint>/"

    If a list of files is returned, the integration is successful.

After verification, you can integrate other commands. For more information, see Common Jindo CLI commands in the appendix.

Security recommendations

Jindo CLI can manage OSS-HDFS service configurations and delete data. Improper use can have serious consequences. Before you deploy the service in a production environment, test it thoroughly in a test environment.

Core principles

  • Least privilege: Grant only the minimum permissions required to complete a task. Avoid excessive authorization.

  • Access control: Implement strict authentication and authorization mechanisms on the client side.

  • Concurrency control: Limit the number of concurrent operations to prevent configuration conflicts and data management issues.

Network security

  • Decoupled deployment: In a decoupled deployment, allow the Agent server to be accessed only from the private IP of the business server. Do not expose the Agent server to the internet.

  • Port management: Use a security group or firewall to precisely control access to ports.

References

Appendix

Common Jindo CLI commands

Command

Description

Example

stat

Displays the status of a file.

jindo fs -stat oss://<bucket-name>.<oss-hdfs-endpoint>/<file>

ls

Lists the files in a directory. The optional -R parameter recursively lists files.

jindo fs -ls [-R] oss://<bucket-name>.<oss-hdfs-endpoint>/<dir>

du

Displays the size of all files in a directory. The following parameters are optional:

  • -s: Displays the total size of the destination folder.

  • -h: Displays sizes in a human-readable format.

jindo fs -du oss://<bucket-name>.<oss-hdfs-endpoint>/<dir>

count

Displays the file size and the number of files. Use the optional -h parameter to display the file size in a human-readable format.

jindo fs -count -h oss://<bucket-name>.<oss-hdfs-endpoint>/<dir>

listUserGroupsMappings

Lists all user and group mappings.

jindo admin -listUserGroupsMappings -dlsUri oss://<bucket-name>.<oss-hdfs-endpoint> [-maxKeys <value>] [-marker <value>]

dumpInventory

Exports file metadata.

jindo admin -dumpInventory oss://<bucket-name>.<oss-hdfs-endpoint>/<dir>

putConfig

Sets service features, such as directory protection.

jindo admin putConfig -dlsUri oss://<bucket-name>.<oss-hdfs-endpoint> -conf <key1=value1> -conf <key2=value2> ...]

getConfig

Gets configuration information, such as directory protection information.

jindo admin getConfig -dlsUri oss://<bucket-name>.<oss-hdfs-endpoint> -name <keys>

Sample web interface code

Save the following code as index.html and place it in the src/main/resources/static/ directory of your Spring Boot project:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Jindo CLI Management Interface</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 1000px;
            margin: 50px auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background-color: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
            text-align: center;
            border-bottom: 2px solid #007bff;
            padding-bottom: 10px;
        }
        .form-group {
            margin-bottom: 20px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #555;
        }
        input[type="text"] {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 16px;
            box-sizing: border-box;
        }
        button {
            background-color: #007bff;
            color: white;
            padding: 12px 24px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            margin-right: 10px;
        }
        button:hover {
            background-color: #0056b3;
        }
        button:disabled {
            background-color: #6c757d;
            cursor: not-allowed;
        }
        .result {
            margin-top: 20px;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 4px;
            background-color: #f8f9fa;
            min-height: 100px;
            font-family: monospace;
            white-space: pre-wrap;
            font-size: 14px;
        }
        .loading {
            color: #007bff;
            font-style: italic;
        }
        .error {
            color: #dc3545;
            background-color: #f8d7da;
            border-color: #f5c6cb;
        }
        .success {
            color: #155724;
            background-color: #d4edda;
            border-color: #c3e6cb;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1> Jindo CLI Management Interface</h1>
        <div class="form-group">
            <label for="ossPath">OSS Path:</label>
            <input type="text" id="ossPath" 
                   value="oss://your-bucket-name.cn-hangzhou.oss-dls.aliyuncs.com/" 
                   placeholder="Enter OSS path (e.g., oss://bucket-name.endpoint/)">
        </div>
        <div class="form-group">
            <button onclick="listFiles()">List Files (ls)</button>
            <button onclick="clearResult()">Clear Result</button>
        </div>
        <div id="result" class="result">
            Ready to execute Jindo CLI commands...
        </div>
    </div>
    <script>
        function listFiles() {
            const path = document.getElementById('ossPath').value;
            const resultDiv = document.getElementById('result');
            if (!path || !path.startsWith('oss://')) {
                resultDiv.innerHTML = 'Error: Please enter a valid OSS path starting with "oss://"';
                resultDiv.className = 'result error';
                return;
            }
            // Show loading
            resultDiv.innerHTML = 'Loading... Please wait';
            resultDiv.className = 'result loading';
            // Call backend API
            fetch('/jindo-ls', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: 'path=' + encodeURIComponent(path)
            })
            .then(response => {
                if (response.ok) {
                    return response.text();
                } else {
                    throw new Error('HTTP ' + response.status + ': ' + response.statusText);
                }
            })
            .then(data => {
                resultDiv.innerHTML = 'Success:\n\n' + data;
                resultDiv.className = 'result success';
            })
            .catch(error => {
                resultDiv.innerHTML = 'Error:\n\n' + error.message;
                resultDiv.className = 'result error';
            });
        }
        function clearResult() {
            document.getElementById('result').innerHTML = 'Ready to execute Jindo CLI commands...';
            document.getElementById('result').className = 'result';
        }
        // Allow Enter key to trigger listFiles
        document.getElementById('ossPath').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                listFiles();
            }
        });
    </script>
</body>
</html>