An advanced Node.js module for running Java JAR files from local paths or URLs with advanced JRE auto-download features, intelligent caching, and automatic validation.
- ✅ Flexible JAR execution: Supports local paths and remote URLs
- ✅ Auto-download JRE: Automatically downloads the Java Runtime Environment if not present
- ✅ Cross-platform: Windows, macOS and Linux
- ✅ Multiple Java versions: Supports Java 8, 11, 17, 21 and other versions
- ✅ Intelligent caching: Reuses downloaded JREs for subsequent executions
- ✅ JAR validation: Automatic verification of JAR file integrity
- ✅ Debug logging: Detailed monitoring of operations
- ✅ Advanced error handling: Specific and informative errors
- ✅ Flexible configuration: Environment variables and custom options
npm install java-js-node
- Node.js: >= 14.0.0
- Operating System: Windows, macOS, or Linux
- Architecture: x64, ARM64, or ARM
- Disk Space: ~100MB for downloaded JRE
const { runJar } = require('java-js-node');
// Run a local JAR
const result = await runJar('./test/helloworld-1.0.0-SNAPSHOT.jar');
console.log('Exit code:', result.exitCode);
console.log('Output:', result.stdout);
const { runJar } = require('java-js-node');
// Basic execution with security validation
async function helloWorld() {
// ✅ JAR validation mandatory for security
const isValid = await runJar.validateJar('./test/helloworld-1.0.0-SNAPSHOT.jar');
if (!isValid) {
throw new Error('JAR unsafe or corrupted');
}
const result = await runJar('./test/helloworld-1.0-SNAPSHOT.jar');
console.log('Exit code:', result.exitCode);
console.log('Output:', result.stdout);
}
const { runJar } = require('java-js-node');
async function runWithArgs() {
// Run JAR with configuration parameters
const result = await runJar('./my-app.jar', ['--config', 'dev.json', '--debug']);
console.log('Application executed with code:', result.exitCode);
}
const { runJar } = require('java-js-node');
async function startSpringApp() {
console.log('🚀 Starting Spring Boot application...');
// ✅ Mandatory validation before execution
const isValid = await runJar.validateJar('./target/spring-app.jar');
if (!isValid) {
throw new Error('Invalid Spring Boot JAR');
}
// Run with production configuration
const result = await runJar('./target/spring-app.jar', [
'--spring.profiles.active=prod',
'--server.port=8080'
]);
if (result.exitCode === 0) {
console.log('✅ Spring Boot started successfully');
} else {
console.error('❌ Startup error:', result.stderr);
}
}
const { runJar } = require('java-js-node');
async function processBatch() {
// ✅ Always validate before execution
const isValid = await runJar.validateJar('./batch-processor.jar');
if (!isValid) {
throw new Error('Invalid processor JAR');
}
// Process files with specific parameters
const result = await runJar('./batch-processor.jar', [
'--input', './data/input.csv',
'--output', './data/output.csv',
'--batch-size', '1000'
]);
console.log('Batch completed with exit code:', result.exitCode);
}
const { create } = require('java-js-node');
async function sequentialExecution() {
const runner = create({ debug: true });
const jars = [
{ path: './init-database.jar', args: ['--setup'] },
{ path: './load-data.jar', args: ['--import'] },
{ path: './process-data.jar', args: ['--transform'] },
{ path: './cleanup.jar', args: ['--finalize'] }
];
for (const jar of jars) {
console.log(`Executing: ${jar.path}`);
// ✅ Validation for each JAR
const isValid = await runner.validateJar(jar.path);
if (!isValid) {
throw new Error(`Invalid JAR ${jar.path}`);
}
const result = await runner.runJar(jar.path, jar.args);
if (result.exitCode !== 0) {
throw new Error(`Error in ${jar.path}: ${result.stderr}`);
}
console.log(`✅ ${jar.path} completed`);
}
console.log('🎉 Sequential pipeline completed!');
}
const { create } = require('java-js-node');
async function parallelExecution() {
const runner = create({ debug: true });
const services = [
{ name: 'auth-service', jar: './auth.jar', args: ['--port', '8081'] },
{ name: 'user-service', jar: './users.jar', args: ['--port', '8082'] },
{ name: 'notification-service', jar: './notifications.jar', args: ['--port', '8083'] }
];
console.log('🚀 Starting services in parallel...');
// Execute all services simultaneously
const promises = services.map(async (service) => {
console.log(`Starting ${service.name}...`);
// ✅ Mandatory validation
const isValid = await runner.validateJar(service.jar);
if (!isValid) {
throw new Error(`Invalid ${service.name} JAR`);
}
const result = await runner.runJar(service.jar, service.args);
return { service: service.name, result };
});
try {
const results = await Promise.all(promises);
results.forEach(({ service, result }) => {
if (result.exitCode === 0) {
console.log(`✅ ${service} started successfully`);
} else {
console.error(`❌ Error in ${service}:`, result.stderr);
}
});
} catch (error) {
console.error('Error in parallel startup:', error.message);
}
}
const { create, validateJar } = require('java-js-node');
async function cicdPipeline() {
const runner = create({
debug: true,
jreMajor: 17,
workdir: './build/runtime'
});
const jarPath = './target/application.jar';
console.log('🔄 Starting CI/CD pipeline...\n');
// 1. ✅ Validate built JAR
console.log('1️⃣ JAR validation...');
const isValid = await validateJar(jarPath);
if (!isValid) {
throw new Error('Invalid built JAR - build failed');
}
console.log('✅ JAR valid\n');
// 2. Unit tests
console.log('2️⃣ Running unit tests...');
const testResult = await runner.runJar(jarPath, ['--test', '--unit']);
if (testResult.exitCode !== 0) {
throw new Error('Unit tests failed');
}
console.log('✅ Unit tests passed\n');
// 3. Integration tests
console.log('3️⃣ Running integration tests...');
const integrationResult = await runner.runJar(jarPath, ['--test', '--integration']);
if (integrationResult.exitCode !== 0) {
throw new Error('Integration tests failed');
}
console.log('✅ Integration tests passed\n');
// 4. Deploy to staging
console.log('4️⃣ Deploy to staging environment...');
await runner.runJar(jarPath, ['--deploy', '--env', 'staging']);
console.log('✅ Staging deploy completed\n');
// 5. End-to-end tests
console.log('5️⃣ Running E2E tests...');
const e2eResult = await runner.runJar(jarPath, ['--test', '--e2e']);
if (e2eResult.exitCode !== 0) {
throw new Error('E2E tests failed');
}
console.log('✅ E2E tests passed\n');
console.log('🎉 CI/CD pipeline completed successfully!');
}
const { create, JavaRunnerError } = require('java-js-node');
class ServiceManager {
constructor() {
this.runner = create({
debug: true,
jreMajor: 17
});
this.services = new Map();
}
async startService(name, jarPath, args = []) {
try {
console.log(`🚀 Starting service ${name}...`);
// ✅ Mandatory validation
const isValid = await this.runner.validateJar(jarPath);
if (!isValid) {
throw new JavaRunnerError(`Invalid JAR ${name}`, 'INVALID_JAR');
}
const result = await this.runner.runJar(jarPath, args);
this.services.set(name, { status: 'running', result });
console.log(`✅ Service ${name} started`);
return result;
} catch (error) {
this.services.set(name, { status: 'error', error: error.message });
console.error(`❌ Error starting ${name}:`, error.message);
throw error;
}
}
async stopService(name) {
if (this.services.has(name)) {
this.services.delete(name);
console.log(`🛑 Service ${name} stopped`);
}
}
getStatus() {
return Array.from(this.services.entries()).map(([name, service]) => ({
name,
status: service.status,
exitCode: service.result?.exitCode
}));
}
}
async function manageMicroservices() {
const manager = new ServiceManager();
try {
// Start core services
await manager.startService('auth-service', './auth.jar', ['--port', '8081']);
await manager.startService('user-service', './users.jar', ['--port', '8082']);
await manager.startService('api-gateway', './gateway.jar', ['--port', '8080']);
console.log('\n📊 Service status:');
console.log(manager.getStatus());
} catch (error) {
console.error('\n❌ Error in microservice management:', error.message);
// Cleanup services in case of error
console.log('🧹 Service cleanup...');
await manager.stopService('auth-service');
await manager.stopService('user-service');
await manager.stopService('api-gateway');
}
}
These practical examples show how to use Node Java Runner in real scenarios, from simple JAR execution to complex enterprise pipeline management. Each example follows the project's security rules with mandatory JAR validation and appropriate error handling.
const { create } = require('java-js-node');
// Create an instance with custom options
const runner = create({
workdir: './custom-runtime', // Custom working directory
jreMajor: 17, // Specific Java version
javaPath: '/custom/java/path', // Custom Java path
debug: true // Enable debug logging
});
// Run JAR with arguments
const result = await runner.runJar('./my-app.jar', ['--port', '8080', '--env', 'production']);
// Verify if a file is a valid JAR
const isValid = await runner.validateJar('./app.jar');
if (isValid) {
console.log('✅ Valid JAR');
}
// Check Java installation
const javaInfo = await runner.checkJavaVersion();
console.log(`Java ${javaInfo.version} found at: ${javaInfo.path}`);
Listeners allow you to capture JAR output in real-time during execution, useful for advanced logging, monitoring, and integration with external systems.
const { create } = require('java-js-node');
async function esempioListenerBasico() {
// Create the runner with custom configuration
const runner = create({
debug: true,
jreMajor: 17
});
// Initialize with the correct pattern
await runner.init({
workdir: './runtime',
minimumVersion: 17
});
console.log('🚀 Starting JAR with basic listener...\n');
// Run JAR with listener for real-time logging
const result = await runner.runJar('./test/helloworld-1.0.0-SNAPSHOT.jar', [], {
onStdout: (data) => {
console.log(`📤 STDOUT: ${data.trim()}`);
},
onStderr: (data) => {
console.log(`⚠️ STDERR: ${data.trim()}`);
}
});
console.log(`\n✅ Execution completed - Exit code: ${result.exitCode}`);
}
const { create } = require('java-js-node');
async function esempioListenerAvanzato() {
const runner = create({
debug: true,
jreMajor: 17
});
await runner.init({
workdir: './runtime'
});
const logs = [];
let startTime = Date.now();
console.log('🔍 Starting JAR with advanced listener...\n');
const result = await runner.runJar('./test/helloworld-1.0.0-SNAPSHOT.jar', [], {
onStdout: (data) => {
const timestamp = new Date().toISOString();
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
logs.push({ type: 'INFO', timestamp, elapsed, data: data.trim() });
console.log(`[${timestamp}] [${elapsed}s] 📤 ${data.trim()}`);
},
onStderr: (data) => {
const timestamp = new Date().toISOString();
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
logs.push({ type: 'ERROR', timestamp, elapsed, data: data.trim() });
console.log(`[${timestamp}] [${elapsed}s] ❌ ${data.trim()}`);
}
});
// Post-execution analysis
const errors = logs.filter(log => log.type === 'ERROR');
const totalTime = ((Date.now() - startTime) / 1000).toFixed(2);
console.log(`\n📊 Execution statistics:`);
console.log(` ⏱️ Total time: ${totalTime}s`);
console.log(` 📝 Total logs: ${logs.length}`);
console.log(` ❌ Errors: ${errors.length}`);
console.log(` ✅ Exit code: ${result.exitCode}`);
}
const { create } = require('java-js-node');
const fs = require('fs').promises;
class CustomLogger {
constructor(logFile) {
this.logFile = logFile;
this.startTime = Date.now();
}
async log(level, message) {
const timestamp = new Date().toISOString();
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(2);
const logEntry = `[${timestamp}] [${elapsed}s] [${level}] ${message}\n`;
console.log(logEntry.trim());
// Save to file
try {
await fs.appendFile(this.logFile, logEntry);
} catch (error) {
console.error(`Error saving log: ${error.message}`);
}
}
info(message) { return this.log('INFO', message); }
error(message) { return this.log('ERROR', message); }
warn(message) { return this.log('WARN', message); }
}
async function esempioIntegrazioneInit() {
const runner = create({
debug: true,
jreMajor: 17
});
// Complete init() pattern with configuration
await runner.init({
workdir: './runtime',
minimumVersion: 17,
jreMajor: 17
});
const logger = new CustomLogger('./logs/app-runtime.log');
console.log('🔧 Starting JAR with custom logger...\n');
const result = await runner.runJar('./test/helloworld-1.0.0-SNAPSHOT.jar', [], {
onStdout: async (data) => {
await logger.info(`Application: ${data.trim()}`);
},
onStderr: async (data) => {
await logger.error(`Application Error: ${data.trim()}`);
}
});
await logger.info(`Execution completed with exit code: ${result.exitCode}`);
console.log(`\n✅ Logs saved in: ./logs/app-runtime.log`);
console.log(`📊 Final exit code: ${result.exitCode}`);
}
const { create } = require('java-js-node');
class PerformanceMonitor {
constructor() {
this.metrics = {
startTime: null,
endTime: null,
memoryUsage: [],
cpuUsage: [],
logCount: 0,
errorCount: 0
};
}
start() {
this.metrics.startTime = Date.now();
console.log('📊 Performance monitoring started');
}
recordMemory() {
const usage = process.memoryUsage();
this.metrics.memoryUsage.push({
timestamp: Date.now(),
rss: Math.round(usage.rss / 1024 / 1024), // MB
heapUsed: Math.round(usage.heapUsed / 1024 / 1024) // MB
});
}
log(type, data) {
this.metrics.logCount++;
if (type === 'ERROR') {
this.metrics.errorCount++;
}
const elapsed = this.metrics.startTime ?
((Date.now() - this.metrics.startTime) / 1000).toFixed(2) : '0.00';
console.log(`[${elapsed}s] ${type === 'ERROR' ? '❌' : '📤'} ${data.trim()}`);
}
end() {
this.metrics.endTime = Date.now();
this.printReport();
}
printReport() {
const totalTime = ((this.metrics.endTime - this.metrics.startTime) / 1000).toFixed(2);
const avgMemory = this.metrics.memoryUsage.length > 0 ?
Math.round(this.metrics.memoryUsage.reduce((sum, m) => sum + m.heapUsed, 0) / this.metrics.memoryUsage.length) : 0;
console.log('\n📊 PERFORMANCE REPORT');
console.log('='.repeat(50));
console.log(`⏱️ Execution time: ${totalTime}s`);
console.log(`📝 Total logs: ${this.metrics.logCount}`);
console.log(`❌ Errors: ${this.metrics.errorCount}`);
console.log(`🧠 Average memory: ${avgMemory} MB`);
console.log(`📊 Memory samples: ${this.metrics.memoryUsage.length}`);
console.log('='.repeat(50));
}
}
async function esempioMonitoraggioPerformance() {
const runner = create({
debug: true,
jreMajor: 17
});
// Optimized init() pattern with configuration
await runner.init({
workdir: './runtime',
minimumVersion: 17
});
const monitor = new PerformanceMonitor();
monitor.start();
// Interval for memory sampling every 500ms
const memoryInterval = setInterval(() => {
monitor.recordMemory();
}, 500);
console.log('⚡ Starting JAR with performance monitoring...\n');
const result = await runner.runJar('./test/helloworld-1.0.0-SNAPSHOT.jar', [], {
onStdout: (data) => monitor.log('INFO', data),
onStderr: (data) => monitor.log('ERROR', data)
});
clearInterval(memoryInterval);
monitor.end();
console.log(`\n🎯 Exit code: ${result.exitCode}`);
console.log(`📈 Detailed metrics available in monitor.metrics`);
}
These examples show how to use listeners for:
- Real-time logging during JAR execution
- Advanced categorization and formatting of logs
- Integration with custom logging systems
- Performance monitoring during execution
- init() pattern for optimal Java runtime configuration
Listeners are particularly useful for applications that require continuous monitoring, advanced debugging, or integration with enterprise logging systems.
Executes a JAR file with the specified arguments.
Parameters:
jarPath
(string): Path to the JAR file (local or URL)args
(string[]): Array of arguments to pass to the JAR
Returns: Promise<Object>
{
stdout: string, // Standard output
stderr: string, // Standard errors
exitCode: number // Process exit code
}
Example:
const result = await runJar('./my-app.jar', ['--config', 'prod.json']);
Validates if a file is a valid JAR.
Parameters:
jarPath
(string): Path to the JAR file
Returns: Promise<boolean>
Example:
const isValid = await validateJar('./application.jar');
Finds the path to the Java executable in the system.
Returns: Promise<string|null>
Example:
const javaPath = await findJavaPath();
if (javaPath) {
console.log('Java found at:', javaPath);
}
Checks the installed Java version.
Returns: Promise<Object>
{
path: string, // Path to Java executable
version: string, // Java version
available: boolean // Availability
}
Example:
const info = await checkJavaVersion();
console.log(`Java ${info.version} at ${info.path}`);
Creates a new NodeJavaRunner instance.
Options:
workdir
(string): Working directory for downloads and cache (default:./runtime
)jreMajor
(number): Major Java version to use (default: 17)javaPath
(string): Custom path to Java executabledebug
(boolean): Enable debug logging (default: false)
Executes a JAR file with the specified arguments.
Validates a JAR file.
Finds the Java path in the system.
Checks the Java version.
The module supports the following environment variables for configuration:
Variable | Description | Default |
---|---|---|
JAVA_JS_NODE_WORKDIR |
Custom working directory | ./runtime |
JAVA_JS_NODE_JRE_MAJOR |
Major Java version | 17 |
JAVA_JS_NODE_DEBUG |
Enable debug logging | false |
JAVA_HOME |
Custom Java installation directory | - |
# Configuration via environment variables
export JAVA_JS_NODE_DEBUG=true
export JAVA_JS_NODE_WORKDIR=/tmp/java-js-node
export JAVA_JS_NODE_JRE_MAJOR=11
export JAVA_HOME=/opt/jdk-17
node my-app.js
Problem: The system cannot find Java.
Solutions:
-
Install Java manually:
# Ubuntu/Debian sudo apt-get install openjdk-17-jre # CentOS/RHEL sudo yum install java-17-openjdk # macOS brew install openjdk@17 # Windows (using Chocolatey) choco install openjdk17
-
Set
JAVA_HOME
:export JAVA_HOME=/path/to/java
-
Use custom configuration:
const runner = create({ javaPath: '/custom/java/path' });
Problem: The specified file is not a valid JAR.
Solutions:
- Verify that the file has
.jar
extension - Check that the file is not corrupted
- Use validation before execution:
const isValid = await validateJar('./app.jar'); if (!isValid) { throw new Error('Invalid JAR'); }
Problem: JRE download failed due to lack of space.
Solutions:
- Free up disk space
- Use a custom working directory with more space:
const runner = create({ workdir: '/path/with/more/space' });
Problem: Unable to download or extract JRE.
Solutions:
- Check the permissions of the working directory
- Use a writable directory:
const runner = create({ workdir: process.env.HOME + '/java-runner' });
const { runJar } = require('java-js-node');
async function startRemoteServer() {
try {
// Download and run JAR from URL
const result = await runJar('https://example.com/app.jar', ['--server', '--port', '3000']);
if (result.exitCode === 0) {
console.log('Server started successfully');
console.log(result.stdout);
} else {
console.error('Server startup error:', result.stderr);
}
} catch (error) {
console.error('Error:', error.message);
}
}
const { create, validateJar } = require('java-js-node');
async function deployApplication() {
const runner = create({
debug: true,
jreMajor: 17,
workdir: './build/runtime'
});
// 1. Validate the built JAR
const jarPath = './target/app-1.0.0.jar';
const isValid = await validateJar(jarPath);
if (!isValid) {
throw new Error('Invalid built JAR');
}
// 2. Run integration tests
console.log('Running integration tests...');
const testResult = await runner.runJar(jarPath, ['--test', '--integration']);
if (testResult.exitCode !== 0) {
throw new Error('Integration tests failed');
}
// 3. Deploy to production
console.log('Deploying to production...');
await runner.runJar(jarPath, ['--deploy', '--env', 'prod']);
console.log('Deploy completed successfully!');
}
const { runJar, JavaRunnerError } = require('java-js-node');
async function safeJarExecution() {
try {
const result = await runJar('./app.jar');
if (result.exitCode === 0) {
console.log('Execution completed');
} else {
console.warn(`JAR terminated with code ${result.exitCode}`);
}
} catch (error) {
if (error instanceof JavaRunnerError) {
switch (error.code) {
case 'JAVA_NOT_FOUND':
console.error('Java not installed. Install Java or check JAVA_HOME');
break;
case 'JAR_NOT_FOUND':
console.error('JAR file not found:', error.message);
break;
case 'INVALID_JAR_FORMAT':
console.error('The specified file is not a valid JAR');
break;
default:
console.error('Unknown error:', error.message);
}
} else {
console.error('Unexpected error:', error.message);
}
}
}
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the project
- Create a branch for your feature (
git checkout -b feature/AmazingFeature
) - Commit your changes (
git commit -m 'Add some AmazingFeature'
) - Push the branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
- Maintain compatibility with Node.js >= 14.0.0
- Add tests for new features
- Update documentation for API changes
- Follow existing code conventions
- Ensure all tests pass
This project is distributed under the ISC license.
ISC License
Copyright (c) TND Team
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
If you encounter problems or have questions:
- Check the Troubleshooting section
- Review the examples in
test.js
- Open an issue on GitHub with problem details
Made with ❤️ by TND Team