You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							290 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							290 lines
						
					
					
						
							11 KiB
						
					
					
				
								import org.apache.kafka.clients.admin.AdminClient;
							 | 
						|
								import org.apache.kafka.clients.admin.AdminClientConfig;
							 | 
						|
								import org.apache.kafka.clients.admin.DescribeClusterResult;
							 | 
						|
								import org.apache.kafka.common.Node;
							 | 
						|
								
							 | 
						|
								import java.io.*;
							 | 
						|
								import java.net.*;
							 | 
						|
								import java.nio.ByteBuffer;
							 | 
						|
								import java.util.*;
							 | 
						|
								import java.util.concurrent.ExecutionException;
							 | 
						|
								
							 | 
						|
								public class AdminClientDebugger {
							 | 
						|
								
							 | 
						|
								    public static void main(String[] args) throws Exception {
							 | 
						|
								        String broker = args.length > 0 ? args[0] : "localhost:9093";
							 | 
						|
								
							 | 
						|
								        System.out.println("=".repeat(80));
							 | 
						|
								        System.out.println("KAFKA ADMINCLIENT DEBUGGER");
							 | 
						|
								        System.out.println("=".repeat(80));
							 | 
						|
								        System.out.println("Target broker: " + broker);
							 | 
						|
								
							 | 
						|
								        // Test 1: Raw socket - capture exact bytes
							 | 
						|
								        System.out.println("\n" + "=".repeat(80));
							 | 
						|
								        System.out.println("TEST 1: Raw Socket - Capture ApiVersions Exchange");
							 | 
						|
								        System.out.println("=".repeat(80));
							 | 
						|
								        testRawSocket(broker);
							 | 
						|
								
							 | 
						|
								        // Test 2: AdminClient with detailed logging
							 | 
						|
								        System.out.println("\n" + "=".repeat(80));
							 | 
						|
								        System.out.println("TEST 2: AdminClient with Logging");
							 | 
						|
								        System.out.println("=".repeat(80));
							 | 
						|
								        testAdminClient(broker);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    private static void testRawSocket(String broker) {
							 | 
						|
								        String[] parts = broker.split(":");
							 | 
						|
								        String host = parts[0];
							 | 
						|
								        int port = Integer.parseInt(parts[1]);
							 | 
						|
								
							 | 
						|
								        try (Socket socket = new Socket(host, port)) {
							 | 
						|
								            socket.setSoTimeout(10000);
							 | 
						|
								
							 | 
						|
								            InputStream in = socket.getInputStream();
							 | 
						|
								            OutputStream out = socket.getOutputStream();
							 | 
						|
								
							 | 
						|
								            System.out.println("Connected to " + broker);
							 | 
						|
								
							 | 
						|
								            // Build ApiVersions request (v4)
							 | 
						|
								            // Format:
							 | 
						|
								            // [Size][ApiKey=18][ApiVersion=4][CorrelationId=0][ClientId][TaggedFields]
							 | 
						|
								            ByteArrayOutputStream requestBody = new ByteArrayOutputStream();
							 | 
						|
								
							 | 
						|
								            // ApiKey (2 bytes) = 18
							 | 
						|
								            requestBody.write(0);
							 | 
						|
								            requestBody.write(18);
							 | 
						|
								
							 | 
						|
								            // ApiVersion (2 bytes) = 4
							 | 
						|
								            requestBody.write(0);
							 | 
						|
								            requestBody.write(4);
							 | 
						|
								
							 | 
						|
								            // CorrelationId (4 bytes) = 0
							 | 
						|
								            requestBody.write(new byte[] { 0, 0, 0, 0 });
							 | 
						|
								
							 | 
						|
								            // ClientId (compact string) = "debug-client"
							 | 
						|
								            String clientId = "debug-client";
							 | 
						|
								            writeCompactString(requestBody, clientId);
							 | 
						|
								
							 | 
						|
								            // Tagged fields (empty)
							 | 
						|
								            requestBody.write(0x00);
							 | 
						|
								
							 | 
						|
								            byte[] request = requestBody.toByteArray();
							 | 
						|
								
							 | 
						|
								            // Write size
							 | 
						|
								            ByteBuffer sizeBuffer = ByteBuffer.allocate(4);
							 | 
						|
								            sizeBuffer.putInt(request.length);
							 | 
						|
								            out.write(sizeBuffer.array());
							 | 
						|
								
							 | 
						|
								            // Write request
							 | 
						|
								            out.write(request);
							 | 
						|
								            out.flush();
							 | 
						|
								
							 | 
						|
								            System.out.println("\nSENT ApiVersions v4 Request:");
							 | 
						|
								            System.out.println("   Size: " + request.length + " bytes");
							 | 
						|
								            hexDump("   Request", request, Math.min(64, request.length));
							 | 
						|
								
							 | 
						|
								            // Read response size
							 | 
						|
								            byte[] sizeBytes = new byte[4];
							 | 
						|
								            int read = in.read(sizeBytes);
							 | 
						|
								            if (read != 4) {
							 | 
						|
								                System.out.println("Failed to read response size (got " + read + " bytes)");
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            int responseSize = ByteBuffer.wrap(sizeBytes).getInt();
							 | 
						|
								            System.out.println("\nRECEIVED Response:");
							 | 
						|
								            System.out.println("   Size: " + responseSize + " bytes");
							 | 
						|
								
							 | 
						|
								            // Read response body
							 | 
						|
								            byte[] responseBytes = new byte[responseSize];
							 | 
						|
								            int totalRead = 0;
							 | 
						|
								            while (totalRead < responseSize) {
							 | 
						|
								                int n = in.read(responseBytes, totalRead, responseSize - totalRead);
							 | 
						|
								                if (n == -1) {
							 | 
						|
								                    System.out.println("Unexpected EOF after " + totalRead + " bytes");
							 | 
						|
								                    return;
							 | 
						|
								                }
							 | 
						|
								                totalRead += n;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            System.out.println("   Read complete response: " + totalRead + " bytes");
							 | 
						|
								
							 | 
						|
								            // Decode response
							 | 
						|
								            System.out.println("\nRESPONSE STRUCTURE:");
							 | 
						|
								            decodeApiVersionsResponse(responseBytes);
							 | 
						|
								
							 | 
						|
								            // Try to read more (should timeout or get EOF)
							 | 
						|
								            System.out.println("\n⏱️  Waiting for any additional data (10s timeout)...");
							 | 
						|
								            socket.setSoTimeout(10000);
							 | 
						|
								            try {
							 | 
						|
								                int nextByte = in.read();
							 | 
						|
								                if (nextByte == -1) {
							 | 
						|
								                    System.out.println("   Server closed connection (EOF)");
							 | 
						|
								                } else {
							 | 
						|
								                    System.out.println("   Unexpected data: " + nextByte);
							 | 
						|
								                }
							 | 
						|
								            } catch (SocketTimeoutException e) {
							 | 
						|
								                System.out.println("   Timeout - no additional data");
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								        } catch (Exception e) {
							 | 
						|
								            System.out.println("Error: " + e.getMessage());
							 | 
						|
								            e.printStackTrace();
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    private static void testAdminClient(String broker) {
							 | 
						|
								        Properties props = new Properties();
							 | 
						|
								        props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, broker);
							 | 
						|
								        props.put(AdminClientConfig.CLIENT_ID_CONFIG, "admin-client-debugger");
							 | 
						|
								        props.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, 10000);
							 | 
						|
								        props.put(AdminClientConfig.DEFAULT_API_TIMEOUT_MS_CONFIG, 10000);
							 | 
						|
								
							 | 
						|
								        System.out.println("Creating AdminClient with config:");
							 | 
						|
								        props.forEach((k, v) -> System.out.println("  " + k + " = " + v));
							 | 
						|
								
							 | 
						|
								        try (AdminClient adminClient = AdminClient.create(props)) {
							 | 
						|
								            System.out.println("AdminClient created");
							 | 
						|
								
							 | 
						|
								            // Give the thread time to start
							 | 
						|
								            Thread.sleep(1000);
							 | 
						|
								
							 | 
						|
								            System.out.println("\nCalling describeCluster()...");
							 | 
						|
								            DescribeClusterResult result = adminClient.describeCluster();
							 | 
						|
								
							 | 
						|
								            System.out.println("   Waiting for nodes...");
							 | 
						|
								            Collection<Node> nodes = result.nodes().get();
							 | 
						|
								
							 | 
						|
								            System.out.println("Cluster description retrieved:");
							 | 
						|
								            System.out.println("   Nodes: " + nodes.size());
							 | 
						|
								            for (Node node : nodes) {
							 | 
						|
								                System.out.println("     - Node " + node.id() + ": " + node.host() + ":" + node.port());
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            System.out.println("\n   Cluster ID: " + result.clusterId().get());
							 | 
						|
								
							 | 
						|
								            Node controller = result.controller().get();
							 | 
						|
								            if (controller != null) {
							 | 
						|
								                System.out.println("   Controller: Node " + controller.id());
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								        } catch (ExecutionException e) {
							 | 
						|
								            System.out.println("Execution error: " + e.getCause().getMessage());
							 | 
						|
								            e.getCause().printStackTrace();
							 | 
						|
								        } catch (Exception e) {
							 | 
						|
								            System.out.println("Error: " + e.getMessage());
							 | 
						|
								            e.printStackTrace();
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    private static void decodeApiVersionsResponse(byte[] data) {
							 | 
						|
								        int offset = 0;
							 | 
						|
								
							 | 
						|
								        try {
							 | 
						|
								            // Correlation ID (4 bytes)
							 | 
						|
								            int correlationId = ByteBuffer.wrap(data, offset, 4).getInt();
							 | 
						|
								            System.out.println("   [Offset " + offset + "] Correlation ID: " + correlationId);
							 | 
						|
								            offset += 4;
							 | 
						|
								
							 | 
						|
								            // Header tagged fields (varint - should be 0x00 for flexible v3+)
							 | 
						|
								            int taggedFieldsLength = readUnsignedVarint(data, offset);
							 | 
						|
								            System.out.println("   [Offset " + offset + "] Header Tagged Fields Length: " + taggedFieldsLength);
							 | 
						|
								            offset += varintSize(data[offset]);
							 | 
						|
								
							 | 
						|
								            // Error code (2 bytes)
							 | 
						|
								            short errorCode = ByteBuffer.wrap(data, offset, 2).getShort();
							 | 
						|
								            System.out.println("   [Offset " + offset + "] Error Code: " + errorCode);
							 | 
						|
								            offset += 2;
							 | 
						|
								
							 | 
						|
								            // API Keys array (compact array - varint length)
							 | 
						|
								            int apiKeysLength = readUnsignedVarint(data, offset) - 1; // Compact array: length+1
							 | 
						|
								            System.out.println("   [Offset " + offset + "] API Keys Count: " + apiKeysLength);
							 | 
						|
								            offset += varintSize(data[offset]);
							 | 
						|
								
							 | 
						|
								            // Show first few API keys
							 | 
						|
								            System.out.println("   First 5 API Keys:");
							 | 
						|
								            for (int i = 0; i < Math.min(5, apiKeysLength); i++) {
							 | 
						|
								                short apiKey = ByteBuffer.wrap(data, offset, 2).getShort();
							 | 
						|
								                offset += 2;
							 | 
						|
								                short minVersion = ByteBuffer.wrap(data, offset, 2).getShort();
							 | 
						|
								                offset += 2;
							 | 
						|
								                short maxVersion = ByteBuffer.wrap(data, offset, 2).getShort();
							 | 
						|
								                offset += 2;
							 | 
						|
								                // Per-element tagged fields
							 | 
						|
								                int perElementTagged = readUnsignedVarint(data, offset);
							 | 
						|
								                offset += varintSize(data[offset]);
							 | 
						|
								
							 | 
						|
								                System.out.println("     " + (i + 1) + ". API " + apiKey + ": v" + minVersion + "-v" + maxVersion);
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            System.out.println("   ... (showing first 5 of " + apiKeysLength + " APIs)");
							 | 
						|
								            System.out.println("   Response structure is valid!");
							 | 
						|
								
							 | 
						|
								            // Hex dump of first 64 bytes
							 | 
						|
								            hexDump("\n   First 64 bytes", data, Math.min(64, data.length));
							 | 
						|
								
							 | 
						|
								        } catch (Exception e) {
							 | 
						|
								            System.out.println("   Failed to decode at offset " + offset + ": " + e.getMessage());
							 | 
						|
								            hexDump("   Raw bytes", data, Math.min(128, data.length));
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    private static int readUnsignedVarint(byte[] data, int offset) {
							 | 
						|
								        int value = 0;
							 | 
						|
								        int shift = 0;
							 | 
						|
								        while (true) {
							 | 
						|
								            byte b = data[offset++];
							 | 
						|
								            value |= (b & 0x7F) << shift;
							 | 
						|
								            if ((b & 0x80) == 0)
							 | 
						|
								                break;
							 | 
						|
								            shift += 7;
							 | 
						|
								        }
							 | 
						|
								        return value;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    private static int varintSize(byte firstByte) {
							 | 
						|
								        int size = 1;
							 | 
						|
								        byte b = firstByte;
							 | 
						|
								        while ((b & 0x80) != 0) {
							 | 
						|
								            size++;
							 | 
						|
								            b = (byte) (b << 1);
							 | 
						|
								        }
							 | 
						|
								        return size;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    private static void writeCompactString(ByteArrayOutputStream out, String str) {
							 | 
						|
								        byte[] bytes = str.getBytes();
							 | 
						|
								        writeUnsignedVarint(out, bytes.length + 1); // Compact string: length+1
							 | 
						|
								        out.write(bytes, 0, bytes.length);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    private static void writeUnsignedVarint(ByteArrayOutputStream out, int value) {
							 | 
						|
								        while ((value & ~0x7F) != 0) {
							 | 
						|
								            out.write((byte) ((value & 0x7F) | 0x80));
							 | 
						|
								            value >>>= 7;
							 | 
						|
								        }
							 | 
						|
								        out.write((byte) value);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    private static void hexDump(String label, byte[] data, int length) {
							 | 
						|
								        System.out.println(label + " (hex dump):");
							 | 
						|
								        for (int i = 0; i < length; i += 16) {
							 | 
						|
								            System.out.printf("      %04x  ", i);
							 | 
						|
								            for (int j = 0; j < 16; j++) {
							 | 
						|
								                if (i + j < length) {
							 | 
						|
								                    System.out.printf("%02x ", data[i + j] & 0xFF);
							 | 
						|
								                } else {
							 | 
						|
								                    System.out.print("   ");
							 | 
						|
								                }
							 | 
						|
								                if (j == 7)
							 | 
						|
								                    System.out.print(" ");
							 | 
						|
								            }
							 | 
						|
								            System.out.print(" |");
							 | 
						|
								            for (int j = 0; j < 16 && i + j < length; j++) {
							 | 
						|
								                byte b = data[i + j];
							 | 
						|
								                System.out.print((b >= 32 && b < 127) ? (char) b : '.');
							 | 
						|
								            }
							 | 
						|
								            System.out.println("|");
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								}
							 |