Skip to content

Commit fe685e2

Browse files
authored
[ACL-263] Caching improvements (#337)
1 parent ecc1bb9 commit fe685e2

23 files changed

+604
-212
lines changed

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Please select multiple options if required.
1616
# Checklist:
1717

1818
- [ ] I have updated the `gradle.properties` file with the new version
19-
- [ ] I have updated the `CHANGELOG.md` file with the details of the new versio
19+
- [ ] I have updated the `CHANGELOG.md` file with the details of the new version
2020
- [ ] My code follows the style guidelines of this project
2121
- [ ] I have performed a self-review of my own code
2222
- [ ] I have commented my code where necessary

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/).
77

8-
## [Unreleased]
8+
## [17.0.0] - 2025-01-15
9+
### Changed
10+
* ⚠️ Breaking: removed deprecated HPP link builder
11+
* ⚠️ Breaking: Aligned custom cache implementation to other officially supported client libraries
912

1013
## [16.3.1] - 2025-01-10
1114
### Changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Main properties
22
group=com.truelayer
33
archivesBaseName=truelayer-java
4-
version=16.3.1
4+
version=17.0.0
55

66
# Artifacts properties
77
sonatype_repository_url=https://s01.oss.sonatype.org/service/local/

src/main/java/com/truelayer/java/ITrueLayerClient.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.truelayer.java.auth.IAuthenticationHandler;
44
import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnRequest;
55
import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnResponse;
6-
import com.truelayer.java.hpp.IHostedPaymentPageLinkBuilder;
76
import com.truelayer.java.http.entities.ApiResponse;
87
import com.truelayer.java.mandates.IMandatesHandler;
98
import com.truelayer.java.merchantaccounts.IMerchantAccountsHandler;
@@ -60,14 +59,6 @@ public interface ITrueLayerClient {
6059
*/
6160
ISignupPlusHandler signupPlus();
6261

63-
/**
64-
* Entrypoint for Hosted Payment Page related services.
65-
* @return a utility to build a Hosted Payment Page URL.
66-
* @deprecated use {@link #hppLinkBuilder()} instead.
67-
*/
68-
@Deprecated
69-
IHostedPaymentPageLinkBuilder hpp();
70-
7162
/**
7263
* Entrypoint for Hosted Payment Page related services.
7364
* @return a utility to build a Hosted Payment Page URL.

src/main/java/com/truelayer/java/TrueLayerClient.java

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import com.truelayer.java.commonapi.ICommonHandler;
55
import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnRequest;
66
import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnResponse;
7-
import com.truelayer.java.hpp.IHostedPaymentPageLinkBuilder;
87
import com.truelayer.java.http.entities.ApiResponse;
98
import com.truelayer.java.mandates.IMandatesHandler;
109
import com.truelayer.java.merchantaccounts.IMerchantAccountsHandler;
@@ -33,23 +32,14 @@ public class TrueLayerClient implements ITrueLayerClient {
3332
private IPayoutsHandler payoutsHandler;
3433
private ISignupPlusHandler signupPlusHandler;
3534
private ICommonHandler commonHandler;
36-
37-
/**
38-
* @deprecated - use {@link #hostedPaymentPageLinkBuilder} instead.
39-
*/
40-
@Deprecated
41-
private IHostedPaymentPageLinkBuilder hppLinkBuilder;
42-
4335
private HostedPaymentPageLinkBuilder hostedPaymentPageLinkBuilder;
4436

4537
public TrueLayerClient(
4638
IAuthenticationHandler authenticationHandler,
47-
IHostedPaymentPageLinkBuilder hostedPaymentPageLinkBuilder,
4839
ICommonHandler commonHandler,
4940
ISignupPlusHandler signupPlusHandler,
5041
HostedPaymentPageLinkBuilder hppLinkBuilder) {
5142
this.authenticationHandler = authenticationHandler;
52-
this.hppLinkBuilder = hostedPaymentPageLinkBuilder;
5343
this.commonHandler = commonHandler;
5444
this.signupPlusHandler = signupPlusHandler;
5545
this.hostedPaymentPageLinkBuilder = hppLinkBuilder;
@@ -128,15 +118,6 @@ public ISignupPlusHandler signupPlus() {
128118
return signupPlusHandler;
129119
}
130120

131-
/**
132-
* {@inheritDoc}
133-
*/
134-
@Override
135-
@Deprecated
136-
public IHostedPaymentPageLinkBuilder hpp() {
137-
return this.hppLinkBuilder;
138-
}
139-
140121
@Override
141122
public HostedPaymentPageLinkBuilder hppLinkBuilder() {
142123
return this.hostedPaymentPageLinkBuilder;

src/main/java/com/truelayer/java/TrueLayerClientBuilder.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@
99
import com.truelayer.java.commonapi.ICommonApi;
1010
import com.truelayer.java.commonapi.ICommonHandler;
1111
import com.truelayer.java.entities.RequestScopes;
12-
import com.truelayer.java.hpp.IHostedPaymentPageLinkBuilder;
1312
import com.truelayer.java.http.OkHttpClientFactory;
1413
import com.truelayer.java.http.RetrofitFactory;
1514
import com.truelayer.java.http.auth.cache.ICredentialsCache;
16-
import com.truelayer.java.http.auth.cache.SimpleCredentialsCache;
15+
import com.truelayer.java.http.auth.cache.InMemoryCredentialsCache;
1716
import com.truelayer.java.http.interceptors.logging.DefaultLogConsumer;
1817
import com.truelayer.java.mandates.IMandatesApi;
1918
import com.truelayer.java.mandates.IMandatesHandler;
@@ -72,6 +71,9 @@ public class TrueLayerClientBuilder {
7271

7372
private Consumer<String> logMessageConsumer;
7473

74+
/**
75+
* Holder for the cache implementation. Null if caching is disabled
76+
*/
7577
private ICredentialsCache credentialsCache;
7678

7779
private ProxyConfiguration proxyConfiguration;
@@ -182,12 +184,13 @@ public TrueLayerClientBuilder withHttpLogs(Consumer<String> logConsumer) {
182184
* @return the instance of the client builder used
183185
*/
184186
public TrueLayerClientBuilder withCredentialsCaching() {
185-
this.credentialsCache = new SimpleCredentialsCache(Clock.systemUTC());
187+
this.credentialsCache = new InMemoryCredentialsCache(Clock.systemUTC());
186188
return this;
187189
}
188190

189191
/**
190192
* Utility to enable a custom cache for Oauth credentials.
193+
* @param credentialsCache the custom cache implementation
191194
* @return the instance of the client builder used
192195
*/
193196
public TrueLayerClientBuilder withCredentialsCaching(ICredentialsCache credentialsCache) {
@@ -228,10 +231,6 @@ public TrueLayerClient build() {
228231
.httpClient(RetrofitFactory.build(authServerApiHttpClient, environment.getAuthApiUri()))
229232
.build();
230233

231-
IHostedPaymentPageLinkBuilder hppLinkBuilder = com.truelayer.java.hpp.HostedPaymentPageLinkBuilder.New()
232-
.uri(environment.getHppUri())
233-
.build();
234-
235234
// We're reusing a client with only User agent and Idempotency key interceptors and give it our base payment
236235
// endpoint
237236
ICommonApi commonApi = RetrofitFactory.build(authServerApiHttpClient, environment.getPaymentsApiUri())
@@ -241,7 +240,7 @@ public TrueLayerClient build() {
241240
// We're building a client which has the authentication handler and the options to cache the token.
242241
// this one represents the baseline for the client used for Signup+ and Payments
243242
OkHttpClient authenticatedApiClient = httpClientFactory.buildAuthenticatedApiClient(
244-
authServerApiHttpClient, authenticationHandler, credentialsCache);
243+
clientCredentials.clientId, authServerApiHttpClient, authenticationHandler, credentialsCache);
245244
ISignupPlusApi signupPlusApi = RetrofitFactory.build(authenticatedApiClient, environment.getPaymentsApiUri())
246245
.create(ISignupPlusApi.class);
247246
SignupPlusHandler.SignupPlusHandlerBuilder signupPlusHandlerBuilder =
@@ -256,7 +255,6 @@ public TrueLayerClient build() {
256255
if (isEmpty(signingOptions)) {
257256
return new TrueLayerClient(
258257
authenticationHandler,
259-
hppLinkBuilder,
260258
commonHandler,
261259
signupPlusHandler,
262260
new HostedPaymentPageLinkBuilder(environment));
@@ -324,7 +322,6 @@ public TrueLayerClient build() {
324322
payoutsHandler,
325323
signupPlusHandler,
326324
commonHandler,
327-
hppLinkBuilder,
328325
new HostedPaymentPageLinkBuilder(environment));
329326
}
330327

src/main/java/com/truelayer/java/http/OkHttpClientFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ public OkHttpClient buildAuthServerApiClient(OkHttpClient baseHttpClient, Client
108108
}
109109

110110
public OkHttpClient buildAuthenticatedApiClient(
111+
String clientId,
111112
OkHttpClient authServerApiClient,
112113
IAuthenticationHandler authenticationHandler,
113114
ICredentialsCache credentialsCache) {
@@ -118,7 +119,7 @@ public OkHttpClient buildAuthenticatedApiClient(
118119
OkHttpClient.Builder authenticatedApiClientBuilder = authServerApiClient.newBuilder();
119120

120121
AccessTokenManager.AccessTokenManagerBuilder accessTokenManagerBuilder =
121-
AccessTokenManager.builder().authenticationHandler(authenticationHandler);
122+
AccessTokenManager.builder().clientId(clientId).authenticationHandler(authenticationHandler);
122123

123124
// setup credentials caching if required
124125
if (isNotEmpty(credentialsCache)) {

src/main/java/com/truelayer/java/http/auth/AccessTokenManager.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.truelayer.java.auth.IAuthenticationHandler;
55
import com.truelayer.java.auth.entities.AccessToken;
66
import com.truelayer.java.entities.RequestScopes;
7+
import com.truelayer.java.http.auth.cache.CredentialsCacheHelper;
78
import com.truelayer.java.http.auth.cache.ICredentialsCache;
89
import com.truelayer.java.http.entities.ApiResponse;
910
import java.util.Optional;
@@ -13,6 +14,8 @@
1314
@Builder
1415
public class AccessTokenManager implements IAccessTokenManager {
1516

17+
private final String clientId;
18+
1619
private final IAuthenticationHandler authenticationHandler;
1720

1821
private final ICredentialsCache credentialsCache;
@@ -23,10 +26,12 @@ private Optional<ICredentialsCache> getCredentialsCache() {
2326

2427
@Override
2528
public AccessToken getToken(RequestScopes scopes) {
29+
String cacheKey = CredentialsCacheHelper.buildKey(clientId, scopes);
30+
2631
if (getCredentialsCache().isPresent()) {
27-
return getCredentialsCache().get().getToken(scopes).orElseGet(() -> {
32+
return getCredentialsCache().get().getToken(cacheKey).orElseGet(() -> {
2833
AccessToken token = tryGetToken(scopes);
29-
credentialsCache.storeToken(scopes, token);
34+
credentialsCache.storeToken(cacheKey, token);
3035
return token;
3136
});
3237
}
@@ -37,7 +42,8 @@ public AccessToken getToken(RequestScopes scopes) {
3742
@Override
3843
@Synchronized
3944
public void invalidateToken(RequestScopes scopes) {
40-
getCredentialsCache().ifPresent(iCredentialsCache -> iCredentialsCache.clearToken(scopes));
45+
String cacheKey = CredentialsCacheHelper.buildKey(clientId, scopes);
46+
getCredentialsCache().ifPresent(iCredentialsCache -> iCredentialsCache.clearToken(cacheKey));
4147
}
4248

4349
private AccessToken tryGetToken(RequestScopes scopes) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.truelayer.java.http.auth.cache;
2+
3+
import static java.nio.charset.StandardCharsets.UTF_8;
4+
import static org.apache.commons.lang3.ObjectUtils.isEmpty;
5+
6+
import com.truelayer.java.TrueLayerException;
7+
import com.truelayer.java.entities.RequestScopes;
8+
import java.security.MessageDigest;
9+
import java.security.NoSuchAlgorithmException;
10+
import java.text.MessageFormat;
11+
import java.util.ArrayList;
12+
import java.util.Collections;
13+
import java.util.List;
14+
15+
public class CredentialsCacheHelper {
16+
private static final String CACHE_KEY_PREFIX = "tl-auth-token";
17+
private static final String SCOPES_DELIMITER = ",";
18+
19+
public static String buildKey(String clientId, RequestScopes requestScopes) {
20+
if (isEmpty(clientId) || isEmpty(requestScopes) || isEmpty(requestScopes.getScopes())) {
21+
throw new TrueLayerException("Invalid client id or request scopes provided");
22+
}
23+
24+
List<String> scopes = new ArrayList<>(requestScopes.getScopes());
25+
26+
// Use natural ordering to make ordering not significant
27+
Collections.sort(scopes);
28+
29+
byte[] md5InBytes = digest(String.join(SCOPES_DELIMITER, scopes).getBytes(UTF_8));
30+
return MessageFormat.format("{0}:{1}:{2}", CACHE_KEY_PREFIX, clientId, bytesToHex(md5InBytes));
31+
}
32+
33+
private static byte[] digest(byte[] input) {
34+
MessageDigest md;
35+
try {
36+
md = MessageDigest.getInstance("MD5");
37+
} catch (NoSuchAlgorithmException e) {
38+
throw new TrueLayerException("Hashing algorithm is not available", e);
39+
}
40+
return md.digest(input);
41+
}
42+
43+
private static String bytesToHex(byte[] bytes) {
44+
StringBuilder sb = new StringBuilder();
45+
for (byte b : bytes) {
46+
sb.append(String.format("%02x", b));
47+
}
48+
return sb.toString();
49+
}
50+
}
Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,13 @@
11
package com.truelayer.java.http.auth.cache;
22

33
import com.truelayer.java.auth.entities.AccessToken;
4-
import com.truelayer.java.entities.RequestScopes;
54
import java.util.Optional;
65

76
public interface ICredentialsCache {
87

9-
/**
10-
* Gets the cached access token for the given request scopes.
11-
* @param scopes the requested scopes
12-
* @return an optional access token. If the token is expired an empty optional is returned
13-
*/
14-
Optional<AccessToken> getToken(RequestScopes scopes);
8+
Optional<AccessToken> getToken(String key);
159

16-
/**
17-
* Stores an access token in cache for the given request scopes.
18-
* @param token the new token to store
19-
* @param scopes the requested scopes
20-
*/
21-
void storeToken(RequestScopes scopes, AccessToken token);
10+
void storeToken(String key, AccessToken token);
2211

23-
/**
24-
* Remove the entry in the cache for the given request scopes.
25-
* @param scopes the requested scopes
26-
*/
27-
void clearToken(RequestScopes scopes);
12+
void clearToken(String key);
2813
}

0 commit comments

Comments
 (0)