Outbound Client oAuth Token 기능 개선 및 패치
- 광주은행 내용 적용 - ignite 사용
This commit is contained in:
@@ -13,15 +13,21 @@ import com.eactive.eai.common.lifecycle.Lifecycle;
|
||||
import com.eactive.eai.common.lifecycle.LifecycleException;
|
||||
import com.eactive.eai.common.lifecycle.LifecycleListener;
|
||||
import com.eactive.eai.common.lifecycle.LifecycleSupport;
|
||||
import com.eactive.eai.common.session.SessionManager;
|
||||
import com.eactive.eai.common.session.SessionManagerForIgnite;
|
||||
import com.eactive.eai.common.util.ApplicationContextProvider;
|
||||
import com.eactive.eai.common.util.Logger;
|
||||
import com.mchange.v1.cachedstore.CachedStore.Manager;
|
||||
import com.openbanking.eai.common.token.AccessTokenVO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 1. 기능 : Access 토큰 정보를 DB로 부터 로딩하고 만료시 재발급 정보를 메모리에 관리하는 Manager 클래스
|
||||
@@ -55,13 +61,8 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
/**
|
||||
* 기동 여부
|
||||
*/
|
||||
private boolean started;
|
||||
|
||||
/**
|
||||
* 토큰 정보를 저장하는 collection
|
||||
*/
|
||||
private HashMap<String, AccessTokenVO> tokens;
|
||||
|
||||
private boolean started;
|
||||
|
||||
/**
|
||||
* 인증 정보를 저장하는 Map
|
||||
*/
|
||||
@@ -78,7 +79,6 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
private static final int TOKEN_SCHEDULER_THREAD_POOL_SIZE = 30;
|
||||
|
||||
public AccessTokenManagerByDB() {
|
||||
tokens = new HashMap<>();
|
||||
scheduledTasks = new ConcurrentHashMap<>();
|
||||
outboundOAuthCredentialVos = new HashMap<>();
|
||||
runningTasks = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
@@ -117,7 +117,6 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
lifecycle.fireLifecycleEvent(STARTING_EVENT, this);
|
||||
|
||||
try {
|
||||
tokens.clear();
|
||||
init();
|
||||
} catch(Exception e) {
|
||||
throw new LifecycleException(ExceptionUtil.getErrorCode(e,"RECEAICPM201"));
|
||||
@@ -168,7 +167,6 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
logger.error("Scheduler shutdown interrupted", e);
|
||||
}
|
||||
|
||||
this.tokens = null;
|
||||
started = false;
|
||||
|
||||
if (logger.isWarn()){
|
||||
@@ -240,27 +238,34 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
try {
|
||||
logger.debug("Executing token issuance for adapter group: {}", adapterGroupName);
|
||||
|
||||
// 현재 토큰 가져오기
|
||||
AccessTokenVO accessToken = tokens.get(adapterGroupName);
|
||||
SessionManager.getInstance().getOutboundAccessToken(adapterGroupName, new Function<AccessTokenVO, AccessTokenVO>() {
|
||||
|
||||
// 다음 스케줄 시간 계산
|
||||
long intervalTime = System.currentTimeMillis() + (credential.getIntervalSec() * 1000);
|
||||
// 토큰이 없거나, 다음 스케줄 시간 전에 만료될 경우 재발급
|
||||
if (accessToken == null) {
|
||||
issueToken(credential, false);
|
||||
} else if(accessToken.getExpiration() != null) {
|
||||
// 토큰이 있고 만료시간이 설정 된 경우
|
||||
if(accessToken.getExpiration().before(new Date(intervalTime))){
|
||||
// 다음 스케줄 전에 토큰이 만료되는 경우 재발급
|
||||
issueToken(credential, true);
|
||||
@Override
|
||||
public AccessTokenVO apply(AccessTokenVO accessToken) {
|
||||
|
||||
long intervalTime = System.currentTimeMillis() + (credential.getIntervalSec() * 1000);
|
||||
// // 토큰이 없거나, 다음 스케줄 시간 전에 만료될 경우 재발급
|
||||
if (accessToken == null) {
|
||||
return issueToken(credential);
|
||||
} else if(accessToken.getExpiration() != null) {
|
||||
// 토큰이 있고 만료시간이 설정 된 경우
|
||||
if(accessToken.getExpiration().before(new Date(intervalTime))){
|
||||
// 다음 스케줄 전에 토큰이 만료되는 경우 재발급
|
||||
return issueToken(credential);
|
||||
} else {
|
||||
// 토큰이 아직 유효한 경우
|
||||
logger.debug("Token still valid until next schedule for adapter group: {}, expiration date: {}", adapterGroupName, accessToken.getExpiration());
|
||||
return accessToken;
|
||||
}
|
||||
} else {
|
||||
// 토큰이 아직 유효한 경우
|
||||
logger.debug("Token still valid until next schedule for adapter group: {}, expiration date: {}", adapterGroupName, accessToken.getExpiration());
|
||||
//토큰이 있지만 만료 시간이 없는 경우
|
||||
logger.debug("Token exists but expiration time is null for adapter group: {}", adapterGroupName);
|
||||
return accessToken;
|
||||
}
|
||||
} else {
|
||||
//토큰이 있지만 만료 시간이 없는 경우
|
||||
logger.debug("Token exists but expiration time is null for adapter group: {}", adapterGroupName);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
logger.debug("Token issuance completed for adapter group: {}", adapterGroupName);
|
||||
} catch (Exception e) {
|
||||
@@ -282,19 +287,24 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
}
|
||||
}
|
||||
|
||||
private void issueToken(OutboundOAuthCredentialVo outboundOAuthCredentialVo, boolean isTokenExpiredWithinInterval) throws Exception {
|
||||
String adapterGroupName = outboundOAuthCredentialVo.getAdapterGroupName();
|
||||
boolean isExpired = true;
|
||||
private AccessTokenVO issueToken(OutboundOAuthCredentialVo outboundOAuthCredentialVo) {
|
||||
String adapterGroupName = outboundOAuthCredentialVo.getAdapterGroupName();
|
||||
AccessTokenVO accessToken = null;
|
||||
try {
|
||||
|
||||
// boolean isExpired = true;
|
||||
|
||||
// if (tokens.containsKey(adapterGroupName)) {
|
||||
// AccessTokenVO accessToken = tokens.get(adapterGroupName);
|
||||
// isExpired = accessToken.isExpired();
|
||||
// }
|
||||
// logger.debug("Token status for adapter group: {}, expired: {}, isTokenExpiredWithinInterval: {}", adapterGroupName, isExpired, isTokenExpiredWithinInterval);
|
||||
|
||||
if (tokens.containsKey(adapterGroupName)) {
|
||||
AccessTokenVO accessToken = tokens.get(adapterGroupName);
|
||||
isExpired = accessToken.isExpired();
|
||||
}
|
||||
logger.debug("Token status for adapter group: {}, expired: {}, isTokenExpiredWithinInterval: {}", adapterGroupName, isExpired, isTokenExpiredWithinInterval);
|
||||
|
||||
if (isExpired || isTokenExpiredWithinInterval) {
|
||||
// if (isExpired || isTokenExpiredWithinInterval) {
|
||||
|
||||
|
||||
logger.info("Issuing new token for adapter group: {}", adapterGroupName);
|
||||
tokens.remove(adapterGroupName);
|
||||
// tokens.remove(adapterGroupName);
|
||||
|
||||
AdapterGroupVO gvo = AdapterManager.getInstance().getAdapterGroupVO(adapterGroupName);
|
||||
|
||||
@@ -306,16 +316,23 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
HttpClientAccessTokenServiceByDB service = HttpClientAccessTokenServiceFactoryByDB.createFactory(type);
|
||||
if(service != null) {
|
||||
logger.debug("Executing token service of type: {} for adapter group: {}", type, adapterGroupName);
|
||||
AccessTokenVO accessToken = (AccessTokenVO) service.execute(adapterGroupName, properties, outboundOAuthCredentialVo);
|
||||
this.tokens.put(adapterGroupName, accessToken);
|
||||
accessToken = (AccessTokenVO) service.execute(adapterGroupName, properties, outboundOAuthCredentialVo);
|
||||
logger.info("New token issued successfully for adapter group: {}", adapterGroupName);
|
||||
return accessToken;
|
||||
|
||||
} else {
|
||||
logger.warn("Token service not found for type: {} and adapter group: {}", type, adapterGroupName);
|
||||
}
|
||||
} else {
|
||||
logger.warn("No valid adapter configuration found for adapter group: {}", adapterGroupName);
|
||||
}
|
||||
}
|
||||
|
||||
}catch (Exception e) {
|
||||
logger.error("Task execution failed for adapter group: {}", adapterGroupName, e);
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
|
||||
}
|
||||
|
||||
public void stopToken(String adapterGroupName) {
|
||||
@@ -324,7 +341,7 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
task.cancel(true);
|
||||
logger.warn("Token task stopped for adapter group: {}", adapterGroupName);
|
||||
}
|
||||
tokens.remove(adapterGroupName);
|
||||
SessionManager.getInstance().removeOutboundAccessToken(adapterGroupName);
|
||||
logger.debug("Token removed for adapter group: {}", adapterGroupName);
|
||||
}
|
||||
|
||||
@@ -382,15 +399,28 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
* @return Access 토큰 정보
|
||||
**/
|
||||
public AccessTokenVO getAccessTokenVO(String adapterGroupName) {
|
||||
AccessTokenVO accessToken = null;
|
||||
|
||||
try {
|
||||
accessToken = tokens.get(adapterGroupName);
|
||||
} catch(Exception e) {
|
||||
if (logger.isError()) logger.error("토큰을 찾을 수 없습니다. ["+adapterGroupName+"] ", e);
|
||||
}
|
||||
AccessTokenVO accessToken = null;
|
||||
|
||||
return accessToken;
|
||||
try {
|
||||
accessToken = SessionManager.getInstance().getOutboundAccessToken(adapterGroupName,
|
||||
new Function<AccessTokenVO, AccessTokenVO>() {
|
||||
|
||||
@Override
|
||||
public AccessTokenVO apply(AccessTokenVO t) {
|
||||
|
||||
OutboundOAuthCredentialVo outboundOAuthCredentialVo = outboundOAuthCredentialVos
|
||||
.get(adapterGroupName);
|
||||
|
||||
return issueToken(outboundOAuthCredentialVo);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
if (logger.isError())
|
||||
logger.error("토큰을 찾을 수 없습니다. [" + adapterGroupName + "] ", e);
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,9 +433,9 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
**/
|
||||
public String getAccessToken(String adapterGroupName) {
|
||||
String accessToken = null;
|
||||
|
||||
|
||||
try {
|
||||
accessToken = tokens.get(adapterGroupName).getAccessToken();
|
||||
accessToken = getAccessTokenVO(adapterGroupName).getAccessToken();
|
||||
} catch(Exception e) {
|
||||
if (logger.isError()) logger.error("토큰을 찾을 수 없습니다. ["+adapterGroupName+"] ", e);
|
||||
}
|
||||
@@ -435,27 +465,29 @@ public class AccessTokenManagerByDB implements Lifecycle {
|
||||
* 3. 주의사항
|
||||
*
|
||||
**/
|
||||
public synchronized AccessTokenVO retryAccessTokenVO(String adapterGroupName, Properties properties, String oldToken) throws Exception {
|
||||
|
||||
AccessTokenVO accessToken = getAccessTokenVO(adapterGroupName);
|
||||
public synchronized AccessTokenVO retryAccessTokenVO(String adapterGroupName, Properties properties,
|
||||
String oldToken) throws Exception {
|
||||
|
||||
// 동시 요청이 발생할 경우 synchronized 처리를 했기때문에 토큰을 다시 한번 체크한다.
|
||||
if (accessToken == null || accessToken.isExpired()
|
||||
|| StringUtils.equals(accessToken.getAccessToken(), oldToken)) {
|
||||
AccessTokenVO accessToken = getAccessTokenVO(adapterGroupName);
|
||||
|
||||
// 동시 요청이 발생할 경우 synchronized 처리를 했기때문에 토큰을 다시 한번 체크한다.
|
||||
if (accessToken == null || accessToken.isExpired()
|
||||
|| StringUtils.equals(accessToken.getAccessToken(), oldToken)) {
|
||||
|
||||
if (isOAuthCredentialRegistered(adapterGroupName) == false) {
|
||||
throw new Exception(
|
||||
"There is no OAuthCredentialRegistered information, or whether to use it is 'N'.");
|
||||
}
|
||||
String type = properties.getProperty("ADAPTER_TOKEN_ISSUING_CLIENT_TYPE");
|
||||
|
||||
HttpClientAccessTokenServiceByDB service = HttpClientAccessTokenServiceFactoryByDB.createFactory(type);
|
||||
OutboundOAuthCredentialVo outboundOAuthCredentialVo = outboundOAuthCredentialVos.get(adapterGroupName);
|
||||
if (service != null) {
|
||||
accessToken = (AccessTokenVO) service.execute(adapterGroupName, properties,
|
||||
outboundOAuthCredentialVo);
|
||||
}
|
||||
}
|
||||
|
||||
if(isOAuthCredentialRegistered(adapterGroupName) == false){
|
||||
throw new Exception("There is no OAuthCredentialRegistered information, or whether to use it is 'N'.");
|
||||
}
|
||||
String type = properties.getProperty("ADAPTER_TOKEN_ISSUING_CLIENT_TYPE");
|
||||
|
||||
HttpClientAccessTokenServiceByDB service = HttpClientAccessTokenServiceFactoryByDB.createFactory(type);
|
||||
OutboundOAuthCredentialVo outboundOAuthCredentialVo = outboundOAuthCredentialVos.get(adapterGroupName);
|
||||
if(service != null) {
|
||||
accessToken = (AccessTokenVO) service.execute(adapterGroupName, properties, outboundOAuthCredentialVo);
|
||||
}
|
||||
tokens.put(adapterGroupName, accessToken);
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.eactive.eai.common.session;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.ignite.IgniteCache;
|
||||
|
||||
import com.eactive.eai.authserver.service.BearerTokenInfo;
|
||||
import com.eactive.eai.common.lifecycle.Lifecycle;
|
||||
import com.eactive.eai.common.lifecycle.LifecycleException;
|
||||
@@ -11,6 +16,7 @@ import com.eactive.eai.common.server.EAIServerManager;
|
||||
import com.eactive.eai.common.server.EAIServerVO;
|
||||
import com.eactive.eai.common.sessioninfo.TerminalInfoVO;
|
||||
import com.eactive.eai.common.util.Logger;
|
||||
import com.openbanking.eai.common.token.AccessTokenVO;
|
||||
|
||||
/**
|
||||
* 1. 기능 :
|
||||
@@ -60,6 +66,7 @@ public abstract class SessionManager implements Lifecycle {
|
||||
public static final String WEBSOCKER_CAHCE_NAME = "webSocketTimeout";
|
||||
public static final String TERMINAL_CAHCE_NAME = "terminalSession";
|
||||
public static final String CAGATEWAY_TOKEN_CACHE_NAME = "CAGatewayToken";
|
||||
public static final String OUTBOUND_ACCESS_TOKEN_CACHE_NAME = "OutBoundAccessToken";
|
||||
|
||||
public static final String SESSION_MANAGER_GROUPNAME = "RMIInfo";
|
||||
public static final String SESSION_MANAGER_CACHETYPE = "cacheType";
|
||||
@@ -204,6 +211,13 @@ public abstract class SessionManager implements Lifecycle {
|
||||
|
||||
public abstract void removeCAToken(String key);
|
||||
|
||||
//Outbound Access Token Cache
|
||||
public abstract AccessTokenVO getOutboundAccessToken(String key, Function<AccessTokenVO, AccessTokenVO> getNewToken);
|
||||
|
||||
public abstract void removeOutboundAccessToken(String key);
|
||||
|
||||
public abstract void clearOutboundAccessToken();
|
||||
|
||||
|
||||
// HTTP Cache
|
||||
public abstract SessionVO getHttpLogin(String key);
|
||||
@@ -245,4 +259,5 @@ public abstract class SessionManager implements Lifecycle {
|
||||
public abstract void closeManager();
|
||||
|
||||
public abstract String getCacheStatus();
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.eactive.eai.common.session;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.eactive.eai.authserver.service.BearerTokenInfo;
|
||||
import com.eactive.eai.common.ehcache.CustomRMICacheReplicatorFactory;
|
||||
@@ -14,6 +16,7 @@ import com.eactive.eai.common.sessioninfo.TerminalInfoDAO;
|
||||
import com.eactive.eai.common.sessioninfo.TerminalInfoVO;
|
||||
import com.eactive.eai.common.util.ApplicationContextProvider;
|
||||
import com.eactive.eai.util.HealthCheckManager;
|
||||
import com.openbanking.eai.common.token.AccessTokenVO;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
@@ -937,4 +940,19 @@ public class SessionManagerForEhcache extends SessionManager {
|
||||
public void removeCAToken(String key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTokenVO getOutboundAccessToken(String key, Function<AccessTokenVO, AccessTokenVO> getNewToken) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOutboundAccessToken(String key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearOutboundAccessToken() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.cache.Cache;
|
||||
import javax.cache.configuration.Factory;
|
||||
@@ -54,6 +58,7 @@ import com.eactive.eai.common.util.ApplicationContextProvider;
|
||||
import com.eactive.eai.common.worker.Future;
|
||||
import com.eactive.eai.common.worker.ResponseMap;
|
||||
import com.eactive.eai.util.HealthCheckManager;
|
||||
import com.openbanking.eai.common.token.AccessTokenVO;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
|
||||
@@ -66,6 +71,8 @@ import ch.qos.logback.classic.Level;
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
public class SessionManagerForIgnite extends SessionManager {
|
||||
|
||||
private static final String DISTRIBUTED_TOKEN_LOCK = "DISTRIBUTED_TOKEN_LOCK";
|
||||
// evictMaster Instance - single socket 관련
|
||||
private static IgniteCache<String, String> evictMasterCache = null;
|
||||
// Socket Session cache 정보
|
||||
@@ -76,8 +83,11 @@ public class SessionManagerForIgnite extends SessionManager {
|
||||
private static IgniteCache<String, CacheStoreObject> cacheStore = null;
|
||||
// CA Gateway Token 관리용
|
||||
private static IgniteCache<String, BearerTokenInfo> cacheCAToken = null;
|
||||
// OutboundAccessToekn 관리용
|
||||
private static IgniteCache<String, AccessTokenVO> cacheOutBoundAccessToken = null;
|
||||
// webSocket Login cache 정보
|
||||
private static IgniteCache<String, SessionVO> cacheLogin = null; // key = loginId
|
||||
|
||||
private static IgniteCache<String, String> cacheConvertUserId = null; // 단말ID 를 userID로 변경
|
||||
// HTTP Polling 에 대한 timeout 관리용
|
||||
private static IgniteCache<String, SessionVO> httpLogin = null; // key == uuid
|
||||
@@ -392,6 +402,7 @@ public class SessionManagerForIgnite extends SessionManager {
|
||||
caches.add(initCache(config, sessionTimeout, bootstrapAsynchronously, USERID_CAHCE_NAME, 20000, false));
|
||||
caches.add(initCache(config, webSocketTimeout, bootstrapAsynchronously, WEBSOCKER_CAHCE_NAME, 20000, false));
|
||||
caches.add(initCache(config, sessionTimeout, bootstrapAsynchronously, TERMINAL_CAHCE_NAME, 20000, false));
|
||||
caches.add(initCache(config, 0, bootstrapAsynchronously, OUTBOUND_ACCESS_TOKEN_CACHE_NAME, 1000, false, true)); // transactionMode true
|
||||
caches.add(initCache(config, 0, bootstrapAsynchronously, CAGATEWAY_TOKEN_CACHE_NAME, 1000, false));
|
||||
|
||||
config.setCacheConfiguration(caches.stream().toArray(CacheConfiguration[]::new));
|
||||
@@ -421,6 +432,7 @@ public class SessionManagerForIgnite extends SessionManager {
|
||||
cacheTopic = manager.cache(TOPIC_CAHCE_NAME);
|
||||
cacheStore = manager.cache(STORE_CAHCE_NAME);
|
||||
cacheCAToken = manager.cache(CAGATEWAY_TOKEN_CACHE_NAME);
|
||||
cacheOutBoundAccessToken = manager.cache(OUTBOUND_ACCESS_TOKEN_CACHE_NAME);
|
||||
|
||||
cacheLogin = manager.cache(LOGIN_CAHCE_NAME);
|
||||
httpLogin = manager.cache(HTTP_CAHCE_NAME);
|
||||
@@ -428,49 +440,56 @@ public class SessionManagerForIgnite extends SessionManager {
|
||||
cacheWebSocketTimeout = manager.cache(WEBSOCKER_CAHCE_NAME);
|
||||
cacheTerminal = manager.cache(TERMINAL_CAHCE_NAME);
|
||||
}
|
||||
|
||||
private CacheConfiguration initCache(IgniteConfiguration config, int expiryDurationSecs,
|
||||
String bootstrapAsynchronously, String name, int maxsize, boolean useCacheWriteModeAsync,
|
||||
boolean transactionMode) {
|
||||
if (logger.isDebug()) {
|
||||
logger.debug(logPrefix + " create " + name + " cache expiryDurationSecs=" + expiryDurationSecs);
|
||||
}
|
||||
// near-cache
|
||||
NearCacheConfiguration nearConfiguration = new NearCacheConfiguration();
|
||||
LruEvictionPolicy nearEvictionPolicy = new LruEvictionPolicy();
|
||||
nearEvictionPolicy.setMaxSize(maxsize);
|
||||
nearConfiguration.setNearEvictionPolicy(nearEvictionPolicy);
|
||||
|
||||
CacheConfiguration cacheConfig = new CacheConfiguration(name);
|
||||
|
||||
cacheConfig.setCacheMode(CacheMode.PARTITIONED);
|
||||
|
||||
// ATOMIC,TRANSACTIONAL
|
||||
if (transactionMode) {
|
||||
cacheConfig.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
|
||||
} else {
|
||||
cacheConfig.setAtomicityMode(CacheAtomicityMode.ATOMIC);
|
||||
}
|
||||
|
||||
// Set the number of backups (replicas) for each partition : default(0)
|
||||
cacheConfig.setBackups(1);
|
||||
// Enable reading from backups to improve read consistency : default(true)
|
||||
cacheConfig.setReadFromBackup(true);
|
||||
|
||||
// FULL_SYNC/FULL_ASYNC/PRIMARY_SYNC(default)
|
||||
if (useCacheWriteModeAsync) {
|
||||
cacheConfig.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_ASYNC);
|
||||
} else {
|
||||
// cacheConfig.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
|
||||
cacheConfig.setWriteSynchronizationMode(CacheWriteSynchronizationMode.PRIMARY_SYNC);
|
||||
}
|
||||
|
||||
if (expiryDurationSecs > 0) {
|
||||
cacheConfig.setExpiryPolicyFactory(
|
||||
ModifiedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS, expiryDurationSecs)));
|
||||
} else {
|
||||
cacheConfig.setExpiryPolicyFactory(ModifiedExpiryPolicy.factoryOf(Duration.ETERNAL));
|
||||
}
|
||||
cacheConfig.setNearConfiguration(nearConfiguration);
|
||||
return cacheConfig;
|
||||
}
|
||||
|
||||
private CacheConfiguration initCache(IgniteConfiguration config, int expiryDurationSecs, String bootstrapAsynchronously,
|
||||
String name, int maxsize, boolean useCacheWriteModeAsync) {
|
||||
if (logger.isDebug()) {
|
||||
logger.debug(logPrefix + " create " + name + " cache expiryDurationSecs=" + expiryDurationSecs);
|
||||
}
|
||||
// near-cache
|
||||
NearCacheConfiguration nearConfiguration = new NearCacheConfiguration();
|
||||
LruEvictionPolicy nearEvictionPolicy = new LruEvictionPolicy();
|
||||
nearEvictionPolicy.setMaxSize(maxsize);
|
||||
nearConfiguration.setNearEvictionPolicy(nearEvictionPolicy);
|
||||
|
||||
CacheConfiguration cacheConfig = new CacheConfiguration(name);
|
||||
// REPLICATED,PARTITIONED(default)
|
||||
// cacheConfig.setCacheMode(CacheMode.REPLICATED);
|
||||
cacheConfig.setCacheMode(CacheMode.PARTITIONED);
|
||||
|
||||
// ATOMIC,TRANSACTIONAL
|
||||
cacheConfig.setAtomicityMode(CacheAtomicityMode.ATOMIC);
|
||||
|
||||
// Set the number of backups (replicas) for each partition : default(0)
|
||||
cacheConfig.setBackups(1);
|
||||
// Enable reading from backups to improve read consistency : default(true)
|
||||
cacheConfig.setReadFromBackup(true);
|
||||
|
||||
// FULL_SYNC/FULL_ASYNC/PRIMARY_SYNC(default)
|
||||
if (useCacheWriteModeAsync) {
|
||||
cacheConfig.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_ASYNC);
|
||||
} else {
|
||||
// cacheConfig.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
|
||||
cacheConfig.setWriteSynchronizationMode(CacheWriteSynchronizationMode.PRIMARY_SYNC);
|
||||
}
|
||||
|
||||
if(expiryDurationSecs > 0) {
|
||||
cacheConfig
|
||||
.setExpiryPolicyFactory(ModifiedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS, expiryDurationSecs)));
|
||||
}
|
||||
else {
|
||||
cacheConfig
|
||||
.setExpiryPolicyFactory( ModifiedExpiryPolicy.factoryOf(Duration.ETERNAL) );
|
||||
}
|
||||
cacheConfig.setNearConfiguration(nearConfiguration);
|
||||
return cacheConfig;
|
||||
return initCache(config, expiryDurationSecs, bootstrapAsynchronously, name, maxsize, useCacheWriteModeAsync, false);
|
||||
}
|
||||
|
||||
// WebSocket Login 관련
|
||||
@@ -868,5 +887,114 @@ public class SessionManagerForIgnite extends SessionManager {
|
||||
cacheCAToken.remove(key);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void sleepQuietly(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
// 인터럽트 상태 복구 (중요)
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
public AccessTokenVO getOutboundAccessToken(String key, Function<AccessTokenVO, AccessTokenVO> tokenSupplier) {
|
||||
// 1. 먼저 캐시에서 조회 (락 없이 빠르게)
|
||||
AccessTokenVO token = cacheOutBoundAccessToken.get(key);
|
||||
|
||||
// 2. 만료되었거나 없다면 락 획득 시도
|
||||
if (token == null || token.isExpired()) {
|
||||
Lock lock = cacheOutBoundAccessToken.lock(DISTRIBUTED_TOKEN_LOCK); // Ignite 분산 락
|
||||
|
||||
boolean acquired = false;
|
||||
|
||||
try {
|
||||
logger.debug("calling func getOutboundAccessToken = distributed ignite trylock");
|
||||
acquired = lock.tryLock(60, TimeUnit.SECONDS);
|
||||
|
||||
if ( acquired ) {
|
||||
logger.debug("calling func getOutboundAccessToken = distributed ignite trylock success=>"+acquired);
|
||||
// 3. 락 획득 후 다시 확인 (그 사이 다른 인스턴스가 갱신했을 수 있음)
|
||||
token = cacheOutBoundAccessToken.get(key);
|
||||
|
||||
logger.debug(String.format("getting token in ignite=%s", token));
|
||||
|
||||
if (token == null || token.isExpired()) {
|
||||
// 4. 실제로 비어있을 때만 외부 API 호출 (I/O 발생)
|
||||
token = tokenSupplier.apply(token);
|
||||
|
||||
logger.debug(String.format("getting token is null or expired borrow new token =%s", token));
|
||||
|
||||
cacheOutBoundAccessToken.put(key, token);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug("calling func getOutboundAccessToken = distributed ignite trylock fail=>"+acquired);
|
||||
}
|
||||
}catch(Throwable e) {
|
||||
logger.error("occuring exception in getOutboundAccessToken", e);
|
||||
}finally {
|
||||
|
||||
try {
|
||||
if( acquired ) {
|
||||
lock.unlock();
|
||||
logger.debug("calling func getOutboundAccessToken = distributed ignite unlock");
|
||||
}
|
||||
}catch(Throwable e) {
|
||||
logger.error("occuring exception in getOutboundAccessToken unlock fail.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void removeOutboundAccessToken(String key) {
|
||||
|
||||
Lock lock = cacheOutBoundAccessToken.lock(DISTRIBUTED_TOKEN_LOCK); // Ignite 분산 락
|
||||
|
||||
boolean acquired = false;
|
||||
try {
|
||||
|
||||
logger.debug("calling func removeOutboundAccessToken = distributed ignite trylock");
|
||||
acquired = lock.tryLock(60, TimeUnit.SECONDS);
|
||||
|
||||
if( acquired ) {
|
||||
if (logger.isDebug()) {
|
||||
|
||||
logger.debug("calling func getOutboundAccessToken = distributed ignite trylock success=>"
|
||||
+ acquired);
|
||||
|
||||
logger.debug("calling func removeOutboundAccessToken Remove OutBoundAccessToken key - " + key);
|
||||
}
|
||||
cacheOutBoundAccessToken.remove(key);
|
||||
}else {
|
||||
logger.debug("calling func getOutboundAccessToken = distributed ignite trylock fail=>"+acquired);
|
||||
}
|
||||
|
||||
|
||||
} catch (Throwable e) {
|
||||
logger.error("occuring exception in removeOutboundAccessToken", e);
|
||||
} finally {
|
||||
|
||||
try {
|
||||
if (acquired) {
|
||||
lock.unlock();
|
||||
logger.debug("calling func removeOutboundAccessToken = distributed ignite unlock");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.error("occuring exception in removeOutboundAccessToken unlock fail.", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearOutboundAccessToken() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user