package cc.concurrent.config.client;
import cc.concurrent.config.client.loader.Loader;
import cc.concurrent.config.client.loader.LocalLoader;
import cc.concurrent.config.client.loader.RemoteLoader;
import cc.concurrent.config.core.model.CheckParam;
import cc.concurrent.config.core.model.FilePublished;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* User: yanghe.liang
* Date: 13-11-2
* Time: 下午6:24
*/
public class Config {
private final static Logger logger = LoggerFactory.getLogger(Config.class);
private final RemoteLoader remoteLoader;
private final LocalLoader localLoader;
private final boolean isRemoteFirst;
private final long remoteCheckInterval;
private final ConcurrentHashMap<Class<?>, BeanDesc<?>> cache = new ConcurrentHashMap<Class<?>, BeanDesc<?>>();
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
public Config(String appName, String remoteUrl, int remoteCheckInterval, String localDir) {
this(appName, remoteUrl, remoteCheckInterval, localDir, false);
}
public Config(String appName, String remoteUrl, int remoteCheckInterval, String localDir, boolean isRemoteFirst) {
this.remoteLoader = new RemoteLoader(appName, remoteUrl, localDir);
this.remoteCheckInterval = remoteCheckInterval;
this.localLoader = new LocalLoader(appName, localDir);
this.isRemoteFirst = isRemoteFirst;
}
public void start(Class<?> ... clazzs) {
try {
remoteLoader.init();
localLoader.init();
for (Class<?> clazz : clazzs) {
getBean(clazz, true);
}
startMonitor();
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new IllegalStateException(e);
}
}
public <T> T getBean(Class<T> clazz) {
return getBean(clazz, isRemoteFirst);
}
@SuppressWarnings("unchecked")
public <T> T getBean(Class<T> clazz, boolean isRemoteFirst) {
checkNotNull(clazz);
BeanDesc<?> beanDesc = cache.get(clazz);
if (beanDesc == null) {
synchronized (clazz.getName().intern()) {
beanDesc = cache.get(clazz);
if (beanDesc == null) {
beanDesc = loadBeanDesc(clazz, isRemoteFirst);
if (beanDesc != null) {
cache.put(clazz, beanDesc);
}
}
}
}
checkNotNull(beanDesc);
return (T) beanDesc.getBean();
}
private <T> BeanDesc<T> loadBeanDesc(Class<T> clazz, boolean isRemoteFirst) {
Root root = clazz.getAnnotation(Root.class);
checkNotNull(root, "class %s must have annotation %s", clazz.getName(), Root.class.getName());
String fileName = root.name();
List<Loader> loaders = isRemoteFirst ?
Lists.newArrayList(remoteLoader, localLoader) :
Lists.newArrayList(localLoader, remoteLoader);
for (Loader loader : loaders) {
try {
return loader.load(fileName, clazz);
} catch (IllegalStateException e) {
logger.error(e.getMessage());
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
return null;
}
private void startMonitor() {
scheduler.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
List<CheckParam> checkParams = Lists.newArrayList();
Set<Map.Entry<Class<?>, BeanDesc<?>>> entries = cache.entrySet();
Map<String, Class<?>> classMap = Maps.newHashMap(); // fileName对应的Class
for (Map.Entry<Class<?>, BeanDesc<?>> entry : entries) {
BeanDesc<?> beanDesc = entry.getValue();
checkParams.add(new CheckParam(beanDesc.getFileName(), beanDesc.getMd5()));
classMap.put(beanDesc.getFileName(), beanDesc.getBean().getClass());
}
if (logger.isDebugEnabled()) {
logger.debug("checkParams=" + checkParams);
}
List<FilePublished> changes = remoteLoader.checkAndDownload(checkParams);
if (logger.isDebugEnabled()) {
logger.debug("changes=" + changes);
}
for (FilePublished change : changes) {
String fileName = change.getFileName();
String xml = change.getXml();
String md5 = change.getMd5();
Class<?> clazz = classMap.get(fileName);
Serializer serializer = new Persister();
Object bean = serializer.read(clazz, change.getXml(), false);
BeanDesc<?> beanDesc = new BeanDesc<Object>(fileName, md5, bean);
cache.replace(clazz, beanDesc); // 更新cache
remoteLoader.cacheXml(fileName, xml); // 缓存到本地
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}, remoteCheckInterval, remoteCheckInterval, TimeUnit.SECONDS);
}
}