or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

buffer.mdbytestring.mdfilesystem.mdhashing.mdindex.mdjvm-extensions.mdsources-sinks.md

jvm-extensions.mddocs/

0

# JVM Extensions

1

2

Okio provides extensive JVM-specific extensions for interoperability with Java I/O, NIO, security APIs, and other JVM libraries. These extensions make it easy to integrate Okio with existing Java codebases.

3

4

## Capabilities

5

6

### Java I/O Interoperability

7

8

Convert between Java I/O streams and Okio Sources/Sinks.

9

10

```kotlin { .api }

11

/**

12

* Converts an OutputStream to a Sink

13

* @return Sink that writes to this OutputStream

14

*/

15

fun OutputStream.sink(): Sink

16

17

/**

18

* Converts an InputStream to a Source

19

* @return Source that reads from this InputStream

20

*/

21

fun InputStream.source(): Source

22

23

/**

24

* Converts a Socket's OutputStream to a Sink with timeout support

25

* @return Sink that writes to the socket with timeout handling

26

*/

27

fun Socket.sink(): Sink

28

29

/**

30

* Converts a Socket's InputStream to a Source with timeout support

31

* @return Source that reads from the socket with timeout handling

32

*/

33

fun Socket.source(): Source

34

35

/**

36

* Converts a File to a Source for reading

37

* @return Source that reads from this file

38

* @throws FileNotFoundException if file doesn't exist

39

* @throws IOException if an I/O error occurs

40

*/

41

@Throws(IOException::class)

42

fun File.source(): Source

43

44

/**

45

* Converts a File to a Sink for writing

46

* @param append If true, append to existing file; if false, overwrite

47

* @return Sink that writes to this file

48

* @throws IOException if an I/O error occurs

49

*/

50

@Throws(IOException::class)

51

fun File.sink(append: Boolean = false): Sink

52

53

/**

54

* Converts a File to an appending Sink

55

* @return Sink that appends to this file

56

* @throws IOException if an I/O error occurs

57

*/

58

@Throws(IOException::class)

59

fun File.appendingSink(): Sink

60

```

61

62

**Usage Examples:**

63

64

```kotlin

65

import java.io.*

66

import java.net.Socket

67

68

// File I/O

69

val file = File("/tmp/example.txt")

70

val source = file.source().buffer()

71

val content = source.readUtf8()

72

source.close()

73

74

val sink = file.sink().buffer()

75

sink.writeUtf8("Hello from Okio!")

76

sink.close()

77

78

// Stream conversion

79

val byteArrayOutputStream = ByteArrayOutputStream()

80

val sink = byteArrayOutputStream.sink().buffer()

81

sink.writeUtf8("Data")

82

sink.close()

83

val bytes = byteArrayOutputStream.toByteArray()

84

85

val byteArrayInputStream = ByteArrayInputStream(bytes)

86

val source = byteArrayInputStream.source().buffer()

87

val data = source.readUtf8()

88

89

// Socket I/O with timeout

90

val socket = Socket("example.com", 80)

91

socket.soTimeout = 5000 // 5 second timeout

92

93

val socketSource = socket.source().buffer()

94

val socketSink = socket.sink().buffer()

95

96

socketSink.writeUtf8("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")

97

socketSink.flush()

98

99

val response = socketSource.readUtf8Line()

100

println(response)

101

socket.close()

102

```

103

104

### NIO Interoperability

105

106

Work with Java NIO Path and Channel APIs.

107

108

```kotlin { .api }

109

/**

110

* Converts a NIO Path to a Source for reading

111

* @param options OpenOption instances to control how file is opened

112

* @return Source that reads from the path

113

* @throws IOException if an I/O error occurs

114

*/

115

@Throws(IOException::class)

116

fun java.nio.file.Path.source(vararg options: OpenOption): Source

117

118

/**

119

* Converts a NIO Path to a Sink for writing

120

* @param options OpenOption instances to control how file is opened

121

* @return Sink that writes to the path

122

* @throws IOException if an I/O error occurs

123

*/

124

@Throws(IOException::class)

125

fun java.nio.file.Path.sink(vararg options: OpenOption): Sink

126

```

127

128

**Usage Examples:**

129

130

```kotlin

131

import java.nio.file.Paths

132

import java.nio.file.StandardOpenOption.*

133

134

val nioPath = Paths.get("/tmp/nio-example.txt")

135

136

// Write using NIO path

137

val sink = nioPath.sink(CREATE, WRITE, TRUNCATE_EXISTING).buffer()

138

sink.writeUtf8("NIO Path example")

139

sink.close()

140

141

// Read using NIO path

142

val source = nioPath.source(READ).buffer()

143

val content = source.readUtf8()

144

source.close()

145

println(content)

146

147

// Append to file

148

val appendSink = nioPath.sink(CREATE, WRITE, APPEND).buffer()

149

appendSink.writeUtf8("\nAppended line")

150

appendSink.close()

151

```

152

153

### Cryptographic Extensions

154

155

Integrate with Java security APIs for encryption and authentication.

156

157

```kotlin { .api }

158

/**

159

* Wraps a Sink with cipher encryption

160

* @param cipher Initialized Cipher instance for encryption

161

* @return CipherSink that encrypts data before writing

162

*/

163

fun Sink.cipherSink(cipher: Cipher): CipherSink

164

165

/**

166

* Wraps a Source with cipher decryption

167

* @param cipher Initialized Cipher instance for decryption

168

* @return CipherSource that decrypts data while reading

169

*/

170

fun Source.cipherSource(cipher: Cipher): CipherSource

171

172

/**

173

* Wraps a Sink with MAC computation

174

* @param mac Initialized Mac instance

175

* @return HashingSink that computes MAC while writing

176

*/

177

fun Sink.hashingSink(mac: Mac): HashingSink

178

179

/**

180

* Wraps a Source with MAC computation

181

* @param mac Initialized Mac instance

182

* @return HashingSource that computes MAC while reading

183

*/

184

fun Source.hashingSource(mac: Mac): HashingSource

185

186

/**

187

* Wraps a Sink with MessageDigest computation

188

* @param digest MessageDigest instance

189

* @return HashingSink that computes digest while writing

190

*/

191

fun Sink.hashingSink(digest: MessageDigest): HashingSink

192

193

/**

194

* Wraps a Source with MessageDigest computation

195

* @param digest MessageDigest instance

196

* @return HashingSource that computes digest while reading

197

*/

198

fun Source.hashingSource(digest: MessageDigest): HashingSource

199

```

200

201

**Usage Examples:**

202

203

```kotlin

204

import javax.crypto.Cipher

205

import javax.crypto.spec.SecretKeySpec

206

import javax.crypto.Mac

207

import javax.crypto.spec.SecretKeySpec

208

import java.security.MessageDigest

209

210

// AES encryption

211

val key = SecretKeySpec("MySecretKey12345".toByteArray(), "AES")

212

val encryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding")

213

encryptCipher.init(Cipher.ENCRYPT_MODE, key)

214

215

val file = File("/tmp/encrypted.dat")

216

val encryptedSink = file.sink().cipherSink(encryptCipher).buffer()

217

encryptedSink.writeUtf8("Secret message")

218

encryptedSink.close()

219

220

// AES decryption

221

val decryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding")

222

decryptCipher.init(Cipher.DECRYPT_MODE, key)

223

224

val decryptedSource = file.source().cipherSource(decryptCipher).buffer()

225

val decryptedMessage = decryptedSource.readUtf8()

226

decryptedSource.close()

227

println("Decrypted: $decryptedMessage")

228

229

// HMAC computation

230

val hmacKey = SecretKeySpec("hmac-secret".toByteArray(), "HmacSHA256")

231

val mac = Mac.getInstance("HmacSHA256")

232

mac.init(hmacKey)

233

234

val hmacSink = blackholeSink().hashingSink(mac)

235

hmacSink.write(Buffer().writeUtf8("data to authenticate"), 19)

236

hmacSink.flush()

237

println("HMAC: ${hmacSink.hash.hex()}")

238

239

// MessageDigest computation

240

val sha256 = MessageDigest.getInstance("SHA-256")

241

val digestSink = blackholeSink().hashingSink(sha256)

242

digestSink.write(Buffer().writeUtf8("data to hash"), 12)

243

digestSink.flush()

244

println("SHA-256: ${digestSink.hash.hex()}")

245

```

246

247

### Resource FileSystem

248

249

Access classpath resources as a FileSystem.

250

251

```kotlin { .api }

252

/**

253

* Creates a FileSystem that reads from classpath resources

254

* @return FileSystem for accessing classpath resources

255

*/

256

fun ClassLoader.asResourceFileSystem(): FileSystem

257

258

/**

259

* CipherSink encrypts data using a Cipher before writing to underlying sink

260

*/

261

class CipherSink(

262

private val sink: BufferedSink,

263

private val cipher: Cipher

264

) : Sink {

265

/**

266

* The cipher being used for encryption

267

*/

268

val cipher: Cipher

269

}

270

271

/**

272

* CipherSource decrypts data using a Cipher while reading from underlying source

273

*/

274

class CipherSource(

275

private val source: BufferedSource,

276

private val cipher: Cipher

277

) : Source {

278

/**

279

* The cipher being used for decryption

280

*/

281

val cipher: Cipher

282

}

283

```

284

285

**Usage Examples:**

286

287

```kotlin

288

// Access classpath resources

289

val classLoader = Thread.currentThread().contextClassLoader

290

val resourceFs = classLoader.asResourceFileSystem()

291

292

// Read a resource file

293

val configPath = "config/application.properties".toPath()

294

if (resourceFs.exists(configPath)) {

295

val config = resourceFs.read(configPath) {

296

readUtf8()

297

}

298

println("Config: $config")

299

}

300

301

// List resources in a directory

302

val resourceDir = "static/css".toPath()

303

resourceFs.listOrNull(resourceDir)?.forEach { path ->

304

println("Resource: $path")

305

}

306

```

307

308

### Async Timeout

309

310

Background thread timeout implementation for non-blocking operations.

311

312

```kotlin { .api }

313

/**

314

* Timeout implementation using a background thread

315

* Provides asynchronous timeout functionality

316

*/

317

class AsyncTimeout : Timeout() {

318

/**

319

* Enters this timeout, starting the timeout timer

320

*/

321

fun enter()

322

323

/**

324

* Exits this timeout, stopping the timeout timer

325

* @return true if the timeout was triggered, false otherwise

326

*/

327

fun exit(): Boolean

328

329

/**

330

* Cancels this timeout, preventing it from firing

331

*/

332

fun cancel()

333

334

/**

335

* Wraps a Sink with this timeout

336

* @param sink Sink to wrap with timeout

337

* @return Sink that respects this timeout

338

*/

339

fun sink(sink: Sink): Sink

340

341

/**

342

* Wraps a Source with this timeout

343

* @param source Source to wrap with timeout

344

* @return Source that respects this timeout

345

*/

346

fun source(source: Source): Source

347

348

/**

349

* Executes a block with this timeout applied

350

* @param block Block to execute with timeout

351

* @return Result of the block execution

352

* @throws IOException if timeout occurs

353

*/

354

@Throws(IOException::class)

355

fun <T> withTimeout(block: () -> T): T

356

357

/**

358

* Called when timeout occurs (override to customize behavior)

359

* Default implementation does nothing

360

*/

361

protected open fun timedOut()

362

363

/**

364

* Creates an exception for timeout scenarios

365

* @param cause Optional underlying cause

366

* @return IOException to throw on timeout

367

*/

368

protected open fun newTimeoutException(cause: IOException?): IOException

369

}

370

```

371

372

**Usage Examples:**

373

374

```kotlin

375

import java.util.concurrent.TimeUnit

376

377

// Create timeout for network operations

378

val timeout = AsyncTimeout()

379

timeout.timeout(30, TimeUnit.SECONDS) // 30 second timeout

380

381

try {

382

val result = timeout.withTimeout {

383

// Simulate long-running network operation

384

val socket = Socket("slow-server.com", 80)

385

val source = socket.source().buffer()

386

val sink = socket.sink().buffer()

387

388

sink.writeUtf8("GET / HTTP/1.1\r\nHost: slow-server.com\r\n\r\n")

389

sink.flush()

390

391

val response = source.readUtf8Line()

392

socket.close()

393

response

394

}

395

println("Response: $result")

396

} catch (e: IOException) {

397

println("Operation timed out or failed: ${e.message}")

398

}

399

400

// Custom timeout behavior

401

class CustomTimeout : AsyncTimeout() {

402

override fun timedOut() {

403

println("Custom timeout triggered!")

404

}

405

406

override fun newTimeoutException(cause: IOException?): IOException {

407

return IOException("Custom timeout exceeded", cause)

408

}

409

}

410

```

411

412

### Compression and Decompression

413

414

Work with compressed data streams.

415

416

```kotlin { .api }

417

/**

418

* Sink that compresses data using Deflater before writing

419

*/

420

class DeflaterSink(

421

private val sink: Sink,

422

private val deflater: Deflater

423

) : Sink {

424

/**

425

* The deflater used for compression

426

*/

427

val deflater: Deflater

428

}

429

430

/**

431

* Source that decompresses data using Inflater while reading

432

*/

433

class InflaterSource(

434

private val source: Source,

435

private val inflater: Inflater

436

) : Source {

437

/**

438

* The inflater used for decompression

439

*/

440

val inflater: Inflater

441

442

/**

443

* Attempts to read or inflate data

444

* @param sink Buffer to read into

445

* @param byteCount Number of bytes to attempt to read

446

* @return Number of bytes read, or -1 if exhausted

447

*/

448

fun readOrInflate(sink: Buffer, byteCount: Long): Long

449

450

/**

451

* Refills the inflater with more compressed data

452

* @return true if more data was available

453

*/

454

fun refill(): Boolean

455

}

456

457

/**

458

* Creates a pipe for connecting a source and sink with buffering

459

* @param maxBufferSize Maximum buffer size for the pipe

460

* @return Pipe instance with source and sink properties

461

*/

462

class Pipe(maxBufferSize: Long) {

463

/**

464

* Sink for writing data to the pipe

465

*/

466

val sink: Sink

467

468

/**

469

* Source for reading data from the pipe

470

*/

471

val source: Source

472

473

/**

474

* Folds the pipe buffer into the provided sink

475

* @param sink Sink to receive buffered data

476

*/

477

fun fold(sink: Sink)

478

479

/**

480

* Cancels the pipe, preventing further operations

481

*/

482

fun cancel()

483

}

484

```

485

486

**Usage Examples:**

487

488

```kotlin

489

import java.util.zip.Deflater

490

import java.util.zip.Inflater

491

492

// Compress data

493

val originalData = "This is data to compress. ".repeat(100)

494

val deflater = Deflater(Deflater.BEST_COMPRESSION)

495

val compressedBuffer = Buffer()

496

val deflaterSink = DeflaterSink(compressedBuffer, deflater)

497

val bufferedSink = deflaterSink.buffer()

498

499

bufferedSink.writeUtf8(originalData)

500

bufferedSink.close()

501

502

println("Original size: ${originalData.length}")

503

println("Compressed size: ${compressedBuffer.size}")

504

505

// Decompress data

506

val inflater = Inflater()

507

val inflaterSource = InflaterSource(compressedBuffer, inflater)

508

val bufferedSource = inflaterSource.buffer()

509

510

val decompressedData = bufferedSource.readUtf8()

511

bufferedSource.close()

512

513

println("Decompressed matches original: ${decompressedData == originalData}")

514

515

// Use Pipe for producer-consumer pattern

516

val pipe = Pipe(8192) // 8KB buffer

517

518

// Producer thread

519

Thread {

520

val sink = pipe.sink.buffer()

521

try {

522

for (i in 1..100) {

523

sink.writeUtf8("Message $i\n")

524

Thread.sleep(10) // Simulate work

525

}

526

} finally {

527

sink.close()

528

}

529

}.start()

530

531

// Consumer thread

532

Thread {

533

val source = pipe.source.buffer()

534

try {

535

while (!source.exhausted()) {

536

val message = source.readUtf8Line()

537

if (message != null) {

538

println("Received: $message")

539

}

540

}

541

} finally {

542

source.close()

543

}

544

}.start()

545

```