mirror of https://github.com/halo-dev/halo
Add option cache feature
parent
6e8ef4b35a
commit
cea6a55963
|
@ -38,21 +38,17 @@ public class FreemarkerConfigAwareListener {
|
||||||
|
|
||||||
private final ThemeSettingService themeSettingService;
|
private final ThemeSettingService themeSettingService;
|
||||||
|
|
||||||
private final OptionService optionsService;
|
|
||||||
|
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
|
||||||
public FreemarkerConfigAwareListener(OptionService optionService,
|
public FreemarkerConfigAwareListener(OptionService optionService,
|
||||||
Configuration configuration,
|
Configuration configuration,
|
||||||
ThemeService themeService,
|
ThemeService themeService,
|
||||||
ThemeSettingService themeSettingService,
|
ThemeSettingService themeSettingService,
|
||||||
OptionService optionsService,
|
|
||||||
UserService userService) {
|
UserService userService) {
|
||||||
this.optionService = optionService;
|
this.optionService = optionService;
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.themeService = themeService;
|
this.themeService = themeService;
|
||||||
this.themeSettingService = themeSettingService;
|
this.themeSettingService = themeSettingService;
|
||||||
this.optionsService = optionsService;
|
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,13 +93,11 @@ public class FreemarkerConfigAwareListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadOptionsConfig() throws TemplateModelException {
|
private void loadOptionsConfig() throws TemplateModelException {
|
||||||
Map<String, Object> options = optionService.listOptions();
|
configuration.setSharedVariable("options", optionService.listOptions());
|
||||||
configuration.setSharedVariable("options", options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadThemeConfig() throws TemplateModelException {
|
private void loadThemeConfig() throws TemplateModelException {
|
||||||
ThemeProperty activatedTheme = themeService.getActivatedTheme();
|
configuration.setSharedVariable("theme", themeService.getActivatedTheme());
|
||||||
configuration.setSharedVariable("theme", activatedTheme);
|
|
||||||
configuration.setSharedVariable("settings", themeSettingService.listAsMapBy(themeService.getActivatedThemeId()));
|
configuration.setSharedVariable("settings", themeSettingService.listAsMapBy(themeService.getActivatedThemeId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,14 +30,7 @@ public interface OptionService extends CrudService<Option, Integer> {
|
||||||
|
|
||||||
int DEFAULT_RSS_PAGE_SIZE = 20;
|
int DEFAULT_RSS_PAGE_SIZE = 20;
|
||||||
|
|
||||||
/**
|
String OPTIONS_KEY = "options";
|
||||||
* Save one option
|
|
||||||
*
|
|
||||||
* @param key key must not be blank
|
|
||||||
* @param value value
|
|
||||||
*/
|
|
||||||
@Transactional
|
|
||||||
void save(@NonNull String key, String value);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save multiple options
|
* Save multiple options
|
||||||
|
|
|
@ -19,7 +19,6 @@ import run.halo.app.model.params.OptionParam;
|
||||||
import run.halo.app.model.properties.*;
|
import run.halo.app.model.properties.*;
|
||||||
import run.halo.app.repository.OptionRepository;
|
import run.halo.app.repository.OptionRepository;
|
||||||
import run.halo.app.service.OptionService;
|
import run.halo.app.service.OptionService;
|
||||||
import run.halo.app.service.ThemeService;
|
|
||||||
import run.halo.app.service.base.AbstractCrudService;
|
import run.halo.app.service.base.AbstractCrudService;
|
||||||
import run.halo.app.utils.HaloUtils;
|
import run.halo.app.utils.HaloUtils;
|
||||||
import run.halo.app.utils.ServiceUtils;
|
import run.halo.app.utils.ServiceUtils;
|
||||||
|
@ -60,8 +59,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
propertyEnumMap = Collections.unmodifiableMap(PropertyEnum.getValuePropertyEnumMap());
|
propertyEnumMap = Collections.unmodifiableMap(PropertyEnum.getValuePropertyEnumMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void save(String key, String value) {
|
||||||
public void save(String key, String value) {
|
|
||||||
Assert.hasText(key, "Option key must not be blank");
|
Assert.hasText(key, "Option key must not be blank");
|
||||||
|
|
||||||
if (StringUtils.isBlank(value)) {
|
if (StringUtils.isBlank(value)) {
|
||||||
|
@ -90,8 +88,6 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
Option savedOption = optionRepository.save(option);
|
Option savedOption = optionRepository.save(option);
|
||||||
|
|
||||||
log.debug("Saved option: [{}]", savedOption);
|
log.debug("Saved option: [{}]", savedOption);
|
||||||
|
|
||||||
cacheStore.delete(ThemeService.THEMES_CACHE_KEY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -103,7 +99,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
// TODO Optimize the queries
|
// TODO Optimize the queries
|
||||||
options.forEach(this::save);
|
options.forEach(this::save);
|
||||||
|
|
||||||
publishEvent();
|
publishOptionUpdatedEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -115,7 +111,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
// TODO Optimize the query
|
// TODO Optimize the query
|
||||||
optionParams.forEach(optionParam -> save(optionParam.getKey(), optionParam.getValue()));
|
optionParams.forEach(optionParam -> save(optionParam.getKey(), optionParam.getValue()));
|
||||||
|
|
||||||
publishEvent();
|
publishOptionUpdatedEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -124,7 +120,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
|
|
||||||
save(property.getValue(), value);
|
save(property.getValue(), value);
|
||||||
|
|
||||||
publishEvent();
|
publishOptionUpdatedEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -135,44 +131,51 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
|
|
||||||
properties.forEach((property, value) -> save(property.getValue(), value));
|
properties.forEach((property, value) -> save(property.getValue(), value));
|
||||||
|
|
||||||
publishEvent();
|
publishOptionUpdatedEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public Map<String, Object> listOptions() {
|
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 -> {
|
Map<String, Object> userDefinedOptionMap = ServiceUtils.convertToMap(options, Option::getKey, option -> {
|
||||||
String key = option.getKey();
|
String key = option.getKey();
|
||||||
|
|
||||||
PropertyEnum propertyEnum = propertyEnumMap.get(key);
|
PropertyEnum propertyEnum = propertyEnumMap.get(key);
|
||||||
|
|
||||||
if (propertyEnum == null) {
|
if (propertyEnum == null) {
|
||||||
return option.getValue();
|
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
|
@Override
|
||||||
|
@ -357,7 +360,7 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
||||||
return blogUrl;
|
return blogUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publishEvent() {
|
private void publishOptionUpdatedEvent() {
|
||||||
eventPublisher.publishEvent(new OptionUpdatedEvent(this));
|
eventPublisher.publishEvent(new OptionUpdatedEvent(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
import run.halo.app.cache.StringCacheStore;
|
import run.halo.app.cache.StringCacheStore;
|
||||||
import run.halo.app.config.properties.HaloProperties;
|
import run.halo.app.config.properties.HaloProperties;
|
||||||
import run.halo.app.event.theme.ThemeActivatedEvent;
|
import run.halo.app.event.theme.ThemeActivatedEvent;
|
||||||
|
import run.halo.app.event.theme.ThemeUpdatedEvent;
|
||||||
import run.halo.app.exception.*;
|
import run.halo.app.exception.*;
|
||||||
import run.halo.app.handler.theme.config.ThemeConfigResolver;
|
import run.halo.app.handler.theme.config.ThemeConfigResolver;
|
||||||
import run.halo.app.handler.theme.config.ThemePropertyResolver;
|
import run.halo.app.handler.theme.config.ThemePropertyResolver;
|
||||||
|
@ -237,7 +238,7 @@ public class ThemeServiceImpl implements ThemeService {
|
||||||
FileUtil.del(Paths.get(themeProperty.getThemePath()));
|
FileUtil.del(Paths.get(themeProperty.getThemePath()));
|
||||||
|
|
||||||
// Delete theme cache
|
// Delete theme cache
|
||||||
clearThemeCache();
|
eventPublisher.publishEvent(new ThemeUpdatedEvent(this));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ServiceException("Failed to delete theme folder", e).setErrorData(themeId);
|
throw new ServiceException("Failed to delete theme folder", e).setErrorData(themeId);
|
||||||
}
|
}
|
||||||
|
@ -329,7 +330,7 @@ public class ThemeServiceImpl implements ThemeService {
|
||||||
setActivatedTheme(themeProperty);
|
setActivatedTheme(themeProperty);
|
||||||
|
|
||||||
// Clear the cache
|
// Clear the cache
|
||||||
clearThemeCache();
|
eventPublisher.publishEvent(new ThemeUpdatedEvent(this));
|
||||||
|
|
||||||
// Publish a theme activated event
|
// Publish a theme activated event
|
||||||
eventPublisher.publishEvent(new ThemeActivatedEvent(this));
|
eventPublisher.publishEvent(new ThemeActivatedEvent(this));
|
||||||
|
@ -402,7 +403,7 @@ public class ThemeServiceImpl implements ThemeService {
|
||||||
ThemeProperty property = getProperty(targetThemePath);
|
ThemeProperty property = getProperty(targetThemePath);
|
||||||
|
|
||||||
// Clear theme cache
|
// Clear theme cache
|
||||||
clearThemeCache();
|
this.eventPublisher.publishEvent(new ThemeUpdatedEvent(this));
|
||||||
|
|
||||||
// Delete cache
|
// Delete cache
|
||||||
return property;
|
return property;
|
||||||
|
@ -496,13 +497,6 @@ public class ThemeServiceImpl implements ThemeService {
|
||||||
return Files.createTempDirectory("halo");
|
return Files.createTempDirectory("halo");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears theme cache.
|
|
||||||
*/
|
|
||||||
private void clearThemeCache() {
|
|
||||||
cacheStore.delete(THEMES_CACHE_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets activated theme.
|
* Sets activated theme.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue