mirror of https://github.com/halo-dev/halo
Add custom temporary store support
parent
58e5da4ac5
commit
5771b42c25
8
pom.xml
8
pom.xml
|
@ -48,6 +48,7 @@
|
|||
<qiniu-java-sdk.version>7.2.18</qiniu-java-sdk.version>
|
||||
<thumbnailator.version>0.4.8</thumbnailator.version>
|
||||
<commonmark.version>0.12.1</commonmark.version>
|
||||
<commons-lang3.version>3.8.1</commons-lang3.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -213,6 +214,13 @@
|
|||
<version>2.9.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache commons lang3-->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package cc.ryanc.halo.cache;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Cache store interface.
|
||||
*
|
||||
* @param <K> cache key type
|
||||
* @param <V> cache value type
|
||||
* @author johnniang
|
||||
* *
|
||||
*/
|
||||
public interface CacheStore<K, V> {
|
||||
|
||||
/**
|
||||
* Gets by cache key.
|
||||
*
|
||||
* @param key must not be null
|
||||
* @return cache value
|
||||
*/
|
||||
@NonNull
|
||||
Optional<V> get(@NonNull K key);
|
||||
|
||||
/**
|
||||
* Puts a cache.
|
||||
*
|
||||
* @param key cache key must not be null
|
||||
* @param value cache value must not be null
|
||||
* @param timeout the key expiration must not be less than 0
|
||||
* @param timeUnit timeout unit
|
||||
*/
|
||||
void put(@NonNull K key, @NonNull V value, long timeout, @NonNull TimeUnit timeUnit);
|
||||
|
||||
/**
|
||||
* Delete a key.
|
||||
*
|
||||
* @param key cache key must not be null
|
||||
*/
|
||||
void delete(@NonNull K key);
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package cc.ryanc.halo.cache;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Cache wrapper.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CacheWrapper<T> {
|
||||
|
||||
/**
|
||||
* Cache key.
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* Cache data
|
||||
*/
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* Expired time.
|
||||
*/
|
||||
private Date expireAt;
|
||||
|
||||
/**
|
||||
* Create time.
|
||||
*/
|
||||
private Date createAt;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package cc.ryanc.halo.cache;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* In-memory cache store.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
public class InMemoryCacheStore extends StringCacheStore {
|
||||
|
||||
private final static ConcurrentHashMap<String, String> cacheContainer = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Optional<String> get(String key) {
|
||||
return Optional.ofNullable(cacheContainer.get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, String value, long timeout, TimeUnit timeUnit) {
|
||||
cacheContainer.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
// TODO Consider to delete the cache periodic
|
||||
cacheContainer.remove(key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package cc.ryanc.halo.cache;
|
||||
|
||||
import cc.ryanc.halo.exception.ServiceException;
|
||||
import cc.ryanc.halo.utils.JsonUtils;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Wechat cache store.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class StringCacheStore implements CacheStore<String, String> {
|
||||
|
||||
public <T> void putForWechat(String key, T value, long timeout, TimeUnit timeUnit) {
|
||||
Assert.hasText(key, "Cache key must not be blank");
|
||||
Assert.notNull(value, "Cache value must not be null");
|
||||
Assert.isTrue(timeout > 0, "Timeout must not be less than 0");
|
||||
Assert.notNull(timeUnit, "Time unit must not be null");
|
||||
|
||||
// Convert to second
|
||||
Long seconds = timeUnit.toSeconds(timeout);
|
||||
|
||||
// Round the seconds
|
||||
if (seconds == 0) {
|
||||
seconds = 1L;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
|
||||
// Calculate expire at
|
||||
Date expireAt = DateUtils.addSeconds(now, seconds.intValue());
|
||||
|
||||
// Build cache wrapper
|
||||
CacheWrapper<T> wrapper = new CacheWrapper<>();
|
||||
wrapper.setCreateAt(now);
|
||||
wrapper.setExpireAt(expireAt);
|
||||
wrapper.setKey(key);
|
||||
wrapper.setData(value);
|
||||
|
||||
try {
|
||||
// Convert wrapper to json
|
||||
String valueJson = JsonUtils.objectToJson(wrapper);
|
||||
// Put the the value json to cache store
|
||||
put(key, valueJson, timeout, timeUnit);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new ServiceException("Failed to convert object to json", e).setErrorData(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NonNull
|
||||
public <T> Optional<T> getForWechat(@NonNull String key, @NonNull Class<T> type) {
|
||||
Assert.hasText(key, "Cache key must not be blank");
|
||||
Assert.notNull(type, "Cache type must not be null");
|
||||
|
||||
return get(key).map(value -> {
|
||||
try {
|
||||
CacheWrapper<?> cacheWrapper = JsonUtils.jsonToObject(value, CacheWrapper.class);
|
||||
|
||||
if (cacheWrapper == null) {
|
||||
log.error("Cache wrapper is null, key: [{}]", key);
|
||||
return null;
|
||||
}
|
||||
|
||||
log.debug("Cache wrapper: [{}]", cacheWrapper);
|
||||
|
||||
Date now = new Date();
|
||||
|
||||
if (cacheWrapper.getExpireAt().before(now)) {
|
||||
// Expired then delete it
|
||||
log.debug("Cache key: [{}] has been expired", key);
|
||||
|
||||
delete(key);
|
||||
return null;
|
||||
}
|
||||
|
||||
Object data = cacheWrapper.getData();
|
||||
|
||||
if (data != null && data.getClass().isAssignableFrom(type)) {
|
||||
return (T) data;
|
||||
}
|
||||
|
||||
log.error("Data type: [{}], but specified type: [{}]", data == null ? null : data.getClass(), type);
|
||||
throw new ServiceException("Cache value type is mismatched with the specified type");
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("Failed to convert from json to object", e).setErrorData(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package cc.ryanc.halo.config;
|
||||
|
||||
import cc.ryanc.halo.cache.InMemoryCacheStore;
|
||||
import cc.ryanc.halo.cache.StringCacheStore;
|
||||
import cc.ryanc.halo.config.properties.HaloProperties;
|
||||
import cc.ryanc.halo.filter.CorsFilter;
|
||||
import cc.ryanc.halo.filter.LogFilter;
|
||||
|
@ -8,6 +10,7 @@ import cc.ryanc.halo.security.filter.ApiAuthenticationFilter;
|
|||
import cc.ryanc.halo.security.handler.AdminAuthenticationFailureHandler;
|
||||
import cc.ryanc.halo.security.handler.DefaultAuthenticationFailureHandler;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -23,6 +26,12 @@ import org.springframework.core.Ordered;
|
|||
@EnableConfigurationProperties(HaloProperties.class)
|
||||
public class HaloConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public StringCacheStore stringCacheStore() {
|
||||
return new InMemoryCacheStore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CorsFilter.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue