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

Fix Windows bug on getComponentMessages #913

Merged
merged 34 commits into from
Dec 9, 2020
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5f42099
Non-blocking getSupportedLocales
jessiejuachon Nov 17, 2020
a207056
fixing failing test
jessiejuachon Nov 17, 2020
d291d7a
code clean up
jessiejuachon Nov 17, 2020
134d23d
test
jessiejuachon Nov 18, 2020
a75c69b
fix test
jessiejuachon Nov 18, 2020
de5850a
code clean up
jessiejuachon Nov 18, 2020
1c67db9
code clean up
jessiejuachon Nov 18, 2020
d7b27c3
Refresh cache of supported locales in a separate thread
jessiejuachon Nov 18, 2020
a9b5238
code clean up
jessiejuachon Nov 18, 2020
e1dca83
code clean up
jessiejuachon Nov 18, 2020
7f05a14
fixing sonarcube bug
jessiejuachon Nov 19, 2020
120e4d9
code cleanup
jessiejuachon Nov 19, 2020
c0b385d
sonarcube bug
jessiejuachon Nov 19, 2020
12cc751
code clean up
jessiejuachon Nov 22, 2020
d0dae5d
code clean up
jessiejuachon Nov 22, 2020
b86d337
Use getResourceAsStream to read bundle
jessiejuachon Nov 30, 2020
117b8b2
code clean up
jessiejuachon Dec 1, 2020
7ae72d5
adding else block
jessiejuachon Dec 2, 2020
5c99efe
Update src/main/java/com/vmware/vipclient/i18n/messages/api/opt/local…
jessiejuachon Dec 3, 2020
e58155a
changes after code review
jessiejuachon Dec 3, 2020
cebb186
rename parameter
jessiejuachon Dec 3, 2020
4eb761b
code clean up
jessiejuachon Dec 3, 2020
9fa2837
Merge branch 'non-block-cache' of github.com:jessiejuachon/singleton …
jessiejuachon Dec 3, 2020
d6559b3
thread safety
jessiejuachon Dec 4, 2020
5ab40c5
adding comment
jessiejuachon Dec 4, 2020
26bc138
space clean up
jessiejuachon Dec 4, 2020
f47feea
thread-safe file system access
jessiejuachon Dec 4, 2020
8f11aa8
optimizations
jessiejuachon Dec 4, 2020
84b6e2f
Merge remote-tracking branch 'upstream/g11n-java-client' into non-blo…
jessiejuachon Dec 4, 2020
1d830b2
removing file system lock from this PR (out of scope)
jessiejuachon Dec 5, 2020
b55e685
closing input stream
jessiejuachon Dec 5, 2020
d8ac6f6
closing inputstream
jessiejuachon Dec 5, 2020
6e5e871
Fixing windows bug on getComponentMessages
jessiejuachon Dec 8, 2020
6bb40e0
Merge remote-tracking branch 'upstream/g11n-java-client' into non-blo…
jessiejuachon Dec 8, 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 @@ -40,7 +40,7 @@ public void getSupportedLanguages(String locale, LocaleCacheItem cacheItem) {
Map<String, String> supportedLanguageNames = new HashMap<String, String>();

ProductService ps = new ProductService(dto);
Set<String> supportedLanguages = ps.getSupportedLanguageTags(DataSourceEnum.Bundle);
Set<String> supportedLanguages = ps.getSupportedLocales(DataSourceEnum.Bundle);

if(supportedLanguages != null && !supportedLanguages.isEmpty()) {
Map<String, String> languagesNames = getLanguagesNamesFromBundle(locale);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,31 @@
package com.vmware.vipclient.i18n.messages.api.opt.local;

import com.vmware.vipclient.i18n.VIPCfg;
import com.vmware.vipclient.i18n.base.DataSourceEnum;
import com.vmware.vipclient.i18n.base.cache.MessageCacheItem;
import com.vmware.vipclient.i18n.exceptions.VIPJavaClientException;
import com.vmware.vipclient.i18n.messages.api.opt.MessageOpt;
import com.vmware.vipclient.i18n.messages.api.opt.Opt;
import com.vmware.vipclient.i18n.messages.dto.MessagesDTO;
import com.vmware.vipclient.i18n.messages.service.ProductService;
import com.vmware.vipclient.i18n.util.FormatUtils;
import com.vmware.vipclient.i18n.util.JSONBundleUtil;
import com.vmware.vipclient.i18n.util.LocaleUtility;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.file.*;
import java.util.*;

public class LocalMessagesOpt implements Opt, MessageOpt {

private Logger logger = LoggerFactory.getLogger(LocalMessagesOpt.class.getName());

private static final String OFFLINE_RESOURCE_PATH = "{0}/messages_{1}.json";
private static final String OFFLINE_RESOURCE_PATH = "{0}{1}/messages_{2}.json";
private MessagesDTO dto;

public LocalMessagesOpt(MessagesDTO dto) {
Expand All @@ -43,40 +44,48 @@ public JSONObject getComponentMessages() {

@Override
public void getComponentMessages(MessageCacheItem cacheItem) {
Locale bestMatch = Locale.lookup(Arrays.asList(new Locale.LanguageRange((dto.getLocale()))),
getSupportedLocales());
InputStream is = null;
try {
String filePath = FormatUtils.format(OFFLINE_RESOURCE_PATH, dto.getComponent(), bestMatch.toLanguageTag());
Path path = Paths.get(VIPCfg.getInstance().getOfflineResourcesBaseUrl(), filePath);

URI uri = Thread.currentThread().getContextClassLoader().
getResource(path.toString()).toURI();

Map<String, String> messages = null;
if (uri.getScheme().equals("jar")) {
try(FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap())) {
path = fileSystem.getPath(path.toString());
messages = JSONBundleUtil.getMessages(path);
}
} else {
path = Paths.get(uri);
messages = JSONBundleUtil.getMessages(path);
}
is = getInputStream();
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject) jsonParser.parse(new InputStreamReader(is, "UTF-8"));
Map<String, String> messages = (JSONObject) jsonObject.get("messages");
cacheItem.setCacheItem(messages, null, System.currentTimeMillis(), null);
} catch (Exception e) {
logger.debug(e.getMessage());
// Do not update cacheItem
logger.error("Failed to get offline messages for product: " + dto.getProductID() + " " + dto.getVersion() +
", component: " + dto.getComponent() + ", locale: " + dto.getLocale() + ", exception: " + e.getMessage());
}
}

private List<Locale> getSupportedLocales() {
ProductService ps = new ProductService(dto);
Set<String> supportedLanguages = ps.getSupportedLanguageTags(DataSourceEnum.Bundle);
logger.debug("supported languages: [{}]", supportedLanguages.toString());
List<Locale> supportedLocales = new LinkedList<Locale>();
for (String languageTag : supportedLanguages) {
supportedLocales.add(Locale.forLanguageTag(languageTag));
}
return supportedLocales;
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
logger.debug(e.getMessage());
}
}
}

private InputStream getInputStream() {
String locale = LocaleUtility.fmtToMappedLocale(dto.getLocale()).toLanguageTag();
while (true) {
String offlineResourcePath = VIPCfg.getInstance().getOfflineResourcesBaseUrl();
if(!offlineResourcePath.endsWith("/"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Can "/" be suitable for all systems? Should it be FileSeparator?

Copy link
Contributor Author

@jessiejuachon jessiejuachon Dec 8, 2020

Choose a reason for hiding this comment

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

No, that is the root cause of the bug. If we use FileSeparator (same as Paths.get), then '' (backslash) will be used in Windows platform. However, the forward slash in jar is not platform dependent. Even if the application is run on a Windows platform, the jar files will still have forward slashes. The use of Paths.get (or FileSeparator) was causing the issue of not finding the message bundles.

offlineResourcePath = offlineResourcePath + "/";
String filePath = FormatUtils.format(OFFLINE_RESOURCE_PATH, offlineResourcePath, dto.getComponent(), locale);
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
if (is != null)
return is;
/*
* If valid URI is not found, find the next best matching locale available in the file system
* This could happen if:
* a. the matching resource bundle had been corrupted or removed from the file system since last check.
* b. the requested locale hadn't been matched against the list of supported locales. This happens if
* supported locales cache hasn't been initialized or if previous attempts to populate the cache had failed.
*/
int index = locale.lastIndexOf("-");
if (index <= 0)
break;
locale = locale.substring(0, index);
}
throw new VIPJavaClientException("Failed to get resource bundle for locale: " + locale);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,20 @@ public void getSupportedLocales(MessageCacheItem cacheItem) {
for (URI uri : uris) {
if (uri.getScheme().equals("jar")) {
try (FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap())) {
path = fileSystem.getPath(Paths.get(uri).toString());
path = fileSystem.getPath(path.toString());
getSupportedLocales(path, supportedLocales);
}
} else {
path = Paths.get(uri);
getSupportedLocales(path, supportedLocales);
}
}

} catch (Exception e) {
logger.debug(e.getMessage());
logger.debug("Fetching the list of supported locales failed: " + e.getMessage());
return;
}
Map<String, String> languageTagMap = new HashMap<>();
for (String languageTag: supportedLocales) {
for (String languageTag : supportedLocales) {
languageTagMap.put(languageTag, languageTag);
}
cacheItem.setCacheItem(languageTagMap, null, System.currentTimeMillis(), null);
Expand All @@ -68,15 +68,14 @@ public List<String> getComponents() {
for (URI uri : uris) {
if (uri.getScheme().equals("jar")) {
try (FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap())) {
path = fileSystem.getPath(Paths.get(uri).toString());
path = fileSystem.getPath(path.toString());
getComponents(path, components);
}
} else {
path = Paths.get(uri);
getComponents(path, components);
}
}

} catch (Exception e) {
logger.debug(e.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.util.Iterator;
import java.util.Locale;
import java.util.Set;

import com.vmware.vipclient.i18n.VIPCfg;
import com.vmware.vipclient.i18n.base.DataSourceEnum;
Expand Down Expand Up @@ -49,25 +50,47 @@ private void refreshCacheItem(final MessageCacheItem cacheItem, Iterator<DataSou
return;
}

long timestampOld = cacheItem.getTimestamp();
DataSourceEnum dataSource = msgSourceQueueIter.next();
String localeOrig = dto.getLocale();
if (dataSource.equals(DataSourceEnum.VIP) && dto.getLocale().equals(ConstantsKeys.SOURCE)) {
dto.setLocale(ConstantsKeys.LATEST);
}
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()));
}
dto.setLocale(localeOrig);
if (!proceed(dataSource)) { //Requested locale is not supported, does not match any supported locales
refreshCacheItem(cacheItem, msgSourceQueueIter); // Try the next dataSource
} else {
long timestampOld = cacheItem.getTimestamp();
String localeOrig = dto.getLocale();
if (dataSource.equals(DataSourceEnum.VIP) && dto.getLocale().equals(ConstantsKeys.SOURCE)) {
dto.setLocale(ConstantsKeys.LATEST);
}
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()));
}
dto.setLocale(localeOrig);

// If timestamp is 0, it means that cacheItem not yet in cache. So try the next data source.
if (timestamp == 0) {
// Try the next dataSource in the queue
refreshCacheItem(cacheItem, msgSourceQueueIter);
// If timestamp is 0, it means that cacheItem not yet in cache. So try the next data source.
if (timestamp == 0) {
// Try the next dataSource in the queue
refreshCacheItem(cacheItem, msgSourceQueueIter);
}
}
}

/**
* @return 'true' for either of the following cases. Otherwise, false (locale not supported in data source).
* <ul>
* <li>the dataSource's set of supported locales is not in cache. If the list is not in cache, it should not block refreshCacheItem</li>
* <li>the requested locale is found in the data source's cached set of supported locales.</li>
* </ul>
*/
private boolean proceed(DataSourceEnum dataSource) {
ProductService ps = new ProductService(dto);
Set<String> supportedLocales = ps.getCachedSupportedLocales(dataSource);
logger.debug("supported languages: [{}]", supportedLocales);

/*
* Do not block refreshCacheItem if set of supported locales is not in cache (i.e. supportedLocales.isEmpty()).
* This happens either when cache is not initialized, OR previous attempts to fetch the set had failed.
*/
return (supportedLocales.isEmpty() || supportedLocales.contains(dto.getLocale()) || VIPCfg.getInstance().isPseudo());
}

/**
Expand Down Expand Up @@ -109,22 +132,23 @@ public MessageCacheItem getMessages(Iterator<Locale> fallbackLocalesIter) {
if (cacheItem.isExpired())
refreshCacheItemTask(cacheItem); // Refresh the cacheItem in a separate thread
} else { // Item is not in cache.
ProductService ps = new ProductService(dto);
Locale locale = Locale.forLanguageTag(dto.getLocale());
if (ps.isSupportedLocale(locale) || VIPCfg.getInstance().isPseudo()) {
cacheItem = createCacheItem(); // Fetch for the requested locale from data store, create cacheItem and store in cache
if (cacheItem.getCachedData().isEmpty()) // Failed to fetch messages for the requested locale
cacheItem = getFallbackLocaleMessages(fallbackLocalesIter);
} else // Requested locale is not supported and does not match any supported locales
cacheItem = createCacheItem(); // Fetch for the requested locale from data store, create cacheItem and store in cache
if (cacheItem.getCachedData().isEmpty()) // Failed to fetch messages for the requested locale
cacheItem = getFallbackLocaleMessages(fallbackLocalesIter);
}

return cacheItem;
}

private void doLocaleMatching() {
Locale matchedLocale = LocaleUtility.pickupLocaleFromList(new ProductService(dto).getSupportedLocales(), Locale.forLanguageTag(dto.getLocale()));
if (matchedLocale != null) // Requested locale matches a supported locale (eg. requested locale "fr_CA matches supported locale "fr")
dto.setLocale(LocaleUtility.fmtToMappedLocale(dto.getLocale()).toLanguageTag());

//Match against list of supported locales that is already in the cache
Set<Locale> supportedLocales = LocaleUtility.langTagtoLocaleSet(new ProductService(dto).getCachedSupportedLocales());
Locale matchedLocale = LocaleUtility.pickupLocaleFromList(supportedLocales, Locale.forLanguageTag(dto.getLocale()));
if (matchedLocale != null) { // Requested locale matches a supported locale (eg. requested locale "fr_CA matches supported locale "fr")
dto.setLocale(matchedLocale.toLanguageTag());
}
}

/**
Expand Down
Loading