Debugging WebSocket Applications With NetBeans IDE

WebSocket applications have a habit of failing in places that unit tests never reach. A handshake succeeds in development, the deployment looks clean, and yet once real clients connect from places as far apart as a Perth mining site and a Hobart café, frames start to drop or arrive out of order. The NetBeans IDE ships with a debugger that is well suited to this kind of live, asynchronous traffic, and getting comfortable with it pays off across both small internal tools and large customer-facing services.

This guide walks through a practical workflow that an Australian developer can apply on a Monday morning in Melbourne or a Friday afternoon in Brisbane. The steps assume a Jakarta EE or plain Java SE setup with the embedded GlassFish server, but the same techniques apply when NetBeans points at Payara or WildFly. Along the way we will look at breakpoint placement, frame inspection, hot code replacement, and a handful of habits worth keeping between releases.

Preparing Your WebSocket Project in NetBeans

Open the project that contains your endpoint classes and confirm the server endpoint is annotated correctly. NetBeans recognises @ServerEndpoint, @OnOpen, @OnMessage, and @OnClose and highlights them in the editor. If you generate the class from a template, the IDE adds a placeholder onMessage method that returns a string, which is useful for the first smoke test but rarely what you ship.

Before you press Debug, verify the project properties point at a JDK that supports the WebSocket API you are using. Jakarta EE 10 requires JDK 11 or newer, while the older Java EE 7 API runs fine on JDK 8. Australian teams that still maintain a legacy stack for a Sydney-based financial client often keep two JDKs installed, switching through NetBeans → Tools → Java Platforms. Once that is set, build the project once with F11 to catch any compile errors that would otherwise hide the real bug when the debugger attaches.

Starting the Debugger and Attaching to the Server

Hit Ctrl+F5 to launch the project in debug mode rather than the green Run arrow. NetBeans will start GlassFish (or your chosen server), deploy the WAR, and open the debugger perspective with the call stack and variables panes ready. The server runs in the same JVM as the IDE for embedded GlassFish, which means breakpoints inside endpoint methods will trigger without any extra JPDA configuration.

For remote debugging against a server hosted in a different region, say a staging instance in ap-southeast-2, add the JVM flags -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:4000 to the server startup and create a Debugger attach preset in NetBeans. The suspend=n flag is the practical choice for WebSocket work because you do not want every connected client to freeze while you step through code on a Tuesday arvo.

Watching Frames Move Between Client and Server

Once the debugger is attached, place a breakpoint inside the onMessage method and let real traffic hit the endpoint. NetBeans pauses on the line, and the Variables window shows the Session object, the incoming message, and any config parameters. Right-clicking the message string lets you view it as text, hex, or a parsed JSON tree if the Gson or Jackson libraries are on the classpath.

Stepping over each line with F8 reveals how the message flows through your handler. For binary frames, switch the breakpoint to the method that wraps the ByteBuffer so you can see the raw payload before your code interprets it. This is the moment when subtle bugs surface, like a UTF-8 boundary split across two frames, a missing RemoteEndpoint.Basic.sendObject flush, or a partial JSON string. Many Brisbane-based teams I have worked with keep a small standalone HTML client bookmarked for quick testing; it saves spinning up the full front-end just to send one frame.

Working With Hot Code Replacement Mid-Session

One of the most useful features during a long debug session is hot code replacement, and NetBeans supports it well for plain Java classes. While the debugger is paused on a breakpoint, edit a method body, save, and the IDE recompiles and swaps the bytecode without restarting the server. Open WebSocket sessions continue to use the new method on their next call, which means you can patch a message handler while clients are still chatting away in Sydney and Perth.

Configuration details are covered separately in this configure hot code replacement walkthrough, but the short version is to enable "Apply code changes after save" under Debugger options. Structural changes, like adding fields, changing signatures, or swapping class hierarchies, will still require a redeploy, so keep your edits small and surgical.

Static Analysis and Pre-Release Checks

Before you ship a release, it pays to look at the structure rather than the runtime behaviour. NetBeans can render a UML class diagram from your source, and the WebSocket endpoints show up as nodes alongside any @Inject beans they depend on. Seeing the graph helps spot cycles, missing @Produces annotations, and accidental coupling between endpoint classes.

The class diagram generation feature lives under the Source menu and exports a printable diagram you can attach to a release note. Australian teams that hand documentation to operations often drop the PDF into Confluence alongside deployment runbooks, which keeps the on-call engineer in Adelaide oriented when an incident hits at 3am AEST.

Habits Worth Keeping Between Releases

A short checklist runs through what to verify before a WebSocket service goes out the door.

  • Confirm breakpoints are removed or disabled and that no @Debug system properties remain in production profiles.
  • Validate that onClose and onError paths actually clean up session references, especially when the client network drops mid-handshake.
  • Set a reasonable idle timeout in the server configuration so dead sessions do not accumulate overnight.
  • Add a single debug-only echo endpoint that returns the current server timestamp; it saves a lot of guesswork when triaging an issue from a Darwin data centre.
  • Re-run the profiler after any significant endpoint change to confirm retained heap stays flat under load.
  • Keep the standalone HTML client and a short wscat snippet in the project README so anyone in the team can reproduce traffic in under a minute.

These habits are unglamorous, but they are the difference between a release that survives a long weekend and one that pages the on-call rotation at 5am.