content: /*
   *  Copyright Beijing 58 Information Technology Co.,Ltd.
   *
   *  Licensed to the Apache Software Foundation (ASF) under one
   *  or more contributor license agreements.  See the NOTICE file
   *  distributed with this work for additional information
   *  regarding copyright ownership.  The ASF licenses this file
   *  to you under the Apache License, Version 2.0 (the
   *  "License"); you may not use this file except in compliance
   *  with the License.  You may obtain a copy of the License at
   *
   *        http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing,
   *  software distributed under the License is distributed on an
   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   *  KIND, either express or implied.  See the License for the
   *  specific language governing permissions and limitations
   *  under the License.
   */
  package com.bj58.spat.gaea.server.bootstrap;
  
  import java.io.File;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import sun.misc.Signal;
  
  import com.bj58.spat.gaea.server.bootstrap.signal.OperateSignal;
  import com.bj58.spat.gaea.server.contract.context.Global;
  import com.bj58.spat.gaea.server.contract.context.IProxyFactory;
  import com.bj58.spat.gaea.server.contract.context.ServiceConfig;
  import com.bj58.spat.gaea.server.contract.filter.IFilter;
  import com.bj58.spat.gaea.server.contract.init.IInit;
  import com.bj58.spat.gaea.server.contract.log.ILog;
  import com.bj58.spat.gaea.server.contract.log.Log4jConfig;
  import com.bj58.spat.gaea.server.contract.log.LogFactory;
  import com.bj58.spat.gaea.server.contract.log.SystemPrintStream;
  import com.bj58.spat.gaea.server.contract.server.IServer;
  import com.bj58.spat.gaea.server.deploy.filemonitor.FileMonitor;
  import com.bj58.spat.gaea.server.deploy.filemonitor.HotDeployListener;
  import com.bj58.spat.gaea.server.deploy.filemonitor.NotifyCount;
  import com.bj58.spat.gaea.server.deploy.hotdeploy.DynamicClassLoader;
  import com.bj58.spat.gaea.server.deploy.hotdeploy.GlobalClassLoader;
  import com.bj58.spat.gaea.server.deploy.hotdeploy.ProxyFactoryLoader;
  
  /**
   * serive frame entry
   * 
   * @author Service Platform Architecture Team (spat@58.com)
   */
  public class Main {
  
  	private static ILog logger = null;
  	
  	/**
  	 * start server
  	 * @param args : service name
  	 * @throws Exception
  	 */
  	public static void main(String[] args) throws Exception {
  		if (args.length < 1) {
  			throw new IllegalArgumentException("usage: -Dgaea.service.name=<service-name> [<other-gaea-config>]");
  		}
  
  		String userDir = System.getProperty("user.dir");
  		String rootPath = userDir + "/../";
  		String serviceName = null;
  		Map<String, String> argsMap = new HashMap<String, String>();
  		Global.getSingleton().setRootPath(rootPath);
  		
  		for(int i=0; i<args.length; i++) {
  			if(args[i].startsWith("-D")) {
  				String[] aryArg = args[i].split("=");
  				if(aryArg.length == 2) {
  					if(aryArg[0].equalsIgnoreCase("-Dgaea.service.name")) {
  						serviceName = aryArg[1];
  					}
  					argsMap.put(aryArg[0].replaceFirst("-D", ""), aryArg[1]);
  				}
  			}
  		}
  		
  		if(serviceName == null){
  			throw new Exception("no service name please set it");
  		}
  		
  		String serviceFolderPath = rootPath + "service/deploy/" + serviceName;
  		String gaeaConfigDefaultPath = rootPath + "conf/gaea_config.xml";
  		String gaeaConfigPath = serviceFolderPath + "/gaea_config.xml";
  		String log4jConfigDefaultPath = rootPath + "conf/gaea_log4j.xml";
  		String log4jConfigPath = serviceFolderPath + "/gaea_log4j.xml";
  		
  		// load log4j
  		loadLog4jConfig(log4jConfigPath, log4jConfigDefaultPath);
  		logger = LogFactory.getLogger(Main.class);
  		
  		logger.info("+++++++++++++++++++++ staring +++++++++++++++++++++\n");
  		
  		logger.info("user.dir: " + userDir);
  		logger.info("rootPath: " + rootPath);
  		logger.info("service gaea_config.xml: " + gaeaConfigPath);
  		logger.info("default gaea_config.xml: " + gaeaConfigDefaultPath);
  		logger.info("service gaea_log4j.xml: " + log4jConfigPath);
  		logger.info("default gaea_log4j.xml: " + log4jConfigDefaultPath);
  		
  		// load service config
  		logger.info("load service config...");
  		ServiceConfig sc = loadServiceConfig(gaeaConfigDefaultPath, gaeaConfigPath);
  		Set<String> keySet = argsMap.keySet();
  		for(String key : keySet) {
  			logger.info(key + ": " + argsMap.get(key));
  			sc.set(key, argsMap.get(key));
  		}
  		if(sc.getString("gaea.service.name") == null || sc.getString("gaea.service.name").equalsIgnoreCase("")) {
  			logger.info("gaea.service.name:" + serviceName);
  			sc.set("gaea.service.name", serviceName);
  		}
  		Global.getSingleton().setServiceConfig(sc);
  
  		
  		// init class loader
  		logger.info("-----------------loading global jars------------------");
  		DynamicClassLoader classLoader = new DynamicClassLoader();
  		classLoader.addFolder(
  				rootPath + "service/deploy/" + sc.getString("gaea.service.name") + "/",
  				rootPath + "service/lib/",
  				rootPath + "lib"
  				);
  		
  		GlobalClassLoader.addSystemClassPathFolder(
  				rootPath + "service/deploy/" + sc.getString("gaea.service.name") + "/",
  				rootPath + "service/lib/",
  				rootPath + "lib"
  				);
  		logger.info("-------------------------end-------------------------\n");
  
  		if(new File(serviceFolderPath).isDirectory() || !serviceName.equalsIgnoreCase("error_service_name_is_null")) {
  			// load proxy factory
  			logger.info("--------------------loading proxys-------------------");
  			IProxyFactory proxyFactory = ProxyFactoryLoader.loadProxyFactory(classLoader);
  			Global.getSingleton().setProxyFactory(proxyFactory);
  			logger.info("-------------------------end-------------------------\n");
  			
  			// load init beans
  			logger.info("-----------------loading init beans------------------");
  			loadInitBeans(classLoader, sc);
  			logger.info("-------------------------end-------------------------\n");
  		}
  		
  		// load global request-filters
  		logger.info("-----------loading global request filters------------");
  		List<IFilter> requestFilters = loadFilters(classLoader, sc, "gaea.filter.global.request");
  		for(IFilter filter : requestFilters) {
  			Global.getSingleton().addGlobalRequestFilter(filter);
  		}
  		logger.info("-------------------------end-------------------------\n");
  		
  		// load global response-filters
  		logger.info("-----------loading global response filters-----------");
  		List<IFilter> responseFilters = loadFilters(classLoader, sc, "gaea.filter.global.response");
  		for(IFilter filter : responseFilters) {
  			Global.getSingleton().addGlobalResponseFilter(filter);
  		}
  		logger.info("-------------------------end-------------------------\n");
  		
  		// load connection filters
  		logger.info("-----------loading connection filters-----------");
  		List<IFilter> connFilters = loadFilters(classLoader, sc, "gaea.filter.connection");
  		for(IFilter filter : connFilters) {
  			Global.getSingleton().addConnectionFilter(filter);
  		}
  		logger.info("-------------------------end-------------------------\n");
  		
  		// load secureKey 当gaea.secure不为true时不启动权限认证
  		logger.info("------------------load secureKey start---------------------");
  		if(sc.getString("gaea.secure") != null && "true".equalsIgnoreCase(sc.getString("gaea.secure"))) {
  			logger.info("gaea.secure:" + sc.getString("gaea.secure"));
  			loadSecureKey(sc,serviceFolderPath);
  		}
  		logger.info("------------------load secureKey end----------------------\n");
  		
  		//注册信号 linux下支持USR2
  		logger.info("------------------signal registr start---------------------");
  		String osName = System.getProperty("os.name").toLowerCase();
  		if(osName != null && osName.indexOf("window") == -1){
  			OperateSignal operateSignalHandler = new OperateSignal();
  			Signal sig = new Signal("USR2");
  			Signal.handle(sig, operateSignalHandler);
  		}
  		logger.info("------------------signal registr success----------------------\n");
  		
  		// load servers
  		logger.info("------------------ starting servers -----------------");
  		loadServers(classLoader, sc);
  		logger.info("-------------------------end-------------------------\n");
  		
  		// add current service file to monitor
  		if(sc.getBoolean("gaea.hotdeploy")) {
  			logger.info("------------------init file monitor-----------------");
  			addFileMonitor(rootPath, sc.getString("gaea.service.name"));
  			logger.info("-------------------------end-------------------------\n");
  		}
  		
  		try {
  			registerExcetEven();
  		} catch (Exception e) {
  			logger.error("registerExcetEven error", e);
  			System.exit(0);
  		}
  		
  		logger.info("+++++++++++++++++++++ server start success!!! +++++++++++++++++++++\n");
  		while (true) {
  			Thread.sleep(1 * 60 * 60 * 1000);
  		}
  	}
  	
  	
  	/**
  	 * load service config
  	 * @param cps
  	 * @return
  	 * @throws Exception
  	 */
  	private static ServiceConfig loadServiceConfig(String... cps) throws Exception {
  		ServiceConfig sc = ServiceConfig.getServiceConfig(cps);
  		if(sc == null) {
  			logger.error("ServiceConfig sc is null");
  		}
  		
  		return sc;
  	}
  	
  	/**
  	 * 
  	 * @param configPath
  	 * @param logFilePath
  	 * @throws Exception
  	 */
  	private static void loadLog4jConfig(String configPath, String defaultPath) throws Exception {
  		File fLog4jConfig = new File(configPath);
  		if(fLog4jConfig.exists()) {
  			Log4jConfig.configure(configPath);
  		} else {
  			Log4jConfig.configure(defaultPath);
  		}
  		SystemPrintStream.redirectToLog4j();
  	}
  	
  	/**
  	 * 
  	 * @param classLoader
  	 * @param sc
  	 * @throws Exception
  	 */
  	private static void loadInitBeans(DynamicClassLoader classLoader, ServiceConfig sc) throws Exception{
  		List<String> initList = sc.getList("gaea.init", ",");
  		if(initList != null) {
  			for(String initBeans : initList) {
  				try {
  					logger.info("load: " + initBeans);
  					IInit initBean = (IInit)classLoader.loadClass(initBeans).newInstance();
  					Global.getSingleton().addInit(initBean);
  					initBean.init();
  				} catch(Exception e) {
  					logger.error("init " + initBeans + " error!!!", e);
  				}
  			}
  		}
  	}
  	
  	/**
  	 * 加载授权文件方法
  	 * @param sc
  	 * @param key
  	 * @param serverName
  	 * @throws Exception
  	 */
  	private static void loadSecureKey(ServiceConfig sc, String path) throws Exception{
  		File[] file = new File(path).listFiles();
  		for(File f : file){	
  			String fName = f.getName();
  			if(!f.exists() || fName.indexOf("secure") < 0 || !"xml".equalsIgnoreCase(fName.substring(fName.lastIndexOf(".")+1))){
  				continue;
  			}
  			sc.getSecureConfig(f.getPath());
  		}
  	}
  	
  	
  	/**
  	 * 
  	 * @param classLoader
  	 * @param sc
  	 * @param key
  	 * @throws Exception
  	 */
  	private static List<IFilter> loadFilters(DynamicClassLoader classLoader, ServiceConfig sc, String key) throws Exception {
  		List<String> filterList = sc.getList(key, ",");
  		List<IFilter> instanceList = new ArrayList<IFilter>();
  		if(filterList != null) {
  			for(String filterName : filterList) {
  				try {
  					logger.info("load: " + filterName);
  					IFilter filter = (IFilter)classLoader.loadClass(filterName).newInstance();
  					instanceList.add(filter);
  				} catch(Exception e) {
  					logger.error("load " + filterName + " error!!!", e);
  				}
  			}
  		}
  		
  		return instanceList;
  	}
  	
  	/**
  	 * 
  	 * @param classLoader
  	 * @param sc
  	 * @throws Exception
  	 */
  	private static void loadServers(DynamicClassLoader classLoader, ServiceConfig sc) throws Exception {
  		List<String> servers = sc.getList("gaea.servers", ",");
  		if(servers != null) {
  			for(String server : servers) {
  				try {
  					if(sc.getBoolean(server + ".enable")) {
  						logger.info(server + " is starting...");
  						IServer serverImpl = (IServer) classLoader.loadClass(sc.getString(server + ".implement")).newInstance();
  						Global.getSingleton().addServer(serverImpl);
  						serverImpl.start();
  						logger.info(server + "started success!!!\n");
  					}
  				} catch(Exception ex) {
  					
  				}
  			}
  		}
  	}
  	
  	/**
  	 * add current service file to file monitor
  	 * @param config
  	 * @throws Exception 
  	 */
  	private static void addFileMonitor(String rootPath, String serviceName) throws Exception {
  		FileMonitor.getInstance().addMonitorFile(rootPath + 
  												"service/deploy/" + 
  												serviceName + 
  												"/");
  		
  		FileMonitor.getInstance().setInterval(5000);
  		FileMonitor.getInstance().setNotifyCount(NotifyCount.Once);
  		FileMonitor.getInstance().addListener(new HotDeployListener());
  		FileMonitor.getInstance().start();
  	}
  	
  	/**
  	 * when shutdown server destroyed all socket connection
  	 */
  	private static void registerExcetEven() {
  		Runtime.getRuntime().addShutdownHook(new Thread() {
  			public void run() {
  				for(IServer server : Global.getSingleton().getServerList()) {
  					try {
  						server.stop();
  					} catch (Exception e) {
  						logger.error("stop server error", e);
  					}
  				}
  
  				try {
  					super.finalize();
  				} catch (Throwable e) {
  					logger.error("super.finalize() error when stop server", e);
  				}
  			}
  		});
  	}
  }