Skip to content

Commit 0cd8d2f

Browse files
committed
chore: dedupe pipeline call-state httpClient and reload/toBlocking
Behavior-preserving cleanups in the http/pipeline runtime: - Drop the stored httpClient from PipelineCallState / AsyncPipelineCallState. It was always the same instance as pipeline.httpClient; the sole readers (PipelineNext / AsyncPipelineNext) now reach it through pipeline, removing the field, the constructor parameter, and an argument at every call site. - reload() now replays through append() instead of re-implementing the same stage-dispatch loop, keeping the bucketing policy in one place. - build() materializes the ordered step list with List.toTypedArray() rather than a manual index-lambda Array(size){}. - toBlocking() delegates to AsyncHttpClient.asBlocking() instead of hand-rolling the identical blocking-get with interrupt handling and ExecutionException unwrapping, leaving a single definition of the cancellation contract. - Correct Stage.order's KDoc and a test comment: the builder emits in Stage declaration order (Stage.entries), not by reading the numeric order value.
1 parent 5acd605 commit 0cd8d2f

12 files changed

Lines changed: 19 additions & 50 deletions

File tree

sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/AsyncHttpPipeline.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public class AsyncHttpPipeline internal constructor(
5656
Futures.failed(e)
5757
}
5858
}
59-
val state = AsyncPipelineCallState(this, request, httpClient)
59+
val state = AsyncPipelineCallState(this, request)
6060
return AsyncPipelineNext(state).processAsync()
6161
}
6262

sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/AsyncHttpPipelineBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public class AsyncHttpPipelineBuilder(private val httpClient: AsyncHttpClient) {
9999
/** Builds an immutable [AsyncHttpPipeline]. */
100100
public fun build(): AsyncHttpPipeline {
101101
val ordered = steps.flatten()
102-
return AsyncHttpPipeline(httpClient, Array(ordered.size) { ordered[it] })
102+
return AsyncHttpPipeline(httpClient, ordered.toTypedArray())
103103
}
104104

105105
public companion object {

sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/AsyncPipelineBridges.kt

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99

1010
package org.dexpace.sdk.core.http.pipeline
1111

12+
import org.dexpace.sdk.core.client.AsyncHttpClient
13+
import org.dexpace.sdk.core.client.asBlocking
1214
import org.dexpace.sdk.core.http.request.Request
1315
import org.dexpace.sdk.core.http.response.Response
14-
import org.dexpace.sdk.core.util.Futures
1516
import java.io.InterruptedIOException
1617
import java.util.concurrent.CompletableFuture
17-
import java.util.concurrent.ExecutionException
1818
import java.util.concurrent.Executor
1919
import java.util.concurrent.atomic.AtomicReference
2020

@@ -120,25 +120,5 @@ private fun sendInterruptibly(
120120
* The blocking wait honours `Thread.interrupt()`: interrupting the calling thread restores the
121121
* interrupt flag, cancels the in-flight future, and throws an [InterruptedIOException].
122122
*/
123-
public fun AsyncHttpPipeline.toBlocking(): HttpPipeline {
124-
val async = this
125-
return HttpPipeline.of { request ->
126-
val future = async.sendAsync(request)
127-
try {
128-
future.get()
129-
} catch (ie: InterruptedException) {
130-
// `get()` parks interruptibly (unlike `join()`). Restore the interrupt flag, abort
131-
// the in-flight send, and surface an InterruptedIOException so the caller's I/O
132-
// error handling terminates cleanly.
133-
Thread.currentThread().interrupt()
134-
future.cancel(true)
135-
val ioe = InterruptedIOException("Interrupted while waiting for response")
136-
ioe.initCause(ie)
137-
throw ioe
138-
} catch (ee: ExecutionException) {
139-
// `get()` wraps exceptional completion in ExecutionException; unwrap so callers'
140-
// `catch (IOException)` sees the original failure rather than the JDK wrapper.
141-
throw Futures.unwrap(ee)
142-
}
143-
}
144-
}
123+
public fun AsyncHttpPipeline.toBlocking(): HttpPipeline =
124+
HttpPipeline.of(AsyncHttpClient { sendAsync(it) }.asBlocking())

sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/AsyncPipelineCallState.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@
77

88
package org.dexpace.sdk.core.http.pipeline
99

10-
import org.dexpace.sdk.core.client.AsyncHttpClient
1110
import org.dexpace.sdk.core.http.request.Request
1211

1312
/**
1413
* Per-call mutable cursor over an [AsyncHttpPipeline]'s steps array. Async counterpart of
15-
* [PipelineCallState]: holds the index of the next step to invoke, the in-flight [Request],
16-
* and a reference to the [AsyncHttpClient] used when the cursor reaches the end.
14+
* [PipelineCallState]: holds the index of the next step to invoke and the in-flight [Request].
1715
*
1816
* Cloned via [copy] (exposed to user code through [AsyncPipelineNext.copy]) so async retry /
1917
* redirect steps can re-drive the downstream chain. Cloning copies the current index — the
@@ -24,7 +22,6 @@ import org.dexpace.sdk.core.http.request.Request
2422
internal class AsyncPipelineCallState internal constructor(
2523
val pipeline: AsyncHttpPipeline,
2624
initialRequest: Request,
27-
val httpClient: AsyncHttpClient,
2825
private var index: Int = 0,
2926
) {
3027
/**
@@ -42,5 +39,5 @@ internal class AsyncPipelineCallState internal constructor(
4239
}
4340

4441
/** Returns an independent state cloned at the current cursor position. */
45-
fun copy(): AsyncPipelineCallState = AsyncPipelineCallState(pipeline, request, httpClient, index)
42+
fun copy(): AsyncPipelineCallState = AsyncPipelineCallState(pipeline, request, index)
4643
}

sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/AsyncPipelineNext.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class AsyncPipelineNext internal constructor(private val state: AsyncPipe
3434
val nextStep = state.advance()
3535
return try {
3636
if (nextStep == null) {
37-
state.httpClient.executeAsync(state.request)
37+
state.pipeline.httpClient.executeAsync(state.request)
3838
} else {
3939
nextStep.processAsync(state.request, this)
4040
}

sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/HttpPipeline.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class HttpPipeline internal constructor(
4040
@Throws(IOException::class)
4141
public fun send(request: Request): Response {
4242
if (stepArray.isEmpty()) return httpClient.execute(request)
43-
val state = PipelineCallState(this, request, httpClient)
43+
val state = PipelineCallState(this, request)
4444
return PipelineNext(state).process()
4545
}
4646

sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/HttpPipelineBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public class HttpPipelineBuilder(private val httpClient: HttpClient) {
113113
*/
114114
public fun build(): HttpPipeline {
115115
val ordered = steps.flatten()
116-
return HttpPipeline(httpClient, Array(ordered.size) { ordered[it] })
116+
return HttpPipeline(httpClient, ordered.toTypedArray())
117117
}
118118

119119
public companion object {

sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/PipelineCallState.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import org.dexpace.sdk.core.http.request.Request
1212

1313
/**
1414
* Per-call mutable cursor over a [HttpPipeline]'s steps array. Holds the index of the
15-
* next step to invoke, the originating [Request], and a reference to the [HttpClient] used
16-
* when the cursor reaches the end.
15+
* next step to invoke and the originating [Request].
1716
*
1817
* Cloned via [copy] (exposed to user code through [PipelineNext.copy]) so retry / redirect
1918
* steps can re-drive the downstream chain. Cloning copies the current index — the new
@@ -27,7 +26,6 @@ import org.dexpace.sdk.core.http.request.Request
2726
internal class PipelineCallState internal constructor(
2827
val pipeline: HttpPipeline,
2928
initialRequest: Request,
30-
val httpClient: HttpClient,
3129
private var index: Int = 0,
3230
) {
3331
/**
@@ -51,5 +49,5 @@ internal class PipelineCallState internal constructor(
5149
}
5250

5351
/** Returns an independent state cloned at the current cursor position. */
54-
fun copy(): PipelineCallState = PipelineCallState(pipeline, request, httpClient, index)
52+
fun copy(): PipelineCallState = PipelineCallState(pipeline, request, index)
5553
}

sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/PipelineNext.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class PipelineNext internal constructor(private val state: PipelineCallSt
2727
public fun process(): Response {
2828
val nextStep = state.advance()
2929
return if (nextStep == null) {
30-
state.httpClient.execute(state.request)
30+
state.pipeline.httpClient.execute(state.request)
3131
} else {
3232
nextStep.process(state.request, this)
3333
}

sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/Stage.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ package org.dexpace.sdk.core.http.pipeline
1717
* Sparse [order] values (100s apart) leave room to insert new stages later without
1818
* renumbering existing ones.
1919
*
20-
* @property order Run-order key used by [HttpPipelineBuilder.build] to emit steps; lower
21-
* values run first.
20+
* @property order Stable numeric identity for the stage; ascends with declaration order.
21+
* The builder emits steps in declaration order (`Stage.entries`), not by reading this
22+
* value — it exists as a stable, sortable inspection key for callers.
2223
* @property isPillar True if the stage admits at most one step (singleton). False for
2324
* user-extensible stages backed by an ordered deque.
2425
*/

0 commit comments

Comments
 (0)