Skip to content

Commit a31a897

Browse files
authored
Merge pull request #227 from contentstack/fix/DX-5646-timeout
Fix: Enhance timeout configuration
2 parents 873ed00 + 09e64c0 commit a31a897

File tree

4 files changed

+123
-9
lines changed

4 files changed

+123
-9
lines changed

changelog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## v1.11.1
4+
5+
### Apr 06, 2026
6+
7+
- Fix: `setTimeout` now applies to OkHttp connect, read, and write timeouts (previously only connect). Optional `setConnectTimeout`, `setReadTimeout`, and `setWriteTimeout` override individual phases.
8+
- Build: Maven Surefire updated with `surefire-junit-platform` so JUnit 5 tests run when enabled.
9+
310
## v1.11.0
411

512
### Feb 09, 2026

pom.xml

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<artifactId>cms</artifactId>
88
<packaging>jar</packaging>
99
<name>contentstack-management-java</name>
10-
<version>1.11.0</version>
10+
<version>1.11.1</version>
1111
<description>Contentstack Java Management SDK for Content Management API, Contentstack is a headless CMS with an
1212
API-first approach
1313
</description>
@@ -89,18 +89,19 @@
8989
<maven-javadoc-plugin.version>3.0.0</maven-javadoc-plugin.version>
9090
<dotenv-source.version>5.2.2</dotenv-source.version>
9191
<rxjava-source.version>3.1.12</rxjava-source.version>
92-
<retrofit-source.version>2.12.0</retrofit-source.version>
93-
<converter-gson-version>2.12.0</converter-gson-version>
94-
<okhttp.version>5.1.0</okhttp.version>
92+
<retrofit-source.version>3.0.0</retrofit-source.version>
93+
<converter-gson-version>3.0.0</converter-gson-version>
94+
<okhttp.version>5.3.2</okhttp.version>
9595
<jococo-plugin.version>0.8.13</jococo-plugin.version>
96-
<lombok-source.version>1.18.38</lombok-source.version>
96+
<lombok-source.version>1.18.42</lombok-source.version>
9797
<junit-jupiter.version>5.11.4</junit-jupiter.version>
9898
<junit-jupiter-engine.version>5.10.1</junit-jupiter-engine.version>
9999
<gson.version>2.13.2</gson.version>
100100
<maven-site-plugin.version>3.3</maven-site-plugin.version>
101101
<maven-gpg-plugin.version>1.5</maven-gpg-plugin.version>
102102
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
103103
<maven-release-plugin.version>2.5.3</maven-release-plugin.version>
104+
<maven-surefire-plugin.version>3.5.2</maven-surefire-plugin.version>
104105
</properties>
105106

106107
<dependencies>
@@ -242,7 +243,14 @@
242243
<plugin>
243244
<groupId>org.apache.maven.plugins</groupId>
244245
<artifactId>maven-surefire-plugin</artifactId>
245-
<version>3.0.0-M5</version>
246+
<version>${maven-surefire-plugin.version}</version>
247+
<dependencies>
248+
<dependency>
249+
<groupId>org.apache.maven.surefire</groupId>
250+
<artifactId>surefire-junit-platform</artifactId>
251+
<version>${maven-surefire-plugin.version}</version>
252+
</dependency>
253+
</dependencies>
246254
<configuration>
247255
<includes>
248256
<!-- Run all test files following Maven naming conventions -->
@@ -252,8 +260,9 @@
252260
<include>**/*TestCase.java</include>
253261
<include>**/*TestSuite.java</include>
254262
</includes>
255-
<reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory>
256-
<skipTests>true</skipTests>
263+
<reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory>
264+
<!-- Skip during default lifecycle (e.g. publish); run tests locally with: mvn test -DskipTests=false -->
265+
<skipTests>true</skipTests>
257266
<testFailureIgnore>true</testFailureIgnore>
258267
</configuration>
259268
</plugin>

src/main/java/com/contentstack/cms/Contentstack.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,9 @@ public static class Builder {
599599
private String port = Util.PORT; // Default PORT for Contentstack API
600600
private String version = Util.VERSION; // Default Version for Contentstack API
601601
private int timeout = Util.TIMEOUT; // Default timeout 30 seconds
602+
private Integer connectTimeoutSeconds;
603+
private Integer readTimeoutSeconds;
604+
private Integer writeTimeoutSeconds;
602605
private Boolean retry = Util.RETRY_ON_FAILURE;// Default base url for contentstack
603606
private RetryConfig retryConfig = RetryConfig.defaultConfig();
604607
/**
@@ -687,10 +690,37 @@ public Builder setVersion(@NotNull String version) {
687690
* @return Client timeout
688691
*/
689692
public Builder setTimeout(int timeout) {
693+
validateTimeoutSeconds(timeout, "timeout");
690694
this.timeout = timeout;
691695
return this;
692696
}
693697

698+
public Builder setReadTimeout(int readTimeoutSeconds) {
699+
validateTimeoutSeconds(readTimeoutSeconds, "readTimeout");
700+
this.readTimeoutSeconds = readTimeoutSeconds;
701+
return this;
702+
}
703+
704+
public Builder setWriteTimeout(int writeTimeoutSeconds) {
705+
validateTimeoutSeconds(writeTimeoutSeconds, "writeTimeout");
706+
this.writeTimeoutSeconds = writeTimeoutSeconds;
707+
return this;
708+
}
709+
710+
public Builder setConnectTimeout(int connectTimeoutSeconds) {
711+
validateTimeoutSeconds(connectTimeoutSeconds, "connectTimeout");
712+
this.connectTimeoutSeconds = connectTimeoutSeconds;
713+
return this;
714+
}
715+
716+
private static void validateTimeoutSeconds(int seconds, String name) {
717+
if (seconds <= 0) {
718+
throw new IllegalArgumentException(name + " must be positive.");
719+
}
720+
}
721+
722+
723+
694724
/**
695725
* Create a new connection pool with tuning parameters appropriate for a
696726
* single-user application. The tuning parameters in this pool are
@@ -840,11 +870,16 @@ private void validateClient(Contentstack contentstack) {
840870
}
841871

842872
private OkHttpClient httpClient(Contentstack contentstack, Boolean retryOnFailure) {
873+
int connectSec = this.connectTimeoutSeconds != null ? this.connectTimeoutSeconds : this.timeout;
874+
int readSec = this.readTimeoutSeconds != null ? this.readTimeoutSeconds : this.timeout;
875+
int writeSec = this.writeTimeoutSeconds != null ? this.writeTimeoutSeconds : this.timeout;
843876
OkHttpClient.Builder builder = new OkHttpClient.Builder()
844877
.connectionPool(this.connectionPool)
845878
.addInterceptor(logger())
846879
.proxy(this.proxy)
847-
.connectTimeout(Duration.ofSeconds(this.timeout))
880+
.connectTimeout(Duration.ofSeconds(connectSec))
881+
.readTimeout(Duration.ofSeconds(readSec))
882+
.writeTimeout(Duration.ofSeconds(writeSec))
848883
.retryOnConnectionFailure(retryOnFailure);
849884

850885
// Add either OAuth or traditional auth interceptor

src/test/java/com/contentstack/cms/ContentstackUnitTest.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.net.Proxy;
2020
import java.util.HashMap;
2121
import java.util.Map;
22+
import java.util.concurrent.TimeUnit;
2223

2324
public class ContentstackUnitTest {
2425

@@ -167,6 +168,68 @@ void setTimeout() {
167168
Assertions.assertEquals(3, contentstack.timeout);
168169
}
169170

171+
@Test
172+
void setTimeoutZeroThrows() {
173+
Assertions.assertThrows(IllegalArgumentException.class,
174+
() -> new Contentstack.Builder().setTimeout(0));
175+
}
176+
177+
@Test
178+
void setTimeoutNegativeThrows() {
179+
Assertions.assertThrows(IllegalArgumentException.class,
180+
() -> new Contentstack.Builder().setTimeout(-1));
181+
}
182+
183+
@Test
184+
void setReadTimeoutZeroThrows() {
185+
Assertions.assertThrows(IllegalArgumentException.class,
186+
() -> new Contentstack.Builder().setReadTimeout(0));
187+
}
188+
189+
@Test
190+
void setWriteTimeoutNegativeThrows() {
191+
Assertions.assertThrows(IllegalArgumentException.class,
192+
() -> new Contentstack.Builder().setWriteTimeout(-5));
193+
}
194+
195+
@Test
196+
void setConnectTimeoutInvalidThrows() {
197+
Assertions.assertThrows(IllegalArgumentException.class,
198+
() -> new Contentstack.Builder().setConnectTimeout(-1));
199+
}
200+
201+
@Test
202+
void okHttpTimeoutsMatchSetTimeout() {
203+
Contentstack client = new Contentstack.Builder().setTimeout(45).build();
204+
OkHttpClient ok = (OkHttpClient) client.instance.callFactory();
205+
Assertions.assertEquals(45_000, ok.connectTimeoutMillis());
206+
Assertions.assertEquals(45_000, ok.readTimeoutMillis());
207+
Assertions.assertEquals(45_000, ok.writeTimeoutMillis());
208+
}
209+
210+
@Test
211+
void okHttpDefaultTimeoutsUseUtilTimeout() {
212+
Contentstack client = new Contentstack.Builder().build();
213+
OkHttpClient ok = (OkHttpClient) client.instance.callFactory();
214+
int expectedMs = (int) TimeUnit.SECONDS.toMillis(30);
215+
Assertions.assertEquals(expectedMs, ok.connectTimeoutMillis());
216+
Assertions.assertEquals(expectedMs, ok.readTimeoutMillis());
217+
Assertions.assertEquals(expectedMs, ok.writeTimeoutMillis());
218+
}
219+
220+
@Test
221+
void okHttpPerLegOverridesFallBackToSetTimeout() {
222+
Contentstack client = new Contentstack.Builder()
223+
.setTimeout(10)
224+
.setReadTimeout(90)
225+
.build();
226+
OkHttpClient ok = (OkHttpClient) client.instance.callFactory();
227+
Assertions.assertEquals(10_000, ok.connectTimeoutMillis());
228+
Assertions.assertEquals(90_000, ok.readTimeoutMillis());
229+
Assertions.assertEquals(10_000, ok.writeTimeoutMillis());
230+
Assertions.assertEquals(10, client.timeout);
231+
}
232+
170233
@Test
171234
void testSetAuthtoken() {
172235
Contentstack contentstack = new Contentstack.Builder()

0 commit comments

Comments
 (0)