Skip to main content

Local debugging guide — Java

Last updated on March 20, 2026

This guide covers everything specific to debugging an Extend Service Extension app written in Java. For concepts that apply to all languages — architecture, environment variables, log format, and common issues — see the main debugging guide.


Prerequisites

  • Java 17 or later — verify with:

    java --version
    # Expected: openjdk 17.x.x ... or later
  • VS Code Extension Pack for Java (vscjava.vscode-java-pack) — install from the VS Code marketplace or accept the recommended extensions prompt when you open the repository.

  • AccelByte credentialsAB_BASE_URL, AB_CLIENT_ID, AB_CLIENT_SECRET, and BASE_PATH. See Environment setup in the main guide.


Project structure

src/
└── main/
├── java/
│ └── com/accelbyte/extend/serviceextension/
│ ├── Application.java # Entry point
│ ├── service/
│ │ └── MyService.java # Your business logic
│ ├── interceptor/
│ │ └── AuthServerInterceptor.java # IAM token and permission validation
│ └── ...
└── proto/
└── service.proto # gRPC API definition
FileWhat it does
Application.javaEntry point — wires gRPC server, gRPC-Gateway, metrics, and auth.
service/MyService.javaYour business logic — implements the gRPC service methods.
interceptor/AuthServerInterceptor.javaValidates every incoming request's IAM token and permission.
proto/service.protoDefines the gRPC API — endpoints, request/response shapes, and required permissions.

Port numbers:

PortPurpose
6565gRPC server (internal — used by gRPC-Gateway)
8000gRPC-Gateway HTTP/REST — call this from a browser, Postman, or curl
8080Prometheus metrics endpoint (/metrics)

Running the service locally

From the terminal

# Export all variables from your .env file
export $(grep -v '^#' .env | xargs)

./gradlew bootRun

From VS Code

Use Terminal → Run Task → "Run: Service". This task is defined in .vscode/tasks.json and loads the .env file for you automatically.

Confirming the service is up

You should see logs like:

{"time":"...","level":"INFO","msg":"app server started","service":"extend-app-service-extension"}
{"time":"...","level":"INFO","msg":"starting gRPC-Gateway HTTP server","port":8000}
{"time":"...","level":"INFO","msg":"serving prometheus metrics","port":8080,"endpoint":"/metrics"}

Open http://localhost:8000<BASE_PATH>/apidocs/ in your browser to verify Swagger UI is live.


Attaching the debugger

The repository ships with a ready-to-use launch configuration in .vscode/launch.json that uses the built-in Java debugger from the Extension Pack for Java:

{
"name": "Debug: Service",
"type": "java",
"request": "launch",
"mainClass": "com.accelbyte.extend.serviceextension.Application",
"envFile": "${workspaceFolder}/.env",
"cwd": "${workspaceFolder}"
}

Steps:

  1. Fill in .env (see Environment setup).
  2. Open the Run and Debug panel (Ctrl+Shift+D / Cmd+Shift+D).
  3. Select "Debug: Service" from the dropdown.
  4. Press F5.

Other IDEs — JVM remote debug

Start the app with the JVM debug agent, which listens on port 5005 by default:

export $(grep -v '^#' .env | xargs)
./gradlew bootRun --debug-jvm

Then open your IDE (IntelliJ IDEA, Eclipse) and create a Remote JVM Debug configuration targeting localhost:5005.


Where to put breakpoints

What you want to investigateFile and location
A specific REST endpoint being calledservice/MyService.java — top of the relevant method
Auth/token validation failureinterceptor/AuthServerInterceptor.java — the interceptCall method
Data not saving or loading correctlyThe repository or storage class used in your service
Service not starting at allApplication.java — the main method and bean initialization

Conditional breakpoint syntax

Right-click a breakpoint → Edit Breakpoint → enter a Java expression, for example:

request.getGuildId().equals("guild_001")

The debugger only pauses when the condition is true.


Reading logs

Pipe output through jq for readable JSON logs:

./gradlew bootRun 2>&1 | jq '.'

# Show only ERROR lines
./gradlew bootRun 2>&1 | jq 'select(.level == "ERROR")'

Testing endpoints manually

curl

# Create or update guild progress
curl -s -X POST \
"http://localhost:8000/guild/v1/admin/namespace/mygame/progress" \
-H "Content-Type: application/json" \
-d '{"guildProgress": {"guildId": "guild_001", "namespace": "mygame"}}' | jq .

# Get guild progress
curl -s "http://localhost:8000/guild/v1/admin/namespace/mygame/progress/guild_001" | jq .

Add -H "Authorization: Bearer <your-token>" when PLUGIN_GRPC_SERVER_AUTH_ENABLED=true.

grpcurl (gRPC layer directly)

# List available services
grpcurl -plaintext localhost:6565 list

# Call a method directly
grpcurl -plaintext \
-d '{"namespace":"mygame","guildProgress":{"guildId":"guild_001"}}' \
localhost:6565 service.Service/CreateOrUpdateGuildProgress

Java-specific troubleshooting

Proto changes have no effect

Symptom: You edited src/main/proto/service.proto but nothing changed at runtime.

Cause: The generated gRPC stubs have not been rebuilt.

Fix: Run a clean build to trigger the protobuf Gradle plugin:

./gradlew clean build

Gradle daemon issues

If the service behaves inconsistently between runs, stop the Gradle daemon and try again:

./gradlew --stop
./gradlew bootRun

JVM port conflict for remote debugging

If port 5005 is already in use when running --debug-jvm:

ss -tlnp | grep 5005

Kill the occupying process or change the debug port in build.gradle:

bootRun {
jvmArgs = ["-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5006"]
}

Checking for port conflicts (service ports)

ss -tlnp | grep -E '6565|8000|8080'

Kill any stale process and start again.


AI assistance

The app template ships with a Claude agent skill at .claude/skills/debugging-guide/SKILL.md. Copy the full skill file into your own repository and activate it in your AI assistant.

For the full skill content and prompting tips, see the AI assistance section in the main guide.


References