Static services, OSGi, and reducing unnecessary API calls- what's the Java best practice here?
I have a component which is fetching data from a rest API. This data changes infrequently (at most once per week, as little as once per year), but it's required to be present on every single page within my website.
I have created it like so:
/* Simplified for illustration purposes */
@Model(adaptables = SlingHttpServletRequest.class,
adapters = MyComponentInterface.class,
resourceType = "...",
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class MyComponent implements MyComponentInterface {
private ExternalData externalData;
@OSGiService
ExternalDataService externalDataService;
@PostConstruct
protected void init() {
this.externalData = externalDataService.getExternalData();
}
// other properties and functions as necessary
}
And ExternalDataService looks like this:
@8220494(service = ExternalDataService.class, immediate = true)
public class ExternalDataServiceImpl implements ExternalDataService {
public ExternalData getExternalData() {
ExternalData retVal = FetchDataFromApi();
return retVal;
}
private ExternalData FetchDataFromApi() {
// makes API calls, unmarshall's response, etc
}
// other functions, properties, etc
}
At this point, everything is wired up and working correctly. However, as my content authors are editing pages, and as visitors are visiting the website, I'm afraid that my API is getting hammered with too many requests. I want to instead only fetch data from the API periodically, and cache the rest so that I'm not re-making the same call over and over again.
To do this, I brought Caffeine into my project and wired it into the ExternalDataServiceImpl. However, I need the cache to be static (e.g., utilize the same cache for all instances of the component across pages).
@8220494(service = ExternalDataService.class, immediate = true)
public class ExternalDataServiceImpl implements ExternalDataService {
private static Cache<String, ExternalData> cache;
public ExternalDataServiceImpl() {
if (cache == null) {
cache = Caffeine.newBuilder()
.expireAfterWrite(60, TimeUnit.MINUTES)
.maximumSize(100)
.build();
}
}
public ExternalData getExternalData() {
String key = "external-data";
ExternalData retVal = cache.getIfPresent(key);
if (retVal == null) { // cache miss, fetch from API
retVal = FetchDataFromApi();
cache.put(key, retVal);
}
return retVal;
}
private ExternalData FetchDataFromApi() {
// makes API calls, unmarshall's response, etc
}
// other functions, properties, etc
}
In local testing, this works. The cache is persisted across page loads and my API is only hit occasionally as the cached value expires.
My question is: Is there a better way to do this, will a static property in OSGi become an issue, and is there a way I can register Caffeine as a static service in OSGi?