All Products
Search
Document Center

Object Storage Service:Best practices for Jindo CLI integration

Last Updated:Feb 28, 2026

This document describes how to wrap Jindo CLI commands as REST API operations using Java Spring Boot. Two deployment architectures are covered -- centralized and decoupled -- with working code examples for operations and maintenance (O&M) control systems, automation scripts, and continuous integration and continuous delivery (CI/CD) tools.

When to use this pattern

Wrapping Jindo CLI as a REST API suits the following scenarios:

  • Rapid prototyping and internal tooling -- Build a web-based management interface for OSS-HDFS without developing a full SDK integration.

  • Operations not available via SDK -- Expose Jindo CLI administrative commands (such as putConfig, getConfig, or dumpInventory) that have no direct SDK equivalent.

  • Legacy system integration -- Connect existing platforms that rely on RESTful APIs to Jindo CLI functionality without modifying the platforms themselves.

  • Cross-language access -- Provide a single HTTP Agent that any language or tool can call, rather than embedding Jindo SDK in every application.

For high-throughput production scenarios that require type-safe APIs, connection pooling, and built-in retry logic, use the Jindo software development kit (SDK) directly instead.

Deployment options

Two architectures are available. The following table summarizes the trade-offs.

DimensionCentralized deploymentDecoupled deployment
ArchitectureBusiness application and Jindo CLI on the same serverBusiness application (Server A) and Jindo CLI (Server B) separated
CommunicationLocal process callsRESTful API over internal network
AdvantagesSimple implementation, no network overheadAccessKey isolated on tool server; independent upgrades; cross-language calls; serves multiple business platforms
DisadvantagesCLI execution consumes resources of the main business applicationAdditional maintenance for Agent service; network overhead; no built-in authentication on RESTful API calls
Best forQuick verification, internal automation scripts, single management platformsProduction environments, high extensibility requirements, multi-platform scenarios

Prerequisites

Before you begin, make sure that you have:

  • A bucket with the OSS-HDFS service enabled. For more information, see Enable the OSS-HDFS service

  • A Resource Access Management (RAM) user with permissions to access the OSS-HDFS service. Follow the principle of least privilege -- if only read permissions are needed, do not grant write or delete permissions. For more information, see Grant permissions to access the OSS-HDFS service

  • At least one server (ECS instance or self-managed server) that meets these conditions: located in the same region as the bucket, able to access Object Storage Service (OSS) over the internal network, and has Java Development Kit (JDK) installed

  • Jindo SDK installed and configured:

    • Centralized deployment: Install on the business server

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

Jindo SDK installation steps (skip if already installed)

  1. Download and decompress the SDK

    The following example uses Jindo SDK 6.10.0 on the Linux x86 platform.

    # 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 to the current directory
    tar zxvf jindosdk-6.10.0-linux.tar.gz
  2. Create a configuration file

    Navigate to the /conf directory of the decompressed SDK.

    cd jindosdk-6.10.0-linux/conf/
  3. Configure the SDK

    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 permanent, add the export commands to the ~/.bashrc file.

Option 1: Centralized deployment

The business application and Jindo CLI run on the same server. The business application calls Jindo CLI commands directly through local process execution.

image

Step 1: Develop the backend API operation

Create a Spring Boot REST controller that wraps the jindo fs -ls command.

Note

The main command varies by Jindo SDK version. Some newer versions use jindo, while some earlier versions use jindofs. Run jindo -v or jindofs -v on your server to determine the correct command. All examples in this document use jindo.

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 the deployment is complete, verify the integration in one of the following ways.

Verify on the web interface

For the sample web interface code, see 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. 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 the "List Files" button. If the interface displays the OSS file list, the integration is successful.

image

Verify with curl (alternative)

If a frontend is not available, use curl to test the backend API operation directly.

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

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

After successful verification, integrate other commands into your system. For the full list, see the Common Jindo CLI commands table in the appendix.

Option 2: Decoupled deployment

This architecture suits production environments. Jindo CLI is wrapped as an independent HTTP Agent service, separating responsibilities between the business application and the CLI tool. This architecture works well in internal network environments. For scenarios with strict security requirements, implement your own API access verification mechanism, such as an authentication token.

image

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.

#!/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 address 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.

image.png

5. Verify the Agent service

Open a new terminal to test whether the Agent is working correctly. Replace <Bucketname> and <EndPoint> with your 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://<Bucketname>.<EndPoint>/"}'

If a file list in JSON format is returned, the Agent is deployed successfully.

image

Step 2: Integrate the business system on Server A

Note

The main command varies by Jindo SDK version. Some newer versions use jindo, while some earlier versions use jindofs. Run jindo -v or jindofs -v on your server to determine the correct command. All examples in this document 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 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 for better performance and error handling.

Step 3: Verify the results

1. Check network connectivity

Make sure that the servers can connect to each other. On Server A, run ping <Private IP of Server B>. Stable ping responses without packet loss confirm connectivity.

image

2. Verify on the web interface

For the sample web interface code, see 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. 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 the "List Files" button. If the interface displays the OSS file list, the integration is successful.

image

3. Verify with curl (alternative)

If a frontend is not available, use curl to test the backend API operation directly.

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

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

After successful verification, integrate other commands into your system. For the full list, see the Common Jindo CLI commands table in the appendix.

Security recommendations

Jindo CLI provides capabilities for OSS-HDFS service configuration management and data deletion. Improper use can have serious consequences. Test thoroughly in a test environment before deploying to production.

Core principles

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

  • Access control: Implement strict identity verification 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: Allow the Agent server to be accessed only from the private IP address of the business server. Do not expose the Agent server to the internet.

  • Port management: Use security groups or firewalls to precisely control access to ports.

Input validation

  • Validate all user-supplied input before passing it to Jindo CLI commands. The code examples in this document validate that paths start with oss://. In production, apply stricter validation to prevent command injection.

  • Use shlex.split() (as shown in the Python Agent example) or ProcessBuilder (as shown in the Java examples) to avoid shell interpretation of untrusted input.

References

Appendix

Common Jindo CLI commands

CommandDescriptionExample
statDisplay the status of a file.jindo fs -stat oss://<bucket-name>.<oss-hdfs-endpoint>/<file>
lsList the files in a directory. The optional -R parameter enables recursive listing.jindo fs -ls [-R] oss://<bucket-name>.<oss-hdfs-endpoint>/<dir>
duDisplay the size of all files in a directory. Optional parameters: -s (total size of the destination folder), -h (human-readable format).jindo fs -du oss://<bucket-name>.<oss-hdfs-endpoint>/<dir>
countDisplay the file size and the number of files. The optional -h parameter shows the file size in human-readable format.jindo fs -count -h oss://<bucket-name>.<oss-hdfs-endpoint>/<dir>
listUserGroupsMappingsList the relationships for all users and groups.jindo admin -listUserGroupsMappings -dlsUri oss://<bucket-name>.<oss-hdfs-endpoint> [-maxKeys <value>] [-marker <value>]
dumpInventoryExport file metadata.jindo admin -dumpInventory oss://<bucket-name>.<oss-hdfs-endpoint>/<dir>
putConfigSet service attributes, such as directory protection.jindo admin putConfig -dlsUri oss://<bucket-name>.<oss-hdfs-endpoint> -conf <key1=value1> -conf <key2=value2> ...]
getConfigGet 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>