Speakeasy Logo
Skip to Content

Java SDK async migration guide

Prerequisites

To follow this guide, you need:

  • The Speakeasy CLI version 1.606.9 or higher
  • Java 11 or higher
  • An existing Java SDK generated by Speakeasy
  • Access to your .speakeasy/gen.yaml configuration file

Step 1: Enable async support in configuration

Enable the following flag in your .speakeasy/gen.yaml:

java: asyncMode: enabled

Then regenerate your SDK with compilation skipped:

speakeasy run --skip-compile

This generates the async hooks and adapters necessary for Step 2. We skip compilation because the generator doesn’t touch the SDKHooks file, and async SDK initialization requires an additional method that we need to add manually to that file.

Step 2: Add async hook registration method

The async feature introduces new hook interfaces that work with CompletableFuture. You’ll need to add a new initialization method to your SDKHooks class.

Locate your SDKHooks file

Open ./src/main/java/<package>/hooks/SDKHooks.java in your Java SDK project.

Add the async initialize method

Add this method to your SDKHooks class (org.openapis.openapi is a placeholder for your package):

// replace org.openapis.openapi with your actual package public static void initialize(org.openapis.openapi.utils.AsyncHooks hooks) { // register async hooks here }

Your SDKHooks class should now look like this:

package org.openapis.openapi.hooks; public final class SDKHooks { public static void initialize(org.openapis.openapi.utils.Hooks hooks) { // register synchronous hooks here } public static void initialize(org.openapis.openapi.utils.AsyncHooks hooks) { // register async hooks here } }

Step 3: Migrate existing hooks (if applicable)

If you don’t have existing hooks, skip to Step 4. If you do have hooks, choose one of the following migration options:

Option A: Quick migration with HookAdapters

Use HookAdapters to automatically convert your existing synchronous hooks to async:

package org.openapis.openapi.hooks; import org.openapis.openapi.utils.Hook; import org.openapis.openapi.utils.HookAdapters; import java.util.concurrent.CompletableFuture; public final class SDKHooks { // Your existing hook static Hook.BeforeRequest AUTH_AND_TRACING_HOOK = new AuthAndTracingBeforeRequest( myTokenProvider ); public static void initialize(org.openapis.openapi.utils.Hooks hooks) { // Keep existing synchronous hook registration hooks.registerBeforeRequest(AUTH_AND_TRACING_HOOK); } public static void initialize(org.openapis.openapi.utils.AsyncHooks hooks) { // Convert synchronous hook to async using adapter hooks.registerBeforeRequest(HookAdapters.toAsync(AUTH_AND_TRACING_HOOK)); } }

Use this option for:

  • A quick migration with minimal code changes
  • CPU-bound hooks
  • Low-to-moderate throughput applications

Option B: Full async implementation

Reimplement your hooks using async APIs and non-blocking operations:

package org.openapis.openapi.hooks; import org.openapis.openapi.utils.AsyncHook; import java.util.concurrent.CompletableFuture; import java.util.logging.Logger; public final class SDKHooks { private static final Logger logger = Logger.getLogger(SDKHooks.class.getName()); // Async version of your hook with error handling static AsyncHook.BeforeRequest ASYNC_AUTH_HOOK = (context, request) -> { return authService.getTokenAsync() // Use async API .thenApply(token -> request.toBuilder() .addHeader("Authorization", "Bearer " + token) .build()) .exceptionally(throwable -> { // Handle auth failures gracefully logger.warning("Auth token retrieval failed: " + throwable.getMessage()); return request; // Return original request }); }; public static void initialize(org.openapis.openapi.utils.Hooks hooks) { // Keep existing synchronous hooks if needed } public static void initialize(org.openapis.openapi.utils.AsyncHooks hooks) { // Register native async hooks hooks.registerBeforeRequest(ASYNC_AUTH_HOOK); } }

Use this option for:

  • High-throughput applications
  • I/O-bound operations (such as HTTP calls and database queries)
  • Meeting maximum performance requirements

Step 4: Compile the SDK

After making the configuration and code changes, run the following command again to compile and verify that the changes are correct:

speakeasy run

Then test your application to ensure the async functionality works as expected.

Available async hook interfaces

The async support provides three hook interfaces that mirror the synchronous ones:

AsyncHook.BeforeRequest

import java.util.concurrent.CompletableFuture; CompletableFuture<HttpRequest> beforeRequest( Hook.BeforeRequestContext context, HttpRequest request );

AsyncHook.AfterSuccess

import java.util.concurrent.CompletableFuture; CompletableFuture<HttpResponse<Blob>> afterSuccess( Hook.AfterSuccessContext context, HttpResponse<Blob> response );

AsyncHook.AfterError

import java.util.concurrent.CompletableFuture; CompletableFuture<HttpResponse<Blob>> afterError( Hook.AfterErrorContext context, HttpResponse<Blob> response, Throwable error );

Best practices

  • Choose the right migration option based on your application’s throughput requirements and hook complexity.
  • Test thoroughly after migration to ensure the async behavior meets your expectations.
  • Monitor performance to validate that async support provides the expected benefits.
  • Use appropriate async APIs in Option B implementations (such as async HTTP clients and reactive database drivers).

Troubleshooting

If you encounter these issues, refer to the troubleshooting instructions below:

Missing async initialize method

Ensure you’ve added the initialize(AsyncHooks hooks) method to your SDKHooks class.

Performance issues with HookAdapters

Consider migrating to Option B for I/O-bound hooks in high-throughput scenarios.

Next steps

After a successful migration, your Java SDK will support both synchronous and asynchronous operations, allowing you to leverage non-blocking I/O for improved performance in concurrent applications.

Last updated on