# Handle Query (/sdks/java/how-to/rpc/query-handle)



## Overview [#overview]

A query handler is the answering side of KubeMQ's request/response RPC pattern — the code that does real work and sends back data, unlike a Command handler, which only acknowledges receipt. Reach for it whenever a caller needs an actual answer — a lookup result, a computed value, a status object — not just confirmation that a message arrived.

Registering a handler with `subscribeToQueries` and `onReceiveQueryCallback` opens a subscription; the broker delivers every matching query to your callback as it arrives. The callback builds a `QueryResponseMessage` carrying the original `queryReceived` back to the broker, so the answer routes to the specific caller blocked waiting, and sets `body` with the real result via `sendResponseMessage` before returning.

**Gotchas:** if the handler never sends a response, the caller blocks until its own `timeoutInSeconds` elapses and fails with a timeout, not a fast error. An exception inside the callback doesn't automatically become a failure reply, so uncaught errors can leave the sender hanging. And because every matching query lands on the same callback thread pool, slow or blocking handler code delays every other in-flight caller.

## Prerequisites [#prerequisites]

* KubeMQ server running on `localhost:50000`
* Java SDK installed (`implementation 'io.kubemq.sdk:kubemq-sdk-Java:3.1.1'` (Gradle) or Maven dependency from [Getting Started](/sdks/java))

## Code [#code]

```java title="HandleQueryExample.java"
package io.kubemq.example.queries;

import io.kubemq.sdk.cq.*;

public class HandleQueryExample {
    private static final String ADDRESS = "localhost:50000";
    private static final String CLIENT_ID = "java-queries-handle-query-client";
    private static final String CHANNEL = "java-queries.handle-query";

    public static void main(String[] args) throws InterruptedException {
        // Create a client connected to the KubeMQ server
        CQClient client = CQClient.builder().address(ADDRESS).clientId(CLIENT_ID).build();
        client.ping();
        // Create the queries channel
        client.createQueriesChannel(CHANNEL);

        // Subscribe to handle incoming queries
        QueriesSubscription sub = QueriesSubscription.builder()
                .channel(CHANNEL)
                .onReceiveQueryCallback(query -> {
                    String request = new String(query.getBody());
                    System.out.println("Handling query: " + request);
                    String responseData = "{\"users\": [{\"id\": 1, \"name\": \"Alice\"}]}";

                    // Send response back to the query sender
                    client.sendResponseMessage(QueryResponseMessage.builder()
                            .queryReceived(query).isExecuted(true)
                            .body(responseData.getBytes()).build());
                })
                .onErrorCallback(err -> System.err.println("Error: " + err))
                .build();
        // Start the query handler subscription
        client.subscribeToQueries(sub);
        System.out.println("Query handler listening on: " + CHANNEL);
        Thread.sleep(300);

        // Send a query and wait for the response
        QueryResponseMessage resp = client.sendQuery(QueryMessage.builder()
                .channel(CHANNEL).body("listUsers".getBytes()).timeoutInSeconds(10).build());
        System.out.println("Response: " + new String(resp.getBody()));

        // Clean up resources
        sub.cancel();
        client.deleteQueriesChannel(CHANNEL);
        client.close();
    }
}

```

## How It Works [#how-it-works]

* `subscribeToQueries()` registers a long-lived gRPC streaming subscription; the SDK delivers each inbound query to `onReceiveQueryCallback` on a background thread.
* The callback must call `client.sendResponseMessage(QueryResponseMessage.builder().queryReceived(query)...)` — if the callback returns without sending a response, the caller's `sendQuery()` will block until `timeoutInSeconds` elapses.
* `queryReceived(query)` on the response builder automatically copies the correlation ID needed for KubeMQ to route the response back.
* Keep the callback non-blocking; offload heavy computation to a thread pool and send the response from there.

## Related [#related]

* [Pattern overview](/learn/rpc/getting-started)
* [Java SDK Reference](/sdks/java/reference/rpc)
* [Send Query](/sdks/java/tutorials/query-send)
* [Cached Query](/sdks/java/how-to/rpc/query-cached)
