package org.fastcatsearch.http;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import org.fastcatsearch.alert.ClusterAlertService;
import org.fastcatsearch.common.ThreadPoolFactory;
import org.fastcatsearch.env.Environment;
import org.fastcatsearch.exception.FastcatSearchException;
import org.fastcatsearch.http.action.AuthAction;
import org.fastcatsearch.http.action.HttpAction;
import org.fastcatsearch.http.action.ServiceAction;
import org.fastcatsearch.service.AbstractService;
import org.fastcatsearch.service.ServiceManager;
import org.fastcatsearch.settings.Settings;
import org.fastcatsearch.util.ClassScanner;
import org.fastcatsearch.util.DynamicClassLoader;
import org.jboss.netty.handler.codec.http.HttpRequest;
public class HttpRequestService extends AbstractService implements HttpServerAdapter {
private HttpTransportModule transportModule;
private HttpServiceController serviceController;
private HttpSessionManager httpSessionManager;
public HttpRequestService(Environment environment, Settings settings, ServiceManager serviceManager) throws FastcatSearchException {
super(environment, settings, serviceManager);
}
@Override
protected boolean doStart() throws FastcatSearchException {
int servicePort = environment.settingManager().getIdSettings().getInt("servicePort");
transportModule = new HttpTransportModule(environment, settings, servicePort);
transportModule.httpServerAdapter(this);
ExecutorService executorService = ThreadPoolFactory.newCachedDaemonThreadPool("http-execute-pool", settings.getInt("execute_pool_size", 300));
httpSessionManager = new HttpSessionManager(settings.getInt("session_expire_hour", 1));
serviceController = new HttpServiceController(executorService, httpSessionManager);
if (!transportModule.load()) {
throw new FastcatSearchException("can not load transport module!");
}
Map<String, HttpAction> actionMap = new HashMap<String, HttpAction>();
if (environment.isMasterNode()) {
// master노드에서만 실행되는 액션들 등록..
String[] actionBasePackageList = settings.getStringArray("master-action-base-package", ",");
if (actionBasePackageList != null) {
scanActions(actionMap, actionBasePackageList);
}
}
String[] actionBasePackageList = settings.getStringArray("action-base-package", ",");
if (actionBasePackageList != null) {
scanActions(actionMap, actionBasePackageList);
}
serviceController.setActionMap(actionMap);
return true;
}
private void scanActions(Map<String, HttpAction> actionMap, String[] actionBasePackageList) {
for (String actionBasePackage : actionBasePackageList) {
scanActions(actionMap, actionBasePackage);
}
}
// 하위패키지까지 모두 포함되도록 한다.
private void scanActions(final Map<String, HttpAction> actionMap, String actionBasePackage) {
ClassScanner<HttpAction> scanner = new ClassScanner<HttpAction>() {
@Override
public HttpAction done(String ename, String pkg, Object param) {
registerAction(actionMap, ename);
return null;
}
};
try {
scanner.scanClass(actionBasePackage, null);
} catch (IOException e) {
logger.error("", e);
ClusterAlertService.getInstance().alert(e);
}
}
public void registerAction(String className, String pathPrefix) {
registerAction(serviceController.getActionMap(), className, pathPrefix);
}
private void registerAction(Map<String, HttpAction> actionMap, String className) {
registerAction(actionMap, className, null);
}
private void registerAction(Map<String, HttpAction> actionMap, String className, String pathPrefix) {
if(className == null){
logger.warn("Cannot register action class name >> {} : {}", className, pathPrefix);
return;
}
try {
Class<?> actionClass = DynamicClassLoader.loadClass(className);
// logger.debug("className > {} => {}",className , actionClass);
// actionClass 가 serviceAction을 상속받은 경우만 등록.
if (actionClass != null) {
if (ServiceAction.class.isAssignableFrom(actionClass)) {
HttpAction actionObj = null;
ActionMapping actionMapping = actionClass.getAnnotation(ActionMapping.class);
// annotation이 존재할 경우만 사용.
if (actionMapping != null) {
String path = actionMapping.value();
if(pathPrefix != null){
path = pathPrefix + path;
}
ActionMethod[] method = actionMapping.method();
actionObj = (HttpAction) actionClass.newInstance();
if (actionObj != null) {
actionObj.setMethod(method);
actionObj.setEnvironement(environment);
// 권한 필요한 액션일 경우.
if (actionObj instanceof AuthAction) {
AuthAction authAction = (AuthAction) actionObj;
authAction.setAuthority(actionMapping.authority(), actionMapping.authorityLevel());
logger.debug("ACTION path={}, action={}, method={}, authority={}, authorityLevel={}", path, actionObj, method, actionMapping.authority(),
actionMapping.authorityLevel());
}else{
logger.debug("ACTION path={}, action={}, method={}", path, actionObj, method);
}
actionMap.put(path, actionObj);
}
}
}
}
} catch (InstantiationException e) {
logger.error("action load error! " + className, e);
} catch (IllegalAccessException e) {
logger.error("action load error! " + className, e);
}
}
@Override
protected boolean doStop() throws FastcatSearchException {
transportModule.doUnload();
httpSessionManager.close();
return true;
}
@Override
protected boolean doClose() throws FastcatSearchException {
serviceController = null;
transportModule = null;
httpSessionManager = null;
return true;
}
@Override
public void dispatchRequest(HttpRequest request, HttpChannel channel) {
serviceController.dispatchRequest(request, channel);
}
}