or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

build-integration.mdimage-packaging.mdindex.mdjar-writing.mdlaunch-scripts.mdlayer-support.mdlayout-management.mdlibrary-management.mdmain-class-detection.mdrepackaging.md

launch-scripts.mddocs/

0

# Launch Script Generation

1

2

Tools for creating launch scripts that make JAR files directly executable on Unix-like systems. Launch scripts are prepended to JAR files, allowing them to be executed like native executables while maintaining JAR functionality.

3

4

## Capabilities

5

6

### Launch Script Interface

7

8

Core interface for launch scripts that can be prepended to JAR files.

9

10

```java { .api }

11

public interface LaunchScript {

12

/**

13

* Get the launch script content as a byte array.

14

* The script is prepended to the JAR file to make it executable.

15

*

16

* @return the script content as bytes

17

*/

18

byte[] toByteArray();

19

}

20

```

21

22

### Default Launch Script

23

24

Standard implementation that provides a configurable Unix shell script for launching JAR files.

25

26

```java { .api }

27

public class DefaultLaunchScript implements LaunchScript {

28

/**

29

* Create a launch script from a template file with property substitution.

30

*

31

* @param file the script template file

32

* @param properties properties for template variable substitution

33

* @throws IOException if the template file cannot be read

34

*/

35

public DefaultLaunchScript(File file, Map<?, ?> properties) throws IOException;

36

37

/**

38

* Get the launch script content as bytes.

39

*

40

* @return the processed script content

41

*/

42

@Override

43

public byte[] toByteArray();

44

}

45

```

46

47

## Usage Examples

48

49

### Basic Launch Script

50

51

```java

52

import org.springframework.boot.loader.tools.*;

53

import java.io.File;

54

import java.util.Map;

55

56

// Create a basic launch script

57

File scriptTemplate = new File("src/main/scripts/launch.sh");

58

Map<String, String> properties = Map.of(

59

"initInfoProvides", "myapp",

60

"initInfoShortDescription", "My Spring Boot Application"

61

);

62

63

LaunchScript launchScript = new DefaultLaunchScript(scriptTemplate, properties);

64

65

// Use with JarWriter

66

File targetJar = new File("myapp.jar");

67

try (JarWriter writer = new JarWriter(targetJar, launchScript)) {

68

// Write JAR contents

69

// ... (JAR writing code)

70

}

71

72

// The resulting JAR can be executed directly:

73

// chmod +x myapp.jar

74

// ./myapp.jar

75

```

76

77

### Advanced Launch Script Configuration

78

79

```java

80

import org.springframework.boot.loader.tools.*;

81

import java.io.File;

82

import java.util.HashMap;

83

import java.util.Map;

84

85

// Configure advanced launch script properties

86

Map<String, String> properties = new HashMap<>();

87

88

// Service information

89

properties.put("initInfoProvides", "myapp");

90

properties.put("initInfoShortDescription", "My Spring Boot Application");

91

properties.put("initInfoDescription", "A comprehensive Spring Boot application for data processing");

92

93

// Runtime configuration

94

properties.put("confFolder", "/etc/myapp");

95

properties.put("logFolder", "/var/log/myapp");

96

properties.put("pidFolder", "/var/run/myapp");

97

properties.put("logFilename", "myapp.log");

98

99

// JVM options

100

properties.put("javaOpts", "-Xmx1024m -Xms512m -Dspring.profiles.active=production");

101

102

// User and permissions

103

properties.put("runAsUser", "myapp");

104

properties.put("mode", "service"); // or "auto", "force", "run"

105

106

// File locations

107

properties.put("useStartStopDaemon", "true");

108

properties.put("stopWaitTime", "60");

109

110

File scriptTemplate = new File("launch-template.sh");

111

LaunchScript launchScript = new DefaultLaunchScript(scriptTemplate, properties);

112

113

// Create executable JAR

114

File targetJar = new File("myapp.jar");

115

try (JarWriter writer = new JarWriter(targetJar, launchScript)) {

116

// Write JAR contents...

117

}

118

119

// The JAR can now be used as a system service:

120

// sudo cp myapp.jar /etc/init.d/myapp

121

// sudo chmod +x /etc/init.d/myapp

122

// sudo update-rc.d myapp defaults

123

// sudo service myapp start

124

```

125

126

### Custom Launch Script Template

127

128

Create a custom launch script template (`launch-template.sh`):

129

130

```bash

131

#!/bin/bash

132

#

133

# {{initInfoProvides}} {{initInfoShortDescription}}

134

#

135

# chkconfig: 35 80 20

136

# description: {{initInfoDescription}}

137

#

138

139

. /lib/lsb/init-functions

140

141

USER="{{runAsUser}}"

142

DAEMON="{{initInfoProvides}}"

143

ROOT_DIR="/var/lib/{{initInfoProvides}}"

144

145

SERVER="$ROOT_DIR/{{initInfoProvides}}.jar"

146

LOCK_FILE="/var/lock/subsys/{{initInfoProvides}}"

147

148

start() {

149

log_daemon_msg "Starting $DAEMON"

150

if [ ! -f "$SERVER" ]; then

151

log_failure_msg "$SERVER not found"

152

exit 1

153

fi

154

155

if start-stop-daemon --start --quiet --oknodo --pidfile "$PID_FILE" \

156

--chuid "$USER" --background --make-pidfile \

157

--exec /usr/bin/java -- {{javaOpts}} -jar "$SERVER"

158

then

159

log_end_msg 0

160

touch "$LOCK_FILE"

161

else

162

log_end_msg 1

163

fi

164

}

165

166

stop() {

167

log_daemon_msg "Stopping $DAEMON"

168

if start-stop-daemon --stop --quiet --oknodo --pidfile "$PID_FILE"

169

then

170

log_end_msg 0

171

rm -f "$LOCK_FILE"

172

else

173

log_end_msg 1

174

fi

175

}

176

177

case "$1" in

178

start)

179

start

180

;;

181

stop)

182

stop

183

;;

184

restart)

185

stop

186

start

187

;;

188

status)

189

status_of_proc -p $PID_FILE "$DAEMON" "$DAEMON" && exit 0 || exit $?

190

;;

191

*)

192

echo "Usage: $0 {start|stop|restart|status}"

193

exit 1

194

;;

195

esac

196

197

exit 0

198

199

# The actual JAR content follows after this script

200

```

201

202

### Integration with Repackager

203

204

```java

205

import org.springframework.boot.loader.tools.*;

206

import java.io.File;

207

import java.util.Map;

208

209

// Create executable JAR with launch script using Repackager

210

File sourceJar = new File("target/myapp.jar");

211

File destination = new File("dist/myapp");

212

213

Repackager repackager = new Repackager(sourceJar);

214

215

// Configure launch script

216

File scriptTemplate = getClass().getResource("/launch-template.sh").getFile();

217

Map<String, String> scriptProperties = Map.of(

218

"initInfoProvides", "myapp",

219

"initInfoShortDescription", "My Application",

220

"runAsUser", "appuser",

221

"javaOpts", "-Xmx512m -Dspring.profiles.active=prod"

222

);

223

224

LaunchScript launchScript = new DefaultLaunchScript(scriptTemplate, scriptProperties);

225

226

// Repackage with launch script

227

repackager.repackage(destination, Libraries.NONE, launchScript);

228

229

// Set executable permissions

230

destination.setExecutable(true, false);

231

232

System.out.println("Created executable JAR: " + destination.getAbsolutePath());

233

System.out.println("Run with: " + destination.getAbsolutePath());

234

```

235

236

### Conditional Launch Script

237

238

```java

239

import org.springframework.boot.loader.tools.*;

240

import java.io.File;

241

import java.util.Map;

242

243

// Only add launch script for Unix-like systems

244

public class ConditionalLaunchScript {

245

public static LaunchScript createLaunchScript() {

246

String osName = System.getProperty("os.name").toLowerCase();

247

if (osName.contains("windows")) {

248

// No launch script for Windows

249

return null;

250

}

251

252

// Create launch script for Unix-like systems

253

try {

254

File template = new File("unix-launch-template.sh");

255

Map<String, String> props = Map.of(

256

"initInfoProvides", "myapp",

257

"initInfoShortDescription", "My App"

258

);

259

return new DefaultLaunchScript(template, props);

260

} catch (Exception e) {

261

System.err.println("Failed to create launch script: " + e.getMessage());

262

return null;

263

}

264

}

265

}

266

267

// Usage

268

File sourceJar = new File("myapp.jar");

269

Repackager repackager = new Repackager(sourceJar);

270

271

LaunchScript launchScript = ConditionalLaunchScript.createLaunchScript();

272

if (launchScript != null) {

273

repackager.repackage(Libraries.NONE, launchScript);

274

System.out.println("Created executable JAR with launch script");

275

} else {

276

repackager.repackage(Libraries.NONE);

277

System.out.println("Created JAR without launch script");

278

}

279

```

280

281

### Custom Launch Script Implementation

282

283

```java

284

import org.springframework.boot.loader.tools.LaunchScript;

285

import java.io.ByteArrayOutputStream;

286

import java.io.IOException;

287

import java.io.InputStream;

288

import java.nio.charset.StandardCharsets;

289

290

// Custom launch script implementation

291

public class CustomLaunchScript implements LaunchScript {

292

private final String applicationName;

293

private final String javaOpts;

294

private final String workingDir;

295

296

public CustomLaunchScript(String applicationName, String javaOpts, String workingDir) {

297

this.applicationName = applicationName;

298

this.javaOpts = javaOpts;

299

this.workingDir = workingDir;

300

}

301

302

@Override

303

public byte[] toByteArray() {

304

String script = generateScript();

305

return script.getBytes(StandardCharsets.UTF_8);

306

}

307

308

private String generateScript() {

309

return String.format("""

310

#!/bin/bash

311

#

312

# %s startup script

313

#

314

315

JAVA_OPTS="%s"

316

WORKING_DIR="%s"

317

318

# Change to working directory

319

cd "$WORKING_DIR" || exit 1

320

321

# Find Java executable

322

if [ -n "$JAVA_HOME" ]; then

323

JAVA_EXE="$JAVA_HOME/bin/java"

324

else

325

JAVA_EXE="java"

326

fi

327

328

# Execute the JAR

329

exec "$JAVA_EXE" $JAVA_OPTS -jar "$0" "$@"

330

331

# JAR content follows

332

""", applicationName, javaOpts, workingDir);

333

}

334

}

335

336

// Usage

337

LaunchScript customScript = new CustomLaunchScript(

338

"MyApp",

339

"-Xmx1g -Dspring.profiles.active=prod",

340

"/opt/myapp"

341

);

342

343

try (JarWriter writer = new JarWriter(new File("myapp.jar"), customScript)) {

344

// Write JAR contents...

345

}

346

```

347

348

### Launch Script with Environment Detection

349

350

```java

351

import org.springframework.boot.loader.tools.*;

352

import java.io.File;

353

import java.util.Map;

354

355

// Launch script that detects and adapts to the runtime environment

356

public class EnvironmentAwareLaunchScript implements LaunchScript {

357

private final Map<String, String> properties;

358

359

public EnvironmentAwareLaunchScript(Map<String, String> properties) {

360

this.properties = properties;

361

}

362

363

@Override

364

public byte[] toByteArray() {

365

String script = """

366

#!/bin/bash

367

#

368

# Environment-aware launch script

369

#

370

371

# Detect environment

372

if [ -f "/etc/debian_version" ]; then

373

INIT_SYSTEM="systemd"

374

USER_HOME="/home"

375

elif [ -f "/etc/redhat-release" ]; then

376

INIT_SYSTEM="systemd"

377

USER_HOME="/home"

378

elif [ "$(uname)" = "Darwin" ]; then

379

INIT_SYSTEM="launchd"

380

USER_HOME="/Users"

381

else

382

INIT_SYSTEM="generic"

383

USER_HOME="/home"

384

fi

385

386

# Set environment-specific defaults

387

case "$INIT_SYSTEM" in

388

systemd)

389

LOG_DIR="/var/log/%s"

390

PID_DIR="/var/run"

391

CONFIG_DIR="/etc/%s"

392

;;

393

launchd)

394

LOG_DIR="$USER_HOME/Library/Logs/%s"

395

PID_DIR="/tmp"

396

CONFIG_DIR="$USER_HOME/Library/Application Support/%s"

397

;;

398

*)

399

LOG_DIR="/tmp/logs/%s"

400

PID_DIR="/tmp"

401

CONFIG_DIR="/tmp/config/%s"

402

;;

403

esac

404

405

# Create directories if they don't exist

406

mkdir -p "$LOG_DIR" "$PID_DIR" "$CONFIG_DIR"

407

408

# Set JVM options based on available memory

409

TOTAL_MEM=$(free -m 2>/dev/null | awk 'NR==2{printf "%%d", $2}' || echo "1024")

410

if [ "$TOTAL_MEM" -gt 4096 ]; then

411

JAVA_OPTS="-Xmx2g -Xms1g"

412

elif [ "$TOTAL_MEM" -gt 2048 ]; then

413

JAVA_OPTS="-Xmx1g -Xms512m"

414

else

415

JAVA_OPTS="-Xmx512m -Xms256m"

416

fi

417

418

# Add application-specific properties

419

JAVA_OPTS="$JAVA_OPTS -Dlogging.file.path=$LOG_DIR"

420

JAVA_OPTS="$JAVA_OPTS -Dspring.config.additional-location=$CONFIG_DIR/"

421

422

# Execute the application

423

exec java $JAVA_OPTS -jar "$0" "$@"

424

425

""".formatted(

426

properties.getOrDefault("appName", "app"),

427

properties.getOrDefault("appName", "app"),

428

properties.getOrDefault("appName", "app"),

429

properties.getOrDefault("appName", "app"),

430

properties.getOrDefault("appName", "app")

431

);

432

433

return script.getBytes(StandardCharsets.UTF_8);

434

}

435

}

436

```

437

438

## Launch Script Properties

439

440

Common template variables supported by `DefaultLaunchScript`:

441

442

| Property | Description | Default |

443

|----------|-------------|---------|

444

| `initInfoProvides` | Service name | Application name |

445

| `initInfoShortDescription` | Brief description | Application description |

446

| `initInfoDescription` | Full description | Application description |

447

| `confFolder` | Configuration directory | Same as JAR location |

448

| `logFolder` | Log file directory | `/var/log` |

449

| `pidFolder` | PID file directory | `/var/run` |

450

| `logFilename` | Log file name | Application name + `.log` |

451

| `javaOpts` | JVM options | Empty |

452

| `runAsUser` | User to run as | Current user |

453

| `mode` | Execution mode | `auto` |

454

| `useStartStopDaemon` | Use system daemon | `true` |

455

| `stopWaitTime` | Stop timeout (seconds) | `60` |

456

457

## System Integration

458

459

Launch scripts enable Spring Boot JARs to integrate seamlessly with system service managers:

460

461

- **systemd** (Modern Linux distributions)

462

- **init.d** (Traditional Unix systems)

463

- **launchd** (macOS)

464

- **Windows Services** (with additional tooling)

465

466

The generated executable JARs can be installed as system services, providing process management, automatic restart, and system integration capabilities.