Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

G11n java client - Fix for expiration-related issues (issues 604, 664, and 662) #699

Merged
merged 159 commits into from
Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
159 commits
Select commit Hold shift + click to select a range
f4c1214
Loading configuration from the JSON configuration file
jessiejuachon Mar 11, 2020
1a4d95e
Removing code that is not yet ready to be used from the sample applic…
jessiejuachon Mar 11, 2020
5e72b73
fixing failing code scan
jessiejuachon Mar 11, 2020
73d85ca
Adding a cache properties map to hold cache details such as etag and …
jessiejuachon Mar 12, 2020
2a38107
Removing white space
jessiejuachon Mar 12, 2020
c7f5d4f
Fixing failing unit test
jessiejuachon Mar 12, 2020
056891a
Parse headers, response code, response message from an HTTP response …
jessiejuachon Mar 12, 2020
a2ba98b
Storing response headers in Map of cache properties
jessiejuachon Mar 17, 2020
17978bd
Fixing failing unit test due to NullPointerException when doing Map.p…
jessiejuachon Mar 17, 2020
6c69170
Changes after code review.
jessiejuachon Mar 17, 2020
41a8aa1
Removing exception handling for now
jessiejuachon Mar 18, 2020
dd2455d
Deprecating VIPCfg.initialize; cleaning up code
jessiejuachon Mar 19, 2020
e26d372
Storing the http response code in the cache
jessiejuachon Mar 20, 2020
01ab69e
Using cached etag as if-none-match request header value in the reques…
jessiejuachon Mar 25, 2020
750c9d2
Handling 404 response from Singleton service
jessiejuachon Mar 26, 2020
529e5cc
Using HTTP repsonse's Cache-Control max-age and timestamp to dertermi…
jessiejuachon Mar 26, 2020
b40fcf9
Moving constants to URLUtils
jessiejuachon Mar 26, 2020
fa123f9
Fixing failing test
jessiejuachon Mar 27, 2020
30b2054
Fixing failing code scan
jessiejuachon Mar 27, 2020
0c69787
Merge branch 'g11n-java-client' into g11n-java-client
Xiaochao8 Mar 27, 2020
ffc0a74
Cleaning up import in ComponentService,java
jessiejuachon Mar 30, 2020
d8450e4
Cleaning up code - caching/expiry
jessiejuachon Mar 30, 2020
d7f7641
Merge branch 'g11n-java-client' of https://github.com/jessiejuachon/s…
jessiejuachon Mar 30, 2020
0dcd827
Adding tests for new caching workflow; enabling old caching workflow …
jessiejuachon Mar 31, 2020
d06adb3
Fixing a failing test
jessiejuachon Mar 31, 2020
48c8558
Adding mock server response
jessiejuachon Mar 31, 2020
4e45bed
Removing unused file sampleconfig.json
jessiejuachon Mar 31, 2020
6a40dc8
Changes from code review
jessiejuachon Mar 31, 2020
4c3c12d
Value of VIPCfg.cacheExpiredTime is -1 when value is not set in confi…
jessiejuachon Mar 31, 2020
7e0d939
Changing a LinkedHashMap to a HashMap because insertion order does no…
jessiejuachon Apr 2, 2020
baef0d0
Adding CacheItem object to contain map of data and a another map for …
jessiejuachon Apr 3, 2020
02a96e9
Fixing failing unit test
jessiejuachon Apr 3, 2020
78d88f7
Fixing code scan issues
jessiejuachon Apr 3, 2020
655c223
Code cleanup
jessiejuachon Apr 3, 2020
8746915
Lazily instantiating cacheProps; code clean up and comments
jessiejuachon Apr 5, 2020
897a03c
Changes after code review
jessiejuachon Apr 7, 2020
3a4e183
Adding header to CacheItem.java
jessiejuachon Apr 7, 2020
b08feb4
Fixing code san issue. Synchronize on a final field
jessiejuachon Apr 7, 2020
8f57e24
Fixing code scan bug
jessiejuachon Apr 7, 2020
72c8799
Changes after code review
jessiejuachon Apr 7, 2020
92edfc8
Updating CasheService.isContainComponent to return false if cache key…
jessiejuachon Apr 7, 2020
2042554
HttpRequester if-none-match header and other customized headers are a…
jessiejuachon Apr 8, 2020
ae707ff
Code clean up; keep old caching expiration logic
jessiejuachon Apr 8, 2020
82818d5
Fixing code scan bug
jessiejuachon Apr 8, 2020
31d4ff6
Merge branch 'g11n-java-client' into g11n-java-client
jessiejuachon Apr 8, 2020
c9f83d7
code clean up
jessiejuachon Apr 9, 2020
78dcc1c
Removing response code and response msg from cache; Passing cacheItem…
jessiejuachon Apr 9, 2020
9281c64
Changes after code review
jessiejuachon Apr 10, 2020
2657762
Adding header to CacheItem.java file
jessiejuachon Apr 10, 2020
9e80f09
Fixing failing unit test
jessiejuachon Apr 10, 2020
68467f3
Changes after code review
jessiejuachon Apr 10, 2020
d67c72e
Fixing code scan bugs
jessiejuachon Apr 10, 2020
8591973
Not storing anything in cache if response is neither 200 nor 304
jessiejuachon Apr 13, 2020
22a5b29
Loading messages from specified offline resource bundles
jessiejuachon Apr 15, 2020
ce2488b
Merge branch 'g11n-java-client' of https://github.com/vmware/singleto…
jessiejuachon Apr 15, 2020
1092963
Adding header
jessiejuachon Apr 15, 2020
35d1d97
Fixing failing unie test
jessiejuachon Apr 15, 2020
ada17f1
Code changes after review; code clean up
jessiejuachon Apr 16, 2020
fc60eef
Using java.nio.file.Paths
jessiejuachon Apr 17, 2020
3e70449
Offline mode as fallback when service fetch fails
jessiejuachon Apr 17, 2020
609d8e4
Resetting VIPCfg and I18nFactory instances for each test
jessiejuachon Apr 17, 2020
dd19b1c
Resetting VIPCfg and I18nFactory after test
jessiejuachon Apr 17, 2020
7389f8b
Fixing tests
jessiejuachon Apr 17, 2020
5b92e44
Limit to wiremock test logging
jessiejuachon Apr 20, 2020
dfb41b5
Decreasing unit test log
jessiejuachon Apr 20, 2020
21a7105
this test is timing out and trying to connect 70+ times
jessiejuachon Apr 21, 2020
84343d5
Adding comments to code
jessiejuachon Apr 21, 2020
be8e443
Merge branch 'g11n-java-client' of https://github.com/vmware/singleto…
jessiejuachon Apr 21, 2020
b805d36
Adding timestamp to cache fromn local bundle
jessiejuachon Apr 21, 2020
17bc16c
Deprecating methods in TranslationMessage; adding new method getMessage
jessiejuachon Apr 23, 2020
15da010
Added tests for TranslationMessage.getMessage
jessiejuachon Apr 24, 2020
631de86
Fixing test
jessiejuachon Apr 24, 2020
ffd9adb
Fixing test
jessiejuachon Apr 24, 2020
c00f7ae
Fixing test
jessiejuachon Apr 24, 2020
d0504c4
Removing comment field because source collection is not supported any…
jessiejuachon Apr 24, 2020
79b18d0
Code clean up
jessiejuachon Apr 24, 2020
ec9f5db
Changes from code review
jessiejuachon Apr 24, 2020
2e8bd36
Code clean up
jessiejuachon Apr 24, 2020
4e44a8c
Optional SourceOpt in initialization
jessiejuachon Apr 26, 2020
de5c4ee
Fixing test
jessiejuachon Apr 27, 2020
406cb2a
Adding comments to code
jessiejuachon Apr 28, 2020
c80696a
Adding comment for javadoc
jessiejuachon Apr 28, 2020
79d111e
Cleaning up code
jessiejuachon Apr 28, 2020
24d8d5d
Code clean up
jessiejuachon Apr 28, 2020
23e62b6
Fixing test
jessiejuachon Apr 28, 2020
369643f
Removing source message fallback from this PR
jessiejuachon Apr 29, 2020
4e67e25
Merge branch 'g11n-java-client' into g11n-java-client
jessiejuachon Apr 29, 2020
636b5b4
Default locale in config file instead of hardcoded; get supported loc…
jessiejuachon May 5, 2020
71a87e5
Merge branch 'g11n-java-client' into g11n-java-client
jessiejuachon May 5, 2020
b9a90e3
Fixing test
jessiejuachon May 5, 2020
060666d
Fixing tests
jessiejuachon May 5, 2020
52e3695
Default locale to Locale.ENGLISH if not set in config file
jessiejuachon May 5, 2020
2858ebc
Changed after code review: code clean up, bug fix
jessiejuachon May 6, 2020
bbe3047
Update src/main/java/com/vmware/vipclient/i18n/messages/api/opt/local…
jessiejuachon May 8, 2020
fa56388
Update src/main/java/com/vmware/vipclient/i18n/messages/api/opt/local…
jessiejuachon May 8, 2020
08cd96d
code clean up, error logging, bug fix
jessiejuachon May 8, 2020
e89ff56
Merge branch 'g11n-java-client' of https://github.com/vmware/singleto…
jessiejuachon May 8, 2020
119aebd
code clean up
jessiejuachon May 8, 2020
e8a937d
code clean up
jessiejuachon May 11, 2020
9d95cd1
Using messages_source.json for source locale messages.
jessiejuachon May 12, 2020
06b60bb
code clean up
jessiejuachon May 12, 2020
51b4dfa
Merge branch 'g11n-java-client' of https://github.com/vmware/singleto…
jessiejuachon May 13, 2020
778a7a1
Commenting out a test
jessiejuachon May 13, 2020
077f19b
code clean up
jessiejuachon May 13, 2020
9424346
code clean up
jessiejuachon May 13, 2020
c25a676
Error logging when fetch failed
jessiejuachon May 13, 2020
db641b9
bug fix
jessiejuachon May 13, 2020
5cd06d6
code clean up
jessiejuachon May 14, 2020
6915f11
Updating sample application
jessiejuachon May 15, 2020
a64be3f
Merge branch 'g11n-java-client' into g11n-java-client
jessiejuachon May 17, 2020
7128194
fixing failing smoke test
jessiejuachon May 19, 2020
c4a388c
Merge branch 'g11n-java-client' of https://github.com/jessiejuachon/s…
jessiejuachon May 19, 2020
5ecea93
code clean up
jessiejuachon May 19, 2020
4c5b0a1
fixing code scan
jessiejuachon May 19, 2020
067d19d
Fixing code scan
jessiejuachon May 19, 2020
711ce80
Adding prodMode configuration
jessiejuachon May 19, 2020
b7dabdb
Merge branch 'g11n-java-client' into g11n-java-client
jessiejuachon May 19, 2020
d8543ec
code clean up
jessiejuachon May 20, 2020
e8094f4
Setting default prodMode to true
jessiejuachon May 21, 2020
9a5ef77
Removing prodMode configuration and logic. Throwing exception if sour…
jessiejuachon Jun 17, 2020
5011c38
Updating javadoc
jessiejuachon Jun 17, 2020
33432b1
Fixing bug: Fallback shall apply to both TranslationMessage.getMessag…
jessiejuachon Jun 17, 2020
c9d3cae
Adding test for Translateion.getMessages, locale not supported
jessiejuachon Jun 17, 2020
adb40ec
code comments
jessiejuachon Jun 17, 2020
7ab5c59
code clean up
jessiejuachon Jun 19, 2020
3c10089
Fix issue: https://github.com/vmware/singleton/issues/622
jessiejuachon Jun 19, 2020
76105b7
code clean up
jessiejuachon Jun 29, 2020
49c5648
code clean up
jessiejuachon Jun 29, 2020
d543e99
code clean up
jessiejuachon Jun 29, 2020
3ecf958
code clean up
jessiejuachon Jun 29, 2020
e6a07a1
code clean up
jessiejuachon Jun 29, 2020
89ff088
code clean up
jessiejuachon Jun 29, 2020
f1356ab
code clean up
jessiejuachon Jun 29, 2020
c63bc2d
Merge branch 'g11n-java-client' of https://github.com/vmware/singleto…
jessiejuachon Jul 15, 2020
beab148
Fix for https://github.com/vmware/singleton/issues/661
jessiejuachon Jul 15, 2020
2cd7713
adding license header
jessiejuachon Jul 16, 2020
e13b694
changes after code review
jessiejuachon Jul 16, 2020
f1f70b3
Update src/main/java/com/vmware/vipclient/i18n/messages/api/opt/local…
jessiejuachon Jul 20, 2020
fe535dc
Update src/main/java/com/vmware/vipclient/i18n/messages/service/Produ…
jessiejuachon Jul 20, 2020
0d22b9f
changes after code review
jessiejuachon Jul 20, 2020
4870782
changes after code review
jessiejuachon Jul 20, 2020
f451436
fix for issues: 664, 662, 686
jessiejuachon Jul 22, 2020
89d7b0d
code clean up
jessiejuachon Jul 23, 2020
ec6e693
code clean up
jessiejuachon Jul 24, 2020
65f1d08
Adding LocaleDTO
jessiejuachon Jul 24, 2020
60fb3b3
code clean up
jessiejuachon Jul 24, 2020
a698f20
code clean up
jessiejuachon Jul 24, 2020
eede3b5
changes after code review
jessiejuachon Jul 31, 2020
2dbfc72
Bug fix https://github.com/vmware/singleton/issues/604, https://githu…
jessiejuachon Aug 3, 2020
e63ab94
Merge branch 'g11n-java-client' into g11n-java-client
jessiejuachon Aug 3, 2020
ae1dccd
code clean up
jessiejuachon Aug 3, 2020
6fbaab4
Mapping to the fallback locale's cache
jessiejuachon Aug 5, 2020
ff99a37
code clean up
jessiejuachon Aug 5, 2020
271ee08
changes after code review
jessiejuachon Aug 7, 2020
230e21c
code clean up
jessiejuachon Aug 12, 2020
55764a7
code clean up
jessiejuachon Aug 12, 2020
e9214cb
code clean up
jessiejuachon Aug 12, 2020
beaa5ef
code clean up
jessiejuachon Aug 12, 2020
673b8fd
code clean up
jessiejuachon Aug 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,77 +14,60 @@ public class MessageCacheItem implements CacheItem {
public MessageCacheItem() {

}

public MessageCacheItem (Map<String, String> dataMap, String etag, long timestamp, Long maxAgeMillis) {
super();
this.addCachedData(dataMap);
this.etag = etag;
this.timestamp = timestamp;
this.maxAgeMillis = maxAgeMillis;
}

public MessageCacheItem (Map<String, String> dataMap) {
super();

public MessageCacheItem(Map<String, String> dataMap) {
if (dataMap != null)
this.addCachedData(dataMap);
this.cachedData.putAll(dataMap);
}


public MessageCacheItem (String locale, Map<String, String> dataMap, String etag, long timestamp, Long maxAgeMillis) {
this.setCacheItem(locale, dataMap, etag, timestamp, maxAgeMillis);
}

private String locale;
private String etag;
private long timestamp;
private Long maxAgeMillis = 86400000l;

private final Map<String, String> cachedData = new HashMap<String, String>();

public void addCacheData(String key, String value) {
this.cachedData.put(key, value);
private final Map<String, String> cachedData = new HashMap<>();

public synchronized void setCacheItem(String locale, Map<String, String> dataToCache, String etag, long timestamp, Long maxAgeMillis) {
if (dataToCache != null)
this.cachedData.putAll(dataToCache);
this.setCacheItem(locale, etag, timestamp, maxAgeMillis);
}

public synchronized void addCachedData(Map<String, String> cachedData) {
if (cachedData != null)
this.cachedData.putAll(cachedData);
public synchronized void setCacheItem(String locale, String etag, long timestamp, Long maxAgeMillis) {
this.locale = locale;
if (etag != null && !etag.isEmpty())
this.etag = etag;
this.timestamp = timestamp;
if (maxAgeMillis != null)
this.maxAgeMillis = maxAgeMillis;
}

public synchronized void setCacheItem (MessageCacheItem cacheItem) {
// Do not update cacheItem if timestamp is earlier than current.
// An older timestamp comes from an old thread that was blocked.
if (cacheItem.getTimestamp() < this.timestamp)
return;
this.addCachedData(cacheItem.getCachedData());
this.etag = cacheItem.etag;
this.timestamp = cacheItem.timestamp;
this.maxAgeMillis = cacheItem.maxAgeMillis;
this.setCacheItem(cacheItem.getLocale(), cacheItem.getCachedData(), cacheItem.getEtag(), cacheItem.getTimestamp(), cacheItem.getMaxAgeMillis());
}

public synchronized String getEtag() {
public String getEtag() {
return etag;
}

public synchronized void setEtag(String etag) {
this.etag = etag;
}

public synchronized long getTimestamp() {
public long getTimestamp() {
return timestamp;
}

public synchronized void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}

public Map<String, String> getCachedData() {
return cachedData;
}

public synchronized Long getMaxAgeMillis() {
public Long getMaxAgeMillis() {
return maxAgeMillis;
}

public synchronized void setMaxAgeMillis(Long maxAgeMillis) {
this.maxAgeMillis = maxAgeMillis;
}
public String getLocale() { return locale; }

public boolean isExpired() {
public synchronized boolean isExpired() {
// If offline mode only, cache never expires.
if (VIPCfg.getInstance().getVipServer() == null) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ public void getComponentMessages(MessageCacheItem cacheItem) {
path = Paths.get(uri);
messages = JSONBundleUtil.getMessages(path);
}

cacheItem.addCachedData(messages);
cacheItem.setTimestamp(System.currentTimeMillis());
cacheItem.setCacheItem(dto.getLocale(), messages, null, System.currentTimeMillis(), null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need to store time to cache because data from local will never expire.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data from local must expire. Use case: If data from local was fetched as a fallback for when Singleton service is temporarily down. In this case, when the cached data from local expires, it should try to fetch from the service again.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be set the time in other place because here is Local Opt, it doesn't need to consider the time.

Copy link
Contributor Author

@jessiejuachon jessiejuachon Aug 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So where do you recommend to set the time? By the way, there is nothing about expire time here, only timestamp of when the data was fetched from local bundles. And it has been there before this PR, just combined them into 1 call, so I do not want to make any change about that in this PR.

} catch (Exception e) {
logger.debug(e.getMessage());
// Do not update cacheItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@
import java.util.List;
import java.util.Map;

import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vmware.vipclient.i18n.VIPCfg;
import com.vmware.vipclient.i18n.base.cache.MessageCacheItem;
import com.vmware.vipclient.i18n.messages.api.opt.BaseOpt;
Expand All @@ -23,7 +18,10 @@
import com.vmware.vipclient.i18n.messages.api.url.V2URL;
import com.vmware.vipclient.i18n.messages.dto.MessagesDTO;
import com.vmware.vipclient.i18n.util.ConstantsKeys;
import com.vmware.vipclient.i18n.util.LocaleUtility;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ComponentBasedOpt extends BaseOpt implements Opt, MessageOpt {
private final Logger logger = LoggerFactory.getLogger(ComponentBasedOpt.class.getName());
Expand Down Expand Up @@ -52,33 +50,32 @@ public void getComponentMessages(MessageCacheItem cacheItem) {

if (responseCode != null && (responseCode.equals(HttpURLConnection.HTTP_OK) ||
responseCode.equals(HttpURLConnection.HTTP_NOT_MODIFIED))) {

// If already in cache (timestamp > 0), always extend the timestamp.
if (cacheItem.getTimestamp() != 0 && response.get(URLUtils.RESPONSE_TIMESTAMP) != null)
cacheItem.setTimestamp((long) response.get(URLUtils.RESPONSE_TIMESTAMP) );
long timestamp = 0;
String etag = null;
Long maxAgeMillis = null;

if (response.get(URLUtils.RESPONSE_TIMESTAMP) != null)
timestamp = (long) response.get(URLUtils.RESPONSE_TIMESTAMP);
if (response.get(URLUtils.HEADERS) != null)
cacheItem.setEtag(URLUtils.createEtagString((Map<String, List<String>>) response.get(URLUtils.HEADERS)));
etag = URLUtils.createEtagString((Map<String, List<String>>) response.get(URLUtils.HEADERS));
if (response.get(URLUtils.MAX_AGE_MILLIS) != null)
cacheItem.setMaxAgeMillis((Long) response.get(URLUtils.MAX_AGE_MILLIS));
maxAgeMillis = (Long) response.get(URLUtils.MAX_AGE_MILLIS);

if (responseCode.equals(HttpURLConnection.HTTP_OK)) {
JSONObject respObj = (JSONObject) JSONValue.parse((String) response.get(URLUtils.BODY));
try {
if (getResponseCode(respObj) == 200){
// If not yet in cache (timestamp = 0), store the timestamp only when successful (business code 200).
if (cacheItem.getTimestamp() == 0 && response.get(URLUtils.RESPONSE_TIMESTAMP) != null) {
cacheItem.setTimestamp((long) response.get(URLUtils.RESPONSE_TIMESTAMP) );
}
if (getResponseCode(respObj) == 200) {
Map<String,String> messages = this.getMsgsJson(response);
if (messages != null) {
cacheItem.addCachedData(messages);
cacheItem.setCacheItem(this.dto.getLocale(), messages, etag, timestamp, maxAgeMillis);
}
}
} catch (Exception e) {
logger.error("Failed to get messages");
}
}

} else {
cacheItem.setCacheItem(this.dto.getLocale(), etag, timestamp, maxAgeMillis);
Xiaochao8 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ public JSONObject queryFromServer(final Set<String> components, final Set<String
Map<String, Object> response = VIPCfg.getInstance().getVipService().getHttpRequester().request(url, ConstantsKeys.GET,
requestData);
this.responseStr = (String) response.get(URLUtils.BODY);
cacheItem.setEtag(URLUtils.createEtagString((Map<String, List<String>>) response.get(URLUtils.HEADERS)));
cacheItem.setTimestamp((long) response.get(URLUtils.RESPONSE_TIMESTAMP));
cacheItem.setMaxAgeMillis((Long) response.get(URLUtils.MAX_AGE_MILLIS));
String etag = URLUtils.createEtagString((Map<String, List<String>>) response.get(URLUtils.HEADERS));
long timestamp = (long) response.get(URLUtils.RESPONSE_TIMESTAMP);
Long maxAgeMillis = (Long) response.get(URLUtils.MAX_AGE_MILLIS);
cacheItem.setCacheItem(null, etag, timestamp, maxAgeMillis);
if (StringUtil.isEmpty(this.responseStr))
throw new VIPJavaClientException(ConstantsMsg.SERVER_RETURN_EMPTY);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
*/
package com.vmware.vipclient.i18n.messages.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.*;

import com.vmware.vipclient.i18n.VIPCfg;
import com.vmware.vipclient.i18n.base.cache.Cache;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,37 +32,43 @@ public ComponentService(MessagesDTO dto) {
this.dto = dto;
}

/**
* Fetch messages from either remote vip service or local bundle
*
* @param cacheItem MessageCacheItem object to store the messages
* @param msgSourceQueueIter Iterator of the msgSourceQueue (e.g. [DataSourceEnum.VIP, DataSourceEnum.Bundle])
*/
@SuppressWarnings("unchecked")
private void fetchMessages(final MessageCacheItem cacheItem, Iterator<DataSourceEnum> msgSourceQueueIter) {
if (!msgSourceQueueIter.hasNext())
return;

long timestampOld = cacheItem.getTimestamp();
DataSourceEnum dataSource = msgSourceQueueIter.next();
dataSource.createMessageOpt(dto).getComponentMessages(cacheItem);
long timestamp = cacheItem.getTimestamp();
if (timestampOld == timestamp) {
logger.debug(FormatUtils.format(ConstantsMsg.GET_MESSAGES_FAILED, dto.getComponent(), dto.getLocale(), dataSource.toString()));
}

// Skip this block if timestamp is not 0 (which means cacheItem is in the cache) regardless if cacheItem is expired or not.
// Otherwise, try the next dataSource in the queue.
if (timestamp == 0) {
// Try the next dataSource in the queue
if (msgSourceQueueIter.hasNext()) {
fetchMessages(cacheItem, msgSourceQueueIter);
// If no more data source in queue, log the error. This means that neither online nor offline fetch succeeded.
} else {
logger.debug(FormatUtils.format(ConstantsMsg.GET_MESSAGES_FAILED_ALL, dto.getComponent(), dto.getLocale()));
}
}
}
/**
* Fetches data and populates the MessageCacheItem
*
* @param cacheItem The MessageCacheItem to populate/refresh data in. The following properties of cacheItem will be populated/refreshed:
* <ul>
* <li>The cachedData map which holds the localized messages</li>
* <li>The timestamp of when the messages were fetched</li>
* <li>The maxAgeMillis which tells how long before the cacheData map is considered to be expired.</li>
* <li>The eTag, if any, which will be used in the succeeding cache refresh.</li>
* </ul>
* @param msgSourceQueueIter Iterator of the message source queue (e.g. [DataSourceEnum.VIP, DataSourceEnum.Bundle])
*/
@SuppressWarnings("unchecked")
private void refreshCacheItem(final MessageCacheItem cacheItem, Iterator<DataSourceEnum> msgSourceQueueIter) {
if (!msgSourceQueueIter.hasNext())
return;

long timestampOld = cacheItem.getTimestamp();
DataSourceEnum dataSource = msgSourceQueueIter.next();
dataSource.createMessageOpt(dto).getComponentMessages(cacheItem);
long timestamp = cacheItem.getTimestamp();
if (timestampOld == timestamp) {
logger.debug(FormatUtils.format(ConstantsMsg.GET_MESSAGES_FAILED, dto.getComponent(), dto.getLocale(), dataSource.toString()));
}

// Skip this block if timestamp is not 0 (which means cacheItem is in the cache) regardless if cacheItem is expired or not.
// Otherwise, try the next dataSource in the queue.
if (timestamp == 0) {
// Try the next dataSource in the queue
if (msgSourceQueueIter.hasNext()) {
refreshCacheItem(cacheItem, msgSourceQueueIter);
// If no more data source in queue, log the error. This means that neither online nor offline fetch succeeded.
} else {
logger.debug(FormatUtils.format(ConstantsMsg.GET_MESSAGES_FAILED_ALL, dto.getComponent(), dto.getLocale()));
}
}
}

/**
* Get MessageCacheItem from cache.
Expand Down Expand Up @@ -100,44 +106,78 @@ public MessageCacheItem getMessages(Iterator<Locale> fallbackLocalesIter) {
if (cacheService.isContainComponent()) { // Item is in cache
cacheItem = cacheService.getCacheOfComponent();
if (cacheItem.isExpired()) { // cacheItem has expired
// Update the cache in a separate thread
populateCacheTask(cacheItem);
// Refresh the cacheItem in a separate thread
refreshCacheItemTask(cacheItem);
}
} else { // Item is not in cache
// Create a new cacheItem object to be stored in cache
cacheItem = new MessageCacheItem();
fetchMessages(cacheItem, VIPCfg.getInstance().getMsgOriginsQueue().iterator());

if (!cacheItem.getCachedData().isEmpty()) {
cacheService.addCacheOfComponent(cacheItem);
} else if (!dto.getLocale().equals(ConstantsKeys.SOURCE) && fallbackLocalesIter!=null && fallbackLocalesIter.hasNext()) {
// If failed to fetch message, use MessageCacheItem of the next fallback locale.
MessagesDTO fallbackLocaleDTO = new MessagesDTO(dto.getComponent(), fallbackLocalesIter.next().toLanguageTag(), dto.getProductID(), dto.getVersion());
cacheItem = new ComponentService(fallbackLocaleDTO).getMessages(fallbackLocalesIter);
if (!cacheItem.getCachedData().isEmpty()) {
cacheService.addCacheOfComponent(cacheItem);
}
// If the cacheItem is for a fallback locale, create and store cacheItem for the requested locale in a separate thread.
if (!LocaleUtility.isSameLocale(cacheItem.getLocale(), this.dto.getLocale())) {
this.createCacheItemTask(fallbackLocalesIter);
}
Comment on lines +112 to 115
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about moving this into above if block so it doesn't fetch every time.

Copy link
Contributor Author

@jessiejuachon jessiejuachon Aug 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We actually want to always try to fetch for the requested locale every time in a separate thread, not just when the cacheItem has expired. This is because if the cacheItem is for a fallback locale, any other call for a non-supported locale can trigger a refresh of the fallback locale's cacheItem. If that happens, the requested locale may not satisfy the if(cacheItem.isExpired() because the fallback locale has been refreshed by some other request. In this case, we still want to try and fetch the data for the requested locale.

} else { // Item is not in cache. Create and store cacheItem for the requested locale
cacheItem = createCacheItem(fallbackLocalesIter);
}
return cacheItem;
}

private void populateCacheTask(MessageCacheItem cacheItem) {

/**
* Creates a new MessageCacheItem for the DTO and stores it in cache.
*
* @param fallbackLocalesIter The fallback locale queue to use in case of failure.
*
*/
private MessageCacheItem createCacheItem(Iterator<Locale> fallbackLocalesIter) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems here has a recursive call: getMessages(Iterator fallbackLocalesIter) -> createCacheItem(Iterator fallbackLocalesIter) -> new ComponentService(fallbackLocaleDTO).getMessages(fallbackLocalesIter)

In my opinion, the hierarchical relation between methods are not clear.

CacheService cacheService = new CacheService(dto);
// Create a new cacheItem object to be stored in cache
MessageCacheItem cacheItem = new MessageCacheItem();
refreshCacheItem(cacheItem, VIPCfg.getInstance().getMsgOriginsQueue().iterator());

if (!cacheItem.getCachedData().isEmpty()) {
cacheService.addCacheOfComponent(cacheItem);
} else if (!dto.getLocale().equals(ConstantsKeys.SOURCE) && fallbackLocalesIter!=null && fallbackLocalesIter.hasNext()) {
// If failed to fetch message for the requetsed DTO, use MessageCacheItem of the next fallback locale.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to judge if current fallback locale is same as the locale in DTO. If same, should skip it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you follow the recursive logic, the locale in the dto will be the previous locale in the fallback locale queue.

MessagesDTO fallbackLocaleDTO = new MessagesDTO(dto.getComponent(), fallbackLocalesIter.next().toLanguageTag(), dto.getProductID(), dto.getVersion());
cacheItem = new ComponentService(fallbackLocaleDTO).getMessages(fallbackLocalesIter);
if (!cacheItem.getCachedData().isEmpty()) {
cacheService.addCacheOfComponent(cacheItem);
}
Comment on lines +136 to +142
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here only use the first fallback locale in the fallback locale chain, doesn't go through the whole fallback locale chain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It actually does go through the whole cahin/queue. In line 138, you see fallbackLocalesIter.next(). By calling .next(), the current pointer of fallbackLocalesIter moves to the next item in the queue. When you pass fallbackLocalesIter to ComponentService.getMessages() in line139, the iterator holds the correct place (current pointer) in the queue.

}
return cacheItem;
}

private void createCacheItemTask(Iterator<Locale> fallbackLocalesIter) {
Callable<MessageCacheItem> callable = () -> {
try {
return this.createCacheItem(fallbackLocalesIter);
} catch (Exception e) {
// To make sure that the thread will close
// even when an exception is thrown
return null;
}
};
FutureTask<MessageCacheItem> task = new FutureTask<>(callable);
Thread thread = new Thread(task);
thread.start();
}

private void refreshCacheItemTask(MessageCacheItem cacheItem) {
Callable<MessageCacheItem> callable = () -> {
try {
// Pass cacheItem to getMessages so that:
// 1. A previously stored etag, if any, can be used for the next HTTP request.
// 2. CacheItem properties such as etag, timestamp and maxAgeMillis can be refreshed
// with new properties from the next HTTP response.
fetchMessages(cacheItem, VIPCfg.getInstance().getMsgOriginsQueue().listIterator());
return cacheItem;
// Get the locale of the cacheItem object. It may not be the same as the requested DTO's locale (e.g. the cacheItem is for a fallback locale).
String cacheItemLocale = cacheItem.getLocale();

// Refresh the properties of the cacheItem accordingly by passing a DTO with the correct locale
// to ComponentService, so that it will fetch messages for the correct locale to refresh the cacheItem.
MessagesDTO cacheItemDTO = new MessagesDTO(dto.getComponent(), cacheItemLocale, dto.getProductID(), dto.getVersion());
new ComponentService(cacheItemDTO).refreshCacheItem(cacheItem, VIPCfg.getInstance().getMsgOriginsQueue().listIterator());

return cacheItem;
} catch (Exception e) {
// To make sure that the thread will close
// even when an exception is thrown
return null;
}
};
FutureTask<MessageCacheItem> task = new FutureTask<MessageCacheItem>(callable);
FutureTask<MessageCacheItem> task = new FutureTask<>(callable);
Thread thread = new Thread(task);
thread.start();
}
Expand Down
Loading