Skip to content

Commit c5e42fe

Browse files
Merge pull request #110 from OneBusAway/release-please--branches--main--changes--next
release: 0.1.0-alpha.54
2 parents 2e02604 + 5a82182 commit c5e42fe

10 files changed

Lines changed: 967 additions & 69 deletions

File tree

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "0.1.0-alpha.53"
2+
".": "0.1.0-alpha.54"
33
}

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## 0.1.0-alpha.54 (2026-02-25)
4+
5+
Full Changelog: [v0.1.0-alpha.53...v0.1.0-alpha.54](https://github.com/OneBusAway/java-sdk/compare/v0.1.0-alpha.53...v0.1.0-alpha.54)
6+
7+
### Chores
8+
9+
* drop apache dependency ([7c4dc22](https://github.com/OneBusAway/java-sdk/commit/7c4dc22dafc8ce6892a94da4d227d5e0a810c4ce))
10+
* **internal:** expand imports ([6b02ef8](https://github.com/OneBusAway/java-sdk/commit/6b02ef86864a0f6a2a1456f59c017364fa271a40))
11+
* make `Properties` more resilient to `null` ([a743178](https://github.com/OneBusAway/java-sdk/commit/a7431780bc7a3b13b284ab66bfafc7aee55dd802))
12+
313
## 0.1.0-alpha.53 (2026-02-19)
414

515
Full Changelog: [v0.1.0-alpha.52...v0.1.0-alpha.53](https://github.com/OneBusAway/java-sdk/compare/v0.1.0-alpha.52...v0.1.0-alpha.53)

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
<!-- x-release-please-start-version -->
44

5-
[![Maven Central](https://img.shields.io/maven-central/v/org.onebusaway/onebusaway-sdk-java)](https://central.sonatype.com/artifact/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.53)
6-
[![javadoc](https://javadoc.io/badge2/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.53/javadoc.svg)](https://javadoc.io/doc/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.53)
5+
[![Maven Central](https://img.shields.io/maven-central/v/org.onebusaway/onebusaway-sdk-java)](https://central.sonatype.com/artifact/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.54)
6+
[![javadoc](https://javadoc.io/badge2/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.54/javadoc.svg)](https://javadoc.io/doc/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.54)
77

88
<!-- x-release-please-end -->
99

@@ -15,7 +15,7 @@ It is generated with [Stainless](https://www.stainless.com/).
1515

1616
<!-- x-release-please-start-version -->
1717

18-
The REST API documentation can be found on [developer.onebusaway.org](https://developer.onebusaway.org). Javadocs are available on [javadoc.io](https://javadoc.io/doc/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.53).
18+
The REST API documentation can be found on [developer.onebusaway.org](https://developer.onebusaway.org). Javadocs are available on [javadoc.io](https://javadoc.io/doc/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.54).
1919

2020
<!-- x-release-please-end -->
2121

@@ -26,7 +26,7 @@ The REST API documentation can be found on [developer.onebusaway.org](https://de
2626
### Gradle
2727

2828
```kotlin
29-
implementation("org.onebusaway:onebusaway-sdk-java:0.1.0-alpha.53")
29+
implementation("org.onebusaway:onebusaway-sdk-java:0.1.0-alpha.54")
3030
```
3131

3232
### Maven
@@ -35,7 +35,7 @@ implementation("org.onebusaway:onebusaway-sdk-java:0.1.0-alpha.53")
3535
<dependency>
3636
<groupId>org.onebusaway</groupId>
3737
<artifactId>onebusaway-sdk-java</artifactId>
38-
<version>0.1.0-alpha.53</version>
38+
<version>0.1.0-alpha.54</version>
3939
</dependency>
4040
```
4141

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ repositories {
88

99
allprojects {
1010
group = "org.onebusaway"
11-
version = "0.1.0-alpha.53" // x-release-please-version
11+
version = "0.1.0-alpha.54" // x-release-please-version
1212
}
1313

1414
subprojects {

onebusaway-sdk-java-core/build.gradle.kts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ dependencies {
2727
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2")
2828
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2")
2929
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2")
30-
implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4")
31-
implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1")
3230

3331
testImplementation(kotlin("test"))
3432
testImplementation(project(":onebusaway-sdk-java-client-okhttp"))

onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/Properties.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ fun getOsName(): String {
3434
}
3535
}
3636

37-
fun getOsVersion(): String = System.getProperty("os.version", "unknown")
37+
fun getOsVersion(): String = System.getProperty("os.version", "unknown") ?: "unknown"
3838

3939
fun getPackageVersion(): String =
40-
OnebusawaySdkClient::class.java.`package`.implementationVersion ?: "unknown"
40+
OnebusawaySdkClient::class.java.`package`?.implementationVersion ?: "unknown"
4141

42-
fun getJavaVersion(): String = System.getProperty("java.version", "unknown")
42+
fun getJavaVersion(): String = System.getProperty("java.version", "unknown") ?: "unknown"

onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/HttpRequestBodies.kt

Lines changed: 194 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ package org.onebusaway.core.http
77
import com.fasterxml.jackson.databind.JsonNode
88
import com.fasterxml.jackson.databind.json.JsonMapper
99
import com.fasterxml.jackson.databind.node.JsonNodeType
10+
import java.io.ByteArrayInputStream
1011
import java.io.InputStream
1112
import java.io.OutputStream
13+
import java.util.UUID
1214
import kotlin.jvm.optionals.getOrNull
13-
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder
14-
import org.apache.hc.core5.http.ContentType
15-
import org.apache.hc.core5.http.HttpEntity
1615
import org.onebusaway.core.MultipartField
16+
import org.onebusaway.core.toImmutable
1717
import org.onebusaway.errors.OnebusawaySdkInvalidDataException
1818

1919
@JvmSynthetic
@@ -37,70 +37,208 @@ internal fun multipartFormData(
3737
jsonMapper: JsonMapper,
3838
fields: Map<String, MultipartField<*>>,
3939
): HttpRequestBody =
40-
object : HttpRequestBody {
41-
private val entity: HttpEntity by lazy {
42-
MultipartEntityBuilder.create()
43-
.apply {
44-
fields.forEach { (name, field) ->
45-
val knownValue = field.value.asKnown().getOrNull()
46-
val parts =
47-
if (knownValue is InputStream) {
48-
// Read directly from the `InputStream` instead of reading it all
49-
// into memory due to the `jsonMapper` serialization below.
50-
sequenceOf(name to knownValue)
51-
} else {
52-
val node = jsonMapper.valueToTree<JsonNode>(field.value)
53-
serializePart(name, node)
40+
MultipartBody.Builder()
41+
.apply {
42+
fields.forEach { (name, field) ->
43+
val knownValue = field.value.asKnown().getOrNull()
44+
val parts =
45+
if (knownValue is InputStream) {
46+
// Read directly from the `InputStream` instead of reading it all
47+
// into memory due to the `jsonMapper` serialization below.
48+
sequenceOf(name to knownValue)
49+
} else {
50+
val node = jsonMapper.valueToTree<JsonNode>(field.value)
51+
serializePart(name, node)
52+
}
53+
54+
parts.forEach { (name, bytes) ->
55+
val partBody =
56+
if (bytes is ByteArrayInputStream) {
57+
val byteArray = bytes.readBytes()
58+
59+
object : HttpRequestBody {
60+
61+
override fun writeTo(outputStream: OutputStream) {
62+
outputStream.write(byteArray)
63+
}
64+
65+
override fun contentType(): String = field.contentType
66+
67+
override fun contentLength(): Long = byteArray.size.toLong()
68+
69+
override fun repeatable(): Boolean = true
70+
71+
override fun close() {}
5472
}
73+
} else {
74+
object : HttpRequestBody {
75+
76+
override fun writeTo(outputStream: OutputStream) {
77+
bytes.copyTo(outputStream)
78+
}
79+
80+
override fun contentType(): String = field.contentType
5581

56-
parts.forEach { (name, bytes) ->
57-
addBinaryBody(
58-
name,
59-
bytes,
60-
ContentType.parseLenient(field.contentType),
61-
field.filename().getOrNull(),
62-
)
82+
override fun contentLength(): Long = -1L
83+
84+
override fun repeatable(): Boolean = false
85+
86+
override fun close() = bytes.close()
87+
}
6388
}
64-
}
89+
90+
addPart(
91+
MultipartBody.Part.create(
92+
name,
93+
field.filename().getOrNull(),
94+
field.contentType,
95+
partBody,
96+
)
97+
)
6598
}
66-
.build()
99+
}
67100
}
101+
.build()
102+
103+
private fun serializePart(name: String, node: JsonNode): Sequence<Pair<String, InputStream>> =
104+
when (node.nodeType) {
105+
JsonNodeType.MISSING,
106+
JsonNodeType.NULL -> emptySequence()
107+
JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream())
108+
JsonNodeType.STRING -> sequenceOf(name to node.textValue().byteInputStream())
109+
JsonNodeType.BOOLEAN -> sequenceOf(name to node.booleanValue().toString().byteInputStream())
110+
JsonNodeType.NUMBER -> sequenceOf(name to node.numberValue().toString().byteInputStream())
111+
JsonNodeType.ARRAY ->
112+
node.elements().asSequence().flatMap { element -> serializePart(name, element) }
113+
JsonNodeType.OBJECT ->
114+
node.fields().asSequence().flatMap { (key, value) ->
115+
serializePart("$name[$key]", value)
116+
}
117+
JsonNodeType.POJO,
118+
null ->
119+
throw OnebusawaySdkInvalidDataException("Unexpected JsonNode type: ${node.nodeType}")
120+
}
68121

69-
private fun serializePart(
70-
name: String,
71-
node: JsonNode,
72-
): Sequence<Pair<String, InputStream>> =
73-
when (node.nodeType) {
74-
JsonNodeType.MISSING,
75-
JsonNodeType.NULL -> emptySequence()
76-
JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream())
77-
JsonNodeType.STRING -> sequenceOf(name to node.textValue().inputStream())
78-
JsonNodeType.BOOLEAN ->
79-
sequenceOf(name to node.booleanValue().toString().inputStream())
80-
JsonNodeType.NUMBER ->
81-
sequenceOf(name to node.numberValue().toString().inputStream())
82-
JsonNodeType.ARRAY ->
83-
node.elements().asSequence().flatMap { element -> serializePart(name, element) }
84-
JsonNodeType.OBJECT ->
85-
node.fields().asSequence().flatMap { (key, value) ->
86-
serializePart("$name[$key]", value)
87-
}
88-
JsonNodeType.POJO,
89-
null ->
90-
throw OnebusawaySdkInvalidDataException(
91-
"Unexpected JsonNode type: ${node.nodeType}"
92-
)
122+
private class MultipartBody
123+
private constructor(private val boundary: String, private val parts: List<Part>) : HttpRequestBody {
124+
private val boundaryBytes: ByteArray = boundary.toByteArray()
125+
private val contentType = "multipart/form-data; boundary=$boundary"
126+
127+
// This must remain in sync with `contentLength`.
128+
override fun writeTo(outputStream: OutputStream) {
129+
parts.forEach { part ->
130+
outputStream.write(DASHDASH)
131+
outputStream.write(boundaryBytes)
132+
outputStream.write(CRLF)
133+
134+
outputStream.write(CONTENT_DISPOSITION)
135+
outputStream.write(part.contentDisposition.toByteArray())
136+
outputStream.write(CRLF)
137+
138+
outputStream.write(CONTENT_TYPE)
139+
outputStream.write(part.contentType.toByteArray())
140+
outputStream.write(CRLF)
141+
142+
outputStream.write(CRLF)
143+
part.body.writeTo(outputStream)
144+
outputStream.write(CRLF)
145+
}
146+
147+
outputStream.write(DASHDASH)
148+
outputStream.write(boundaryBytes)
149+
outputStream.write(DASHDASH)
150+
outputStream.write(CRLF)
151+
}
152+
153+
override fun contentType(): String = contentType
154+
155+
// This must remain in sync with `writeTo`.
156+
override fun contentLength(): Long {
157+
var byteCount = 0L
158+
159+
parts.forEach { part ->
160+
val contentLength = part.body.contentLength()
161+
if (contentLength == -1L) {
162+
return -1L
93163
}
94164

95-
private fun String.inputStream(): InputStream = toByteArray().inputStream()
165+
byteCount +=
166+
DASHDASH.size +
167+
boundaryBytes.size +
168+
CRLF.size +
169+
CONTENT_DISPOSITION.size +
170+
part.contentDisposition.toByteArray().size +
171+
CRLF.size +
172+
CONTENT_TYPE.size +
173+
part.contentType.toByteArray().size +
174+
CRLF.size +
175+
CRLF.size +
176+
contentLength +
177+
CRLF.size
178+
}
96179

97-
override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream)
180+
byteCount += DASHDASH.size + boundaryBytes.size + DASHDASH.size + CRLF.size
181+
return byteCount
182+
}
98183

99-
override fun contentType(): String = entity.contentType
184+
override fun repeatable(): Boolean = parts.all { it.body.repeatable() }
100185

101-
override fun contentLength(): Long = entity.contentLength
186+
override fun close() {
187+
parts.forEach { it.body.close() }
188+
}
102189

103-
override fun repeatable(): Boolean = entity.isRepeatable
190+
class Builder {
191+
private val boundary = UUID.randomUUID().toString()
192+
private val parts: MutableList<Part> = mutableListOf()
104193

105-
override fun close() = entity.close()
194+
fun addPart(part: Part) = apply { parts.add(part) }
195+
196+
fun build() = MultipartBody(boundary, parts.toImmutable())
197+
}
198+
199+
class Part
200+
private constructor(
201+
val contentDisposition: String,
202+
val contentType: String,
203+
val body: HttpRequestBody,
204+
) {
205+
companion object {
206+
fun create(
207+
name: String,
208+
filename: String?,
209+
contentType: String,
210+
body: HttpRequestBody,
211+
): Part {
212+
val disposition = buildString {
213+
append("form-data; name=")
214+
appendQuotedString(name)
215+
if (filename != null) {
216+
append("; filename=")
217+
appendQuotedString(filename)
218+
}
219+
}
220+
return Part(disposition, contentType, body)
221+
}
222+
}
223+
}
224+
225+
companion object {
226+
private val CRLF = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte())
227+
private val DASHDASH = byteArrayOf('-'.code.toByte(), '-'.code.toByte())
228+
private val CONTENT_DISPOSITION = "Content-Disposition: ".toByteArray()
229+
private val CONTENT_TYPE = "Content-Type: ".toByteArray()
230+
231+
private fun StringBuilder.appendQuotedString(key: String) {
232+
append('"')
233+
for (ch in key) {
234+
when (ch) {
235+
'\n' -> append("%0A")
236+
'\r' -> append("%0D")
237+
'"' -> append("%22")
238+
else -> append(ch)
239+
}
240+
}
241+
append('"')
242+
}
106243
}
244+
}

onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/RetryingHttpClient.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// File generated from our OpenAPI spec by Stainless.
2+
13
package org.onebusaway.core.http
24

35
import java.io.IOException

0 commit comments

Comments
 (0)