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, ordumpInventory) 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.
| Dimension | Centralized deployment | Decoupled deployment |
|---|---|---|
| Architecture | Business application and Jindo CLI on the same server | Business application (Server A) and Jindo CLI (Server B) separated |
| Communication | Local process calls | RESTful API over internal network |
| Advantages | Simple implementation, no network overhead | AccessKey isolated on tool server; independent upgrades; cross-language calls; serves multiple business platforms |
| Disadvantages | CLI execution consumes resources of the main business application | Additional maintenance for Agent service; network overhead; no built-in authentication on RESTful API calls |
| Best for | Quick verification, internal automation scripts, single management platforms | Production 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)
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.
Step 1: Develop the backend API operation
Create a Spring Boot REST controller that wraps the jindo fs -ls command.
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.

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.
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.py4. Start the HTTP Agent service
python3 JindoCliHttpAgent.pyAfter the service starts, it listens for requests on port 8000.

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.

Step 2: Integrate the business system on Server A
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());
}
}
}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.

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.

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
| Command | Description | Example |
stat | Display the status of a file. | jindo fs -stat oss://<bucket-name>.<oss-hdfs-endpoint>/<file> |
ls | List the files in a directory. The optional -R parameter enables recursive listing. | jindo fs -ls [-R] oss://<bucket-name>.<oss-hdfs-endpoint>/<dir> |
du | Display 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> |
count | Display 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> |
listUserGroupsMappings | List the relationships for all users and groups. | jindo admin -listUserGroupsMappings -dlsUri oss://<bucket-name>.<oss-hdfs-endpoint> [-maxKeys <value>] [-marker <value>] |
dumpInventory | Export file metadata. | jindo admin -dumpInventory oss://<bucket-name>.<oss-hdfs-endpoint>/<dir> |
putConfig | Set service attributes, such as directory protection. | jindo admin putConfig -dlsUri oss://<bucket-name>.<oss-hdfs-endpoint> -conf <key1=value1> -conf <key2=value2> ...] |
getConfig | Get 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>