Core Java

Configuring Jersey Connection and Read Timeouts

This article will guide you through setting connection timeout and read timeout in Jersey using ClientConfig. When developing RESTful applications using Jersey, it is crucial to manage timeouts effectively. Timeouts help prevent requests from hanging indefinitely when the server is slow to respond or unreachable.

1. Understanding Connection Timeout and Read Timeout

Properly setting these timeouts (connection and read) ensures our application does not wait indefinitely for responses, improving resilience and performance.

1.1 Connection Timeout

The connection timeout defines the maximum duration, in milliseconds, that the client will wait while attempting to establish a connection with the server. If the connection is not successfully established within this specified time limit, the request is aborted, and a timeout exception is thrown. Properly configuring this timeout helps prevent indefinite waiting periods and ensures that the application can handle network delays or unresponsive servers more effectively.

1.2 Read Timeout

The read timeout specifies the maximum duration, in milliseconds, that the client will wait for a response after sending a request to the server. If the server fails to respond within the defined time limit, the request is terminated, and a timeout exception is triggered. Configuring an appropriate read timeout helps prevent prolonged waiting periods, improves application responsiveness, and ensures efficient handling of slow or unresponsive servers

2. Dependencies Required

Before implementing the solution, it is essential to include the necessary dependencies in the pom.xml file:

<dependencies>
    <!-- Jersey Core Client -->
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-client</artifactId>
        <version>3.1.9</version>
    </dependency>

    <!-- Jersey Injection for Dependency Management -->
    <dependency>
        <groupId>org.glassfish.jersey.inject</groupId>
        <artifactId>jersey-hk2</artifactId>
        <version>3.1.9</version>
    </dependency>
</dependencies>

For Gradle users, add:

dependencies {
    implementation 'org.glassfish.jersey.core:jersey-client:3.1.9'
    implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.9'
}

3. Configuring Connection Timeout and Read Timeout

To set timeouts in Jersey, we configure ClientConfig using ClientProperties. Below is a complete example demonstrating how to configure connection timeout and read timeout in Jersey.

public class JerseyTimeoutExample {
    
    private static final Logger logger = Logger.getLogger(JerseyTimeoutExample.class.getName());
    
    public static void main(String[] args) {
             
        // Create a ClientConfig instance
        ClientConfig clientConfig = new ClientConfig();
        clientConfig.property(ClientProperties.CONNECT_TIMEOUT, 5000);
        clientConfig.property(ClientProperties.READ_TIMEOUT, 10000);

        // Create a client using the configured settings
        Client client = ClientBuilder.newClient(clientConfig);

        // Define the target URL
        WebTarget target = client.target("https://jsonplaceholder.typicode.com/posts/1");
        try (Response response = target.request().get()) {

            logger.log(Level.INFO, "Response Code: {0}", response.getStatus());
            logger.log(Level.INFO, "Response Body: {0}", response.readEntity(String.class));
        } catch (Exception e) {
            logger.log(Level.WARNING, "Request failed: {0}", e.getMessage());
            
        } finally {
            client.close();
        }
    }
}

3.1 Code Walkthrough and Analysis

Step 1: Configuring Client Timeout Settings

A ClientConfig object is created to apply custom configurations for managing timeouts. The ClientProperties.CONNECT_TIMEOUT is set to 5000 milliseconds (5 seconds), ensuring that the client does not wait indefinitely while attempting to establish a connection. Additionally, the ClientProperties.READ_TIMEOUT is set to 10000 milliseconds (10 seconds), defining the maximum duration the client will wait for a response from the server before triggering a timeout exception. These configurations help improve network efficiency and prevent long delays in request handling.

Step 2: Creating a Jersey Client

A Jersey Client instance is created using ClientBuilder.newClient(clientConfig), which applies the timeout settings.

Step 3: Defining the Target URL

The WebTarget object points to the API URL (https://jsonplaceholder.typicode.com/posts/1).

Step 4: Sending the Request and Handling the Response

A GET request is executed using target.request().get(), and if the request is successful, the response status and body are printed. In case of a timeout or failure, an error message is displayed to notify the user. To ensure proper resource management, both the response and client are closed within the finally block, preventing potential memory leaks and ensuring efficient cleanup.

3.2 Expected Output

If the request completes within the specified time, you will see:

Jan 29, 2025 4:32:09 P.M. com.jcg.examples.JerseyTimeoutExample main
INFO: Response Code: 200
Jan 29, 2025 4:32:09 P.M. com.jcg.examples.JerseyTimeoutExample main
INFO: Response Body: {
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

4. Using the client.property() Method

The client.property() method allows setting timeouts dynamically on a Jersey client instance. This approach provides flexibility by applying timeout configurations at runtime rather than during object creation.

public class JerseyClientPropertyExample {

    private static final Logger logger = Logger.getLogger(JerseyClientPropertyExample.class.getName());

    public static void main(String[] args) {
        // Create a Jersey client
        Client client = ClientBuilder.newClient();

        // Set connection and read timeouts using client.property()
        client.property(ClientProperties.CONNECT_TIMEOUT, 5000); // 5 seconds
        client.property(ClientProperties.READ_TIMEOUT, 10000); // 10 seconds

        WebTarget target = client.target("https://jsonplaceholder.typicode.com/posts/5");

        Response response = null;
        try {
            response = target.request().get();
            logger.log(Level.INFO, "Response Code: {0}", response.getStatus());
            logger.log(Level.INFO, "Response Body: {0}", response.readEntity(String.class));
        } catch (Exception e) {
            logger.log(Level.WARNING, "Request failed: {0}", e.getMessage());
        } finally {
            if (response != null) {
                response.close();
            }
            client.close();
        }
    }
}

In this example, a Jersey client is created using ClientBuilder.newClient(). The client.property() method is then used to set connection and read timeouts dynamically. The connection timeout is set to 5000 milliseconds (5 seconds) and the read timeout is set to 10000 milliseconds (10 seconds). The client then sends a GET request to a valid API endpoint, and if successful, the response status and body are printed.

5. Using the ClientBuilder API

Instead of setting properties dynamically, timeouts can be configured during client creation using the ClientBuilder API. This method is preferred for defining timeout settings at the time of client instantiation.

public class JerseyClientBuilderMain {
    
private static final int TIMEOUT = 5000; // 5 seconds
private static final Logger logger = Logger.getLogger(JerseyClientBuilderMain.class.getName());

    public static void main(String[] args) {
        JerseyClientBuilderMain clientExample = new JerseyClientBuilderMain();
        String response = clientExample.viaClientBuilder();
        logger.log(Level.INFO, "Response: {0}", response);
    }

    public String viaClientBuilder() {
        // Create a ClientBuilder instance and configure timeouts
        ClientBuilder builder = ClientBuilder.newBuilder()
                .connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
                .readTimeout(TIMEOUT, TimeUnit.MILLISECONDS);

        // Build the client and execute the request
        return get(builder.build());
    }

    private String get(Client client) {
        WebTarget target = client.target("https://jsonplaceholder.typicode.com/posts/6");
        Response response = null;
        try {
            response = target.request().get();
            return response.readEntity(String.class);
        } catch (Exception e) {
            return "Request failed: " + e.getMessage();
        } finally {
            if (response != null) {
                response.close();
            }
            client.close();
        }
    }
}

This implementation defines a main class called JerseyClientBuilderMain, where a Jersey client is created using ClientBuilder.newBuilder(). Timeouts are set using connectTimeout() and readTimeout(), both configured to 5000 milliseconds (5 seconds). The viaClientBuilder() method initializes the client with these timeout settings and then calls the get() method to send an HTTP request.

6. Implementing a Retry Mechanism

Timeouts and temporary network failures are common, so implementing a retry mechanism improves reliability. The example below retries failed requests up to 3 times before giving up.

public class JerseyRetryExample {

    private static final Logger logger = Logger.getLogger(JerseyRetryExample.class.getName());
    private static final int MAX_RETRIES = 3; // Maximum retry attempts
    private static final int CONNECT_TIMEOUT = 5000; // 5 seconds
    private static final int READ_TIMEOUT = 10000; // 10 seconds

    public static void main(String[] args) {

        ClientConfig clientConfig = new ClientConfig();
        clientConfig.property(ClientProperties.CONNECT_TIMEOUT, CONNECT_TIMEOUT);
        clientConfig.property(ClientProperties.READ_TIMEOUT, READ_TIMEOUT);

        Client client = ClientBuilder.newClient(clientConfig);
        WebTarget target = client.target("https://jsonplaceholder.typicode.com/posts/1");

        Response response = null;
        int attempt = 0;

        while (attempt < MAX_RETRIES) {
            try {
                attempt++;
                logger.log(Level.INFO, "Attempt {0}", attempt + " to send request...");

                response = target.request().get();

                // If request is successful, print response and break retry loop
                if (response.getStatus() == 200) {
                    logger.log(Level.INFO, "Response Code: {0}", response.getStatus());
                    logger.log(Level.INFO, "Response Body: {0}", response.readEntity(String.class));
                    break;
                } else {
                    logger.log(Level.WARNING, "Request failed with status: {0}", response.getStatus());
                }
            } catch (Exception e) {
                logger.log(Level.WARNING, "Attempt {0}", attempt + " " + e.getMessage());
            }

            // Small delay before retrying
            try {
                Thread.sleep(2000); // Wait 2 seconds before retrying
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        // Close resources
        if (response != null) {
            response.close();
        }
        client.close();
    }
}

The retry logic is implemented using a while loop, allowing the request to be attempted up to MAX_RETRIES times. If a request fails, an error message is printed, and the system waits for a short delay before retrying. When a successful response (HTTP 200) is received, the loop terminates, preventing unnecessary retries. In case of failures, such as timeouts or server errors, exceptions are caught, and another attempt is initiated. To prevent overwhelming the server, a Thread.sleep(2000); introduces a 2-second delay between retries, ensuring a controlled retry mechanism.

Expected Output

If the request succeeds on the first attempt:

INFO: Attempt 1 to send request...
Jan 29, 2025 9:04:04 P.M. 
Jan 29, 2025 9:04:08 P.M. com.jcg.examples.JerseyRetryExample main
INFO: Response Code: 200
Jan 29, 2025 9:04:08 P.M. com.jcg.examples.JerseyRetryExample main
INFO: Response Body: {
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

To test the retry mechanism using an unreachable API endpoint, we can use a valid domain with a non-existent path, such as https://jsonplaceholder.typicode.com/nonexistent. This endpoint will return an HTTP 404 (Not Found) error, simulating a failed request that triggers the retry logic.

Since the /nonexistent path does not exist, the server will respond with HTTP 404 (Not Found). The client will retry the request three times before stopping:

Jersey connection timeout, read timeout, retry mechanism, and response output.

7. Conclusion

This article explored multiple ways to configure Jersey connection and read timeouts to prevent excessive waiting periods when interacting with external APIs. We demonstrated how to set timeouts dynamically using the client.property() method and configure them during client creation using the ClientBuilder API. We also implemented a retry mechanism to automatically reattempt failed requests, ensuring improved reliability in network communication.

By configuring connection and read timeouts in Jersey, we can prevent long waits and improve application responsiveness. This ensures that our client handles network latency efficiently, leading to better performance and user experience.

8. Download the Source Code

This article covered configuring connection and read timeouts in Jersey.

Download
You can download the full source code of this example here: jersey connection read timeouts

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button