This is an automated email from the ASF dual-hosted git repository.

chia7712 pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 82712644a65 KAFKA-20451 Move RequestChannel Responses to server module 
(#22517)
82712644a65 is described below

commit 82712644a65716c270618fc8a035847788120e44
Author: Mickael Maison <[email protected]>
AuthorDate: Fri Jul 10 16:36:36 2026 +0200

    KAFKA-20451 Move RequestChannel Responses to server module (#22517)
    
    This pull request migrates the `Response` class hierarchy from Scala to
    native Java records within the org.apache.kafka.network package to
    improve interoperability.
    
    Reviewers: TaiJuWu <[email protected]>, Ken Huang <[email protected]>,
     Chia-Ping Tsai <[email protected]>
---
 .../main/scala/kafka/network/RequestChannel.scala  | 56 ++++------------------
 .../main/scala/kafka/network/SocketServer.scala    | 23 ++++-----
 .../kafka/network/RequestConvertToJsonTest.scala   | 10 ++--
 .../unit/kafka/network/SocketServerTest.scala      | 14 +++---
 .../scala/unit/kafka/server/KafkaApisTest.scala    | 11 ++---
 .../kafka/network/CloseConnectionResponse.java     | 25 ++++++++++
 .../kafka/network/EndThrottlingResponse.java       | 25 ++++++++++
 .../org/apache/kafka/network/NoOpResponse.java     | 25 ++++++++++
 .../java/org/apache/kafka/network/Response.java    | 30 ++++++++++++
 .../org/apache/kafka/network/SendResponse.java     | 34 +++++++++++++
 .../kafka/network/StartThrottlingResponse.java     | 25 ++++++++++
 11 files changed, 199 insertions(+), 79 deletions(-)

diff --git a/core/src/main/scala/kafka/network/RequestChannel.scala 
b/core/src/main/scala/kafka/network/RequestChannel.scala
index ce776a1ee9f..3c8f0d140d2 100644
--- a/core/src/main/scala/kafka/network/RequestChannel.scala
+++ b/core/src/main/scala/kafka/network/RequestChannel.scala
@@ -18,63 +18,23 @@
 package kafka.network
 
 import java.util.concurrent._
-import com.fasterxml.jackson.databind.JsonNode
 import kafka.utils.Logging
-import org.apache.kafka.common.network.Send
 import org.apache.kafka.common.protocol.{ApiKeys, Errors}
 import org.apache.kafka.common.requests._
 import org.apache.kafka.common.utils.Time
 import org.apache.kafka.common.metrics.internals.MetricsUtils
-import org.apache.kafka.network.{BaseRequest, CallbackRequest, Request, 
ShutdownRequest, WakeupRequest}
+import org.apache.kafka.network.{BaseRequest, CallbackRequest, 
CloseConnectionResponse, EndThrottlingResponse, NoOpResponse, Request, 
Response, SendResponse, ShutdownRequest, StartThrottlingResponse, WakeupRequest}
 import org.apache.kafka.network.metrics.RequestChannelMetrics
 import org.apache.kafka.server.metrics.KafkaMetricsGroup
 
 import java.util.OptionalLong
 import scala.jdk.CollectionConverters._
-import scala.jdk.OptionConverters._
 
 object RequestChannel extends Logging {
 
   private val RequestQueueSizeMetric = "RequestQueueSize"
   private val ResponseQueueSizeMetric = "ResponseQueueSize"
   val ProcessorMetricTag = "processor"
-
-  sealed abstract class Response(val request: Request) {
-
-    def processor: Int = request.processor
-
-    def responseLog: Option[JsonNode] = None
-  }
-
-  /** responseLogValue should only be defined if request logging is enabled */
-  class SendResponse(request: Request,
-                     val responseSend: Send,
-                     val responseLogValue: Option[JsonNode]) extends 
Response(request) {
-    override def responseLog: Option[JsonNode] = responseLogValue
-
-    override def toString: String =
-      s"Response(type=Send, request=$request, send=$responseSend, 
asString=$responseLogValue)"
-  }
-
-  class NoOpResponse(request: Request) extends Response(request) {
-    override def toString: String =
-      s"Response(type=NoOp, request=$request)"
-  }
-
-  class CloseConnectionResponse(request: Request) extends Response(request) {
-    override def toString: String =
-      s"Response(type=CloseConnection, request=$request)"
-  }
-
-  class StartThrottlingResponse(request: Request) extends Response(request) {
-    override def toString: String =
-      s"Response(type=StartThrottling, request=$request)"
-  }
-
-  class EndThrottlingResponse(request: Request) extends Response(request) {
-    override def toString: String =
-      s"Response(type=EndThrottling, request=$request)"
-  }
 }
 
 class RequestChannel(val queueSize: Int,
@@ -125,7 +85,7 @@ class RequestChannel(val queueSize: Int,
     // This case is used when the request handler has encountered an error, 
but the client
     // does not expect a response (e.g. when produce request has acks set to 0)
     updateErrorMetrics(request.header.apiKey, errorCounts.asScala)
-    sendResponse(new RequestChannel.CloseConnectionResponse(request))
+    sendResponse(new CloseConnectionResponse(request))
   }
 
   def sendResponse(
@@ -133,19 +93,19 @@ class RequestChannel(val queueSize: Int,
     response: AbstractResponse
   ): Unit = {
     updateErrorMetrics(request.header.apiKey, response.errorCounts.asScala)
-    sendResponse(new RequestChannel.SendResponse(
+    sendResponse(new SendResponse(
       request,
       request.buildResponseSend(response),
-      request.responseNode(response).toScala
+      request.responseNode(response)
     ))
   }
 
   def sendNoOpResponse(request: Request): Unit = {
-    sendResponse(new RequestChannel.NoOpResponse(request))
+    sendResponse(new NoOpResponse(request))
   }
 
   def startThrottling(request: Request): Unit = {
-    sendResponse(new RequestChannel.StartThrottlingResponse(request))
+    sendResponse(new StartThrottlingResponse(request))
   }
 
   def endThrottling(request: Request): Unit = {
@@ -153,7 +113,7 @@ class RequestChannel(val queueSize: Int,
   }
 
   /** Send a response back to the socket server to be sent over the network */
-  private[network] def sendResponse(response: RequestChannel.Response): Unit = 
{
+  private[network] def sendResponse(response: Response): Unit = {
     if (isTraceEnabled) {
       val requestHeader = response.request.headerForLoggingOrThrottling()
       val message = response match {
@@ -186,7 +146,7 @@ class RequestChannel(val queueSize: Int,
       case _: StartThrottlingResponse | _: EndThrottlingResponse => ()
     }
 
-    val processor = processors.get(response.processor)
+    val processor = processors.get(response.request.processor)
     // The processor may be null if it was shutdown. In this case, the 
connections
     // are closed, so the response is dropped.
     if (processor != null) {
diff --git a/core/src/main/scala/kafka/network/SocketServer.scala 
b/core/src/main/scala/kafka/network/SocketServer.scala
index 50fe55427bf..291e689d2bd 100644
--- a/core/src/main/scala/kafka/network/SocketServer.scala
+++ b/core/src/main/scala/kafka/network/SocketServer.scala
@@ -26,7 +26,7 @@ import java.util.Optional
 import java.util.concurrent._
 import java.util.concurrent.atomic._
 import kafka.network.Processor._
-import kafka.network.RequestChannel.{CloseConnectionResponse, 
EndThrottlingResponse, NoOpResponse, SendResponse, StartThrottlingResponse}
+import org.apache.kafka.network.{CloseConnectionResponse, 
EndThrottlingResponse, NoOpResponse, Response, SendResponse, 
StartThrottlingResponse}
 import kafka.server.{BrokerReconfigurable, KafkaConfig}
 import org.apache.kafka.common.message.ApiMessageType.ListenerType
 import kafka.utils._
@@ -58,7 +58,6 @@ import org.slf4j.event.Level
 import scala.collection._
 import scala.collection.mutable.ArrayBuffer
 import scala.jdk.CollectionConverters._
-import scala.jdk.OptionConverters._
 import scala.util.control.ControlThrowable
 
 /**
@@ -829,8 +828,8 @@ private[kafka] class Processor(
   val thread: KafkaThread = KafkaThread.nonDaemon(threadName, this)
 
   private val newConnections = new 
ArrayBlockingQueue[SocketChannel](connectionQueueSize)
-  private val inflightResponses = mutable.Map[String, 
RequestChannel.Response]()
-  private val responseQueue = new 
LinkedBlockingDeque[RequestChannel.Response]()
+  private val inflightResponses = mutable.Map[String, Response]()
+  private val responseQueue = new LinkedBlockingDeque[Response]()
 
   private[kafka] val metricTags = mutable.LinkedHashMap(
     ListenerMetricTag -> listenerName.value,
@@ -935,7 +934,7 @@ private[kafka] class Processor(
   }
 
   private def processNewResponses(): Unit = {
-    var currentResponse: RequestChannel.Response = null
+    var currentResponse: Response = null
     while ({currentResponse = dequeueResponse(); currentResponse != null}) {
       val channelId = currentResponse.request.context.connectionId
       try {
@@ -964,8 +963,6 @@ private[kafka] class Processor(
             // the client.
             handleChannelMuteEvent(channelId, ChannelMuteEvent.THROTTLE_ENDED)
             tryUnmuteChannel(channelId)
-          case _ =>
-            throw new IllegalArgumentException(s"Unknown response type: 
${currentResponse.getClass}")
         }
       } catch {
         case e: Throwable =>
@@ -975,13 +972,13 @@ private[kafka] class Processor(
   }
 
   // `protected` for test usage
-  protected[network] def sendResponse(response: RequestChannel.Response, 
responseSend: Send): Unit = {
+  protected[network] def sendResponse(response: Response, responseSend: Send): 
Unit = {
     val connectionId = response.request.context.connectionId
     trace(s"Socket server received response to send to $connectionId, 
registering for write and sending data: $response")
     // `channel` can be None if the connection was closed remotely or if 
selector closed it for being idle for too long
     if (channel(connectionId).isEmpty) {
       warn(s"Attempting to send response via channel for which there is no 
open connection, connection id $connectionId")
-      response.request.updateRequestMetrics(0L, response.responseLog.toJava)
+      response.request.updateRequestMetrics(0L, response.responseLog)
     }
     // Invoke send for closingChannel as well so that the send is failed and 
the channel closed properly and
     // removed from the Selector after discarding any pending staged receives.
@@ -1083,10 +1080,10 @@ private[kafka] class Processor(
     selector.clearCompletedSends()
   }
 
-  private def updateRequestMetrics(response: RequestChannel.Response): Unit = {
+  private def updateRequestMetrics(response: Response): Unit = {
     val request = response.request
     val networkThreadTimeNanos = 
openOrClosingChannel(request.context.connectionId).fold(0L)(_.getAndResetNetworkThreadTimeNanos())
-    request.updateRequestMetrics(networkThreadTimeNanos, 
response.responseLog.toJava)
+    request.updateRequestMetrics(networkThreadTimeNanos, response.responseLog)
   }
 
   private def processDisconnected(): Unit = {
@@ -1205,12 +1202,12 @@ private[kafka] class Processor(
     connId
   }
 
-  private[network] def enqueueResponse(response: RequestChannel.Response): 
Unit = {
+  private[network] def enqueueResponse(response: Response): Unit = {
     responseQueue.put(response)
     wakeup()
   }
 
-  private def dequeueResponse(): RequestChannel.Response = {
+  private def dequeueResponse(): Response = {
     val response = responseQueue.poll()
     if (response != null)
       response.request.responseDequeueTimeNanos(Time.SYSTEM.nanoseconds)
diff --git 
a/core/src/test/scala/unit/kafka/network/RequestConvertToJsonTest.scala 
b/core/src/test/scala/unit/kafka/network/RequestConvertToJsonTest.scala
index c8dc9e8e729..621f59194f9 100644
--- a/core/src/test/scala/unit/kafka/network/RequestConvertToJsonTest.scala
+++ b/core/src/test/scala/unit/kafka/network/RequestConvertToJsonTest.scala
@@ -20,19 +20,19 @@ package kafka.network
 import java.net.InetAddress
 import java.nio.ByteBuffer
 import com.fasterxml.jackson.databind.node.{BooleanNode, DoubleNode, 
JsonNodeFactory, LongNode, NullNode, ObjectNode, TextNode}
+import java.util.Optional
 import org.apache.kafka.common.memory.MemoryPool
 import org.apache.kafka.common.message._
 import org.apache.kafka.common.network.{ClientInformation, ListenerName, 
NetworkSend}
 import org.apache.kafka.common.protocol.ApiKeys
 import org.apache.kafka.common.requests._
 import org.apache.kafka.common.security.auth.{KafkaPrincipal, SecurityProtocol}
-import org.apache.kafka.network.{Request, RequestConvertToJson}
+import org.apache.kafka.network.{Request, RequestConvertToJson, SendResponse}
 import org.apache.kafka.network.metrics.RequestChannelMetrics
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
 import org.mockito.Mockito.mock
 
-import scala.jdk.OptionConverters.RichOption
 
 class RequestConvertToJsonTest {
 
@@ -71,7 +71,7 @@ class RequestConvertToJsonTest {
     val req = request(alterIsrRequest)
     val send = new NetworkSend(req.context.connectionId, 
alterIsrRequest.toSend(req.header))
     val headerLog = RequestConvertToJson.requestHeaderNode(req.header)
-    val res = new RequestChannel.SendResponse(req, send, Some(headerLog))
+    val res = new SendResponse(req, send, Optional.of(headerLog))
 
     val totalTimeMs = 1
     val requestQueueTimeMs = 2
@@ -84,7 +84,7 @@ class RequestConvertToJsonTest {
     val messageConversionsTimeMs = 9
 
     val expectedNode = RequestConvertToJson.requestDesc(req.header, 
req.requestLog, req.isForwarded).asInstanceOf[ObjectNode]
-    expectedNode.set("response", 
res.responseLog.getOrElse(NullNode.getInstance()))
+    expectedNode.set("response", 
res.responseLog.orElse(NullNode.getInstance()))
     expectedNode.set("connection", new TextNode(req.context.connectionId))
     expectedNode.set("totalTimeMs", new DoubleNode(totalTimeMs))
     expectedNode.set("requestQueueTimeMs", new DoubleNode(requestQueueTimeMs))
@@ -100,7 +100,7 @@ class RequestConvertToJsonTest {
     expectedNode.set("temporaryMemoryBytes", new 
LongNode(temporaryMemoryBytes))
     expectedNode.set("messageConversionsTime", new 
DoubleNode(messageConversionsTimeMs))
 
-    val actualNode = RequestConvertToJson.requestDescMetrics(req.header, 
req.requestLog, res.responseLog.toJava, req.context, req.session, 
req.isForwarded,
+    val actualNode = RequestConvertToJson.requestDescMetrics(req.header, 
req.requestLog, res.responseLog, req.context, req.session, req.isForwarded,
       totalTimeMs, requestQueueTimeMs, apiLocalTimeMs, apiRemoteTimeMs, 
apiThrottleTimeMs, responseQueueTimeMs,
       responseSendTimeMs, temporaryMemoryBytes, 
messageConversionsTimeMs).asInstanceOf[ObjectNode]
 
diff --git a/core/src/test/scala/unit/kafka/network/SocketServerTest.scala 
b/core/src/test/scala/unit/kafka/network/SocketServerTest.scala
index 4e0de77c247..275b245532d 100644
--- a/core/src/test/scala/unit/kafka/network/SocketServerTest.scala
+++ b/core/src/test/scala/unit/kafka/network/SocketServerTest.scala
@@ -36,7 +36,7 @@ import org.apache.kafka.common.security.auth.{KafkaPrincipal, 
SecurityProtocol}
 import org.apache.kafka.common.security.scram.internals.ScramMechanism
 import org.apache.kafka.common.utils._
 import org.apache.kafka.common.utils.internals.{AppInfoParser, LogContext}
-import org.apache.kafka.network.{CallbackRequest, Request, 
RequestConvertToJson, ShutdownRequest, SocketServerConfigs, WakeupRequest}
+import org.apache.kafka.network.{CallbackRequest, NoOpResponse, Request, 
RequestConvertToJson, Response, SendResponse, ShutdownRequest, 
SocketServerConfigs, WakeupRequest}
 import org.apache.kafka.security.CredentialProvider
 import org.apache.kafka.server.{ApiVersionManager, SimpleApiVersionManager}
 import org.apache.kafka.server.common.{FinalizedFeatures, MetadataVersion}
@@ -60,7 +60,7 @@ import java.security.cert.X509Certificate
 import java.util
 import java.util.concurrent.atomic.AtomicInteger
 import java.util.concurrent._
-import java.util.{Properties, Random}
+import java.util.{Optional, Properties, Random}
 import javax.net.ssl._
 import scala.collection.mutable
 import scala.collection.mutable.ArrayBuffer
@@ -164,7 +164,7 @@ class SocketServerTest {
     val byteBuffer = 
request.body(classOf[AbstractRequest]).serializeWithHeader(request.header)
     val send = new NetworkSend(request.context.connectionId, 
ByteBufferSend.sizePrefixed(byteBuffer))
     val headerLog = RequestConvertToJson.requestHeaderNode(request.header)
-    channel.sendResponse(new RequestChannel.SendResponse(request, send, 
Some(headerLog)))
+    channel.sendResponse(new SendResponse(request, send, 
Optional.of(headerLog)))
   }
 
   def processRequestNoOpResponse(channel: RequestChannel, request: Request): 
Unit = {
@@ -652,9 +652,9 @@ class SocketServerTest {
     val headerLog = RequestConvertToJson.requestHeaderNode(request.header)
     val response =
       if (!noOpResponse)
-        new RequestChannel.SendResponse(request, send, Some(headerLog))
+        new SendResponse(request, send, Optional.of(headerLog))
       else
-        new RequestChannel.NoOpResponse(request)
+        new NoOpResponse(request)
     server.dataPlaneRequestChannel.sendResponse(response)
 
     // Quota manager would call notifyThrottlingDone() on throttling 
completion. Simulate it if throttlingInProgress is
@@ -1104,7 +1104,7 @@ class SocketServerTest {
       val send = new NetworkSend(request.context.connectionId, 
ByteBufferSend.sizePrefixed(ByteBuffer.allocate(responseBufferSize)))
       val headerLog = new ObjectNode(JsonNodeFactory.instance)
       headerLog.set("response", new TextNode("someResponse"))
-      channel.sendResponse(new RequestChannel.SendResponse(request, send, 
Some(headerLog)))
+      channel.sendResponse(new SendResponse(request, send, 
Optional.of(headerLog)))
 
       TestUtils.waitUntilTrue(() => totalTimeHistCount() == 
expectedTotalTimeCount,
         s"request metrics not updated, expected: $expectedTotalTimeCount, 
actual: ${totalTimeHistCount()}")
@@ -2083,7 +2083,7 @@ class SocketServerTest {
       this.conn = Some(conn)
     }
 
-    override protected[network] def sendResponse(response: 
RequestChannel.Response, responseSend: Send): Unit = {
+    override protected[network] def sendResponse(response: Response, 
responseSend: Send): Unit = {
       this.conn.foreach(_.close())
       super.sendResponse(response, responseSend)
     }
diff --git a/core/src/test/scala/unit/kafka/server/KafkaApisTest.scala 
b/core/src/test/scala/unit/kafka/server/KafkaApisTest.scala
index 97b458e27b3..a0079dd99eb 100644
--- a/core/src/test/scala/unit/kafka/server/KafkaApisTest.scala
+++ b/core/src/test/scala/unit/kafka/server/KafkaApisTest.scala
@@ -88,7 +88,7 @@ import org.apache.kafka.coordinator.share.{ShareCoordinator, 
ShareCoordinatorTes
 import org.apache.kafka.coordinator.transaction.{InitProducerIdResult, 
TransactionLogConfig}
 import org.apache.kafka.image.{MetadataDelta, MetadataImage, 
MetadataProvenance}
 import org.apache.kafka.metadata.{ConfigRepository, KRaftMetadataCache, 
MetadataCache, MetadataCacheFixtures, MockConfigRepository}
-import org.apache.kafka.network.{Request, Session}
+import org.apache.kafka.network.{Request, SendResponse, Session}
 import org.apache.kafka.network.metrics.{RequestChannelMetrics, RequestMetrics}
 import org.apache.kafka.raft.{KRaftConfigs, QuorumConfig}
 import org.apache.kafka.security.authorizer.AclEntry
@@ -127,7 +127,6 @@ import java.util.function.Consumer
 import java.util.{Comparator, Optional, OptionalInt, OptionalLong, Properties}
 import scala.collection.{Map, Seq, mutable}
 import scala.jdk.CollectionConverters._
-import scala.jdk.OptionConverters._
 
 class KafkaApisTest extends Logging {
   private val requestChannel: RequestChannel = mock(classOf[RequestChannel])
@@ -10412,13 +10411,13 @@ class KafkaApisTest extends Logging {
       request.context.header.apiVersion
     )
 
-    // Create the RequestChannel.Response that is created when sendResponse is 
called in order to update the metrics.
-    val sendResponse = new RequestChannel.SendResponse(
+    // Create the Response that is created when sendResponse is called in 
order to update the metrics.
+    val sendResponse = new SendResponse(
       request,
       request.buildResponseSend(response),
-      request.responseNode(response).toScala
+      request.responseNode(response)
     )
-    request.updateRequestMetrics(time.milliseconds(), 
sendResponse.responseLog.toJava)
+    request.updateRequestMetrics(time.milliseconds(), sendResponse.responseLog)
 
     AbstractResponse.parseResponse(
       request.context.header.apiKey,
diff --git 
a/server/src/main/java/org/apache/kafka/network/CloseConnectionResponse.java 
b/server/src/main/java/org/apache/kafka/network/CloseConnectionResponse.java
new file mode 100644
index 00000000000..73fa00ce854
--- /dev/null
+++ b/server/src/main/java/org/apache/kafka/network/CloseConnectionResponse.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kafka.network;
+
+public record CloseConnectionResponse(Request request) implements Response {
+
+    @Override
+    public String toString() {
+        return "Response(type=CloseConnection, request=" + request + ")";
+    }
+}
diff --git 
a/server/src/main/java/org/apache/kafka/network/EndThrottlingResponse.java 
b/server/src/main/java/org/apache/kafka/network/EndThrottlingResponse.java
new file mode 100644
index 00000000000..c9fcacb7f6d
--- /dev/null
+++ b/server/src/main/java/org/apache/kafka/network/EndThrottlingResponse.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kafka.network;
+
+public record EndThrottlingResponse(Request request) implements Response {
+
+    @Override
+    public String toString() {
+        return "Response(type=EndThrottling, request=" + request + ")";
+    }
+}
diff --git a/server/src/main/java/org/apache/kafka/network/NoOpResponse.java 
b/server/src/main/java/org/apache/kafka/network/NoOpResponse.java
new file mode 100644
index 00000000000..8c7ad667d8e
--- /dev/null
+++ b/server/src/main/java/org/apache/kafka/network/NoOpResponse.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kafka.network;
+
+public record NoOpResponse(Request request) implements Response {
+
+    @Override
+    public String toString() {
+        return "Response(type=NoOp, request=" + request + ")";
+    }
+}
diff --git a/server/src/main/java/org/apache/kafka/network/Response.java 
b/server/src/main/java/org/apache/kafka/network/Response.java
new file mode 100644
index 00000000000..eb51c0fcc07
--- /dev/null
+++ b/server/src/main/java/org/apache/kafka/network/Response.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kafka.network;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.util.Optional;
+
+public sealed interface Response permits SendResponse, NoOpResponse, 
CloseConnectionResponse, StartThrottlingResponse, EndThrottlingResponse {
+
+    Request request();
+
+    default Optional<JsonNode> responseLog() {
+        return Optional.empty();
+    }
+}
diff --git a/server/src/main/java/org/apache/kafka/network/SendResponse.java 
b/server/src/main/java/org/apache/kafka/network/SendResponse.java
new file mode 100644
index 00000000000..2d991700514
--- /dev/null
+++ b/server/src/main/java/org/apache/kafka/network/SendResponse.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kafka.network;
+
+import org.apache.kafka.common.network.Send;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.util.Optional;
+
+/**
+ * @param responseLog should only be defined if request logging is enabled
+ */
+public record SendResponse(Request request, Send responseSend, 
Optional<JsonNode> responseLog) implements Response {
+
+    @Override
+    public String toString() {
+        return "Response(type=Send, request=" + request + ", send=" + 
responseSend + ", asString=" + responseLog + ")";
+    }
+}
diff --git 
a/server/src/main/java/org/apache/kafka/network/StartThrottlingResponse.java 
b/server/src/main/java/org/apache/kafka/network/StartThrottlingResponse.java
new file mode 100644
index 00000000000..016745545a4
--- /dev/null
+++ b/server/src/main/java/org/apache/kafka/network/StartThrottlingResponse.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kafka.network;
+
+public record StartThrottlingResponse(Request request) implements Response {
+
+    @Override
+    public String toString() {
+        return "Response(type=StartThrottling, request=" + request + ")";
+    }
+}

Reply via email to