Add option cache feature

pull/146/head
johnniang 2019-04-29 02:06:02 +08:00
parent 6e8ef4b35a
commit cea6a55963
7 changed files with 126 additions and 63 deletions

View File

@ -38,21 +38,17 @@ public class FreemarkerConfigAwareListener {
private final ThemeSettingService themeSettingService;
private final OptionService optionsService;
private final UserService userService;
public FreemarkerConfigAwareListener(OptionService optionService,
Configuration configuration,
ThemeService themeService,
ThemeSettingService themeSettingService,
OptionService optionsService,
UserService userService) {
this.optionService = optionService;
this.configuration = configuration;
this.themeService = themeService;
this.themeSettingService = themeSettingService;
this.optionsService = optionsService;
this.userService = userService;
}
@ -97,13 +93,11 @@ public class FreemarkerConfigAwareListener {
}
private void loadOptionsConfig() throws TemplateModelException {
Map<String, Object> options = optionService.listOptions();
configuration.setSharedVariable("options", options);
configuration.setSharedVariable("options", optionService.listOptions());
}
private void loadThemeConfig() throws TemplateModelException {
ThemeProperty activatedTheme = themeService.getActivatedTheme();
configuration.setSharedVariable("theme", activatedTheme);
configuration.setSharedVariable("theme", themeService.getActivatedTheme());
configuration.setSharedVariable("settings", themeSettingService.listAsMapBy(themeService.getActivatedThemeId()));
}
}

View File

@ -0,0 +1,29 @@
package run.halo.app.event.options;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import run.halo.app.cache.StringCacheStore;
import run.halo.app.service.OptionService;
/**
* Option updated listener.
*
* @author johnniang
* @date 19-4-29
*/
@Component
public class OptionUpdatedListener implements ApplicationListener<OptionUpdatedEvent> {
private final StringCacheStore cacheStore;
public OptionUpdatedListener(StringCacheStore cacheStore) {
this.cacheStore = cacheStore;
}
@Override
@Async
public void onApplicationEvent(OptionUpdatedEvent event) {
cacheStore.delete(OptionService.OPTIONS_KEY);
}
}

View File

@ -0,0 +1,21 @@
package run.halo.app.event.theme;
import org.springframework.context.ApplicationEvent;
/**
* Theme updated event.
*
* @author johnniang
* @date 19-4-29
*/
public class ThemeUpdatedEvent extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public ThemeUpdatedEvent(Object source) {
super(source);
}
}

View File

@ -0,0 +1,29 @@
package run.halo.app.event.theme;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import run.halo.app.cache.StringCacheStore;
import run.halo.app.service.ThemeService;
/**
* Theme updated listener.
*
* @author johnniang
* @date 19-4-29
*/
@Component
public class ThemeUpdatedListener implements ApplicationListener<ThemeUpdatedEvent> {
private final StringCacheStore cacheStore;
public ThemeUpdatedListener(StringCacheStore cacheStore) {
this.cacheStore = cacheStore;
}
@Override
@Async
public void onApplicationEvent(ThemeUpdatedEvent event) {
cacheStore.delete(ThemeService.THEMES_CACHE_KEY);
}
}

View File

@ -30,14 +30,7 @@ public interface OptionService extends CrudService<Option, Integer> {
int DEFAULT_RSS_PAGE_SIZE = 20;
/**
* Save one option
*
* @param key key must not be blank
* @param value value
*/
@Transactional
void save(@NonNull String key, String value);
String OPTIONS_KEY = "options";
/**
* Save multiple options

View File

@ -19,7 +19,6 @@ import run.halo.app.model.params.OptionParam;
import run.halo.app.model.properties.*;
import run.halo.app.repository.OptionRepository;
import run.halo.app.service.OptionService;
import run.halo.app.service.ThemeService;
import run.halo.app.service.base.AbstractCrudService;
import run.halo.app.utils.HaloUtils;
import run.halo.app.utils.ServiceUtils;
@ -60,8 +59,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
propertyEnumMap = Collections.unmodifiableMap(PropertyEnum.getValuePropertyEnumMap());
}
@Override
public void save(String key, String value) {
private void save(String key, String value) {
Assert.hasText(key, "Option key must not be blank");
if (StringUtils.isBlank(value)) {
@ -90,8 +88,6 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
Option savedOption = optionRepository.save(option);
log.debug("Saved option: [{}]", savedOption);
cacheStore.delete(ThemeService.THEMES_CACHE_KEY);
}
@Override
@ -103,7 +99,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
// TODO Optimize the queries
options.forEach(this::save);
publishEvent();
publishOptionUpdatedEvent();
}
@Override
@ -115,7 +111,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
// TODO Optimize the query
optionParams.forEach(optionParam -> save(optionParam.getKey(), optionParam.getValue()));
publishEvent();
publishOptionUpdatedEvent();
}
@Override
@ -124,7 +120,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
save(property.getValue(), value);
publishEvent();
publishOptionUpdatedEvent();
}
@Override
@ -135,44 +131,51 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
properties.forEach((property, value) -> save(property.getValue(), value));
publishEvent();
publishOptionUpdatedEvent();
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Object> listOptions() {
List<Option> options = listAll();
// Get options from cache
return cacheStore.getAny(OPTIONS_KEY, Map.class).orElseGet(() -> {
List<Option> options = listAll();
Set<String> keys = ServiceUtils.fetchProperty(options, Option::getKey);
Set<String> keys = ServiceUtils.fetchProperty(options, Option::getKey);
Map<String, Object> userDefinedOptionMap = ServiceUtils.convertToMap(options, Option::getKey, option -> {
String key = option.getKey();
Map<String, Object> userDefinedOptionMap = ServiceUtils.convertToMap(options, Option::getKey, option -> {
String key = option.getKey();
PropertyEnum propertyEnum = propertyEnumMap.get(key);
PropertyEnum propertyEnum = propertyEnumMap.get(key);
if (propertyEnum == null) {
return option.getValue();
}
if (propertyEnum == null) {
return option.getValue();
}
return PropertyEnum.convertTo(option.getValue(), propertyEnum);
return PropertyEnum.convertTo(option.getValue(), propertyEnum);
});
Map<String, Object> result = new HashMap<>(userDefinedOptionMap);
// Add default property
propertyEnumMap.keySet()
.stream()
.filter(key -> !keys.contains(key))
.forEach(key -> {
PropertyEnum propertyEnum = propertyEnumMap.get(key);
if (StringUtils.isBlank(propertyEnum.defaultValue())) {
return;
}
result.put(key, PropertyEnum.convertTo(propertyEnum.defaultValue(), propertyEnum));
});
// Cache the result
cacheStore.putAny(OPTIONS_KEY, result);
return result;
});
Map<String, Object> result = new HashMap<>(userDefinedOptionMap);
// Add default property
propertyEnumMap.keySet()
.stream()
.filter(key -> !keys.contains(key))
.forEach(key -> {
PropertyEnum propertyEnum = propertyEnumMap.get(key);
if (StringUtils.isBlank(propertyEnum.defaultValue())) {
return;
}
result.put(key, PropertyEnum.convertTo(propertyEnum.defaultValue(), propertyEnum));
});
return result;
}
@Override
@ -357,7 +360,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
return blogUrl;
}
private void publishEvent() {
private void publishOptionUpdatedEvent() {
eventPublisher.publishEvent(new OptionUpdatedEvent(this));
}
}

View File

@ -22,6 +22,7 @@ import org.springframework.web.multipart.MultipartFile;
import run.halo.app.cache.StringCacheStore;
import run.halo.app.config.properties.HaloProperties;
import run.halo.app.event.theme.ThemeActivatedEvent;
import run.halo.app.event.theme.ThemeUpdatedEvent;
import run.halo.app.exception.*;
import run.halo.app.handler.theme.config.ThemeConfigResolver;
import run.halo.app.handler.theme.config.ThemePropertyResolver;
@ -237,7 +238,7 @@ public class ThemeServiceImpl implements ThemeService {
FileUtil.del(Paths.get(themeProperty.getThemePath()));
// Delete theme cache
clearThemeCache();
eventPublisher.publishEvent(new ThemeUpdatedEvent(this));
} catch (Exception e) {
throw new ServiceException("Failed to delete theme folder", e).setErrorData(themeId);
}
@ -329,7 +330,7 @@ public class ThemeServiceImpl implements ThemeService {
setActivatedTheme(themeProperty);
// Clear the cache
clearThemeCache();
eventPublisher.publishEvent(new ThemeUpdatedEvent(this));
// Publish a theme activated event
eventPublisher.publishEvent(new ThemeActivatedEvent(this));
@ -402,7 +403,7 @@ public class ThemeServiceImpl implements ThemeService {
ThemeProperty property = getProperty(targetThemePath);
// Clear theme cache
clearThemeCache();
this.eventPublisher.publishEvent(new ThemeUpdatedEvent(this));
// Delete cache
return property;
@ -496,13 +497,6 @@ public class ThemeServiceImpl implements ThemeService {
return Files.createTempDirectory("halo");
}
/**
* Clears theme cache.
*/
private void clearThemeCache() {
cacheStore.delete(THEMES_CACHE_KEY);
}
/**
* Sets activated theme.
*