package cn.com.qmth.examcloud.web.bootstrap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.ThreadContext; import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpStatus; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cn.com.qmth.examcloud.commons.exception.ExamCloudRuntimeException; import cn.com.qmth.examcloud.commons.logging.ExamCloudLog; import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory; import cn.com.qmth.examcloud.commons.util.HttpMethod; import cn.com.qmth.examcloud.commons.util.JsonUtil; import cn.com.qmth.examcloud.commons.util.OKHttpUtil; import cn.com.qmth.examcloud.commons.util.PropertiesUtil; import cn.com.qmth.examcloud.commons.util.StringUtil; import cn.com.qmth.examcloud.commons.util.Util; import cn.com.qmth.examcloud.web.cloud.AppSelf; import cn.com.qmth.examcloud.web.cloud.AppSelfHolder; import okhttp3.Response; /** * 系统启动器 * * @author WANGWEI * @date 2019年3月25日 * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved. */ public class AppBootstrap { private static ExamCloudLog log = ExamCloudLogFactory.getLog(AppBootstrap.class); private static Map properties; private static String active; private static String startupCode; private static Long appId; private static String secretKey; private static String appCode; private static String appSimpleName; private static String configCenterHost; private static String configCenterPort; /** * 启动方法 * * @author WANGWEI * @param primarySource * @param args * @return */ public static synchronized ConfigurableApplicationContext run(Class primarySource, String... args) { ThreadContext.put("TRACE_ID", Thread.currentThread().getName()); log.info("Starting..."); checkBootstrapParams(args); sendStartupRequest2ConfigCenter(); if (null != args) { for (String arg : args) { arg = arg.trim(); if (arg.startsWith("--")) { String key = arg.substring(2, arg.indexOf("=")).trim(); String value = arg.substring(arg.indexOf("=") + 1).trim(); properties.put(key, value); } } } // 网卡选择 String preferredNetworks = properties.get("examcloud.inet.preferredNetworks"); if (StringUtils.isNotBlank(preferredNetworks)) { System.setProperty("spring.cloud.inetutils.preferred-networks", preferredNetworks); properties.put("spring.cloud.inetutils.preferred-networks", preferredNetworks); } properties.put("spring.profiles.active", active); Set argSet = Sets.newLinkedHashSet(); for (Entry p : properties.entrySet()) { String key = p.getKey(); String value = p.getValue(); if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) { continue; } key = key.trim(); value = value.trim(); PropertyHolder.setProperty(p.getKey(), p.getValue()); String arg = "--" + key + "=" + value; argSet.add(arg); } AppSelfHolder.set(buildAppSelf()); String[] newArgs = argSet.toArray(new String[argSet.size()]); ConfigurableApplicationContext context = null; try { context = SpringApplication.run(primarySource, newArgs); noticeConfigCenter("success"); } catch (Exception e) { String stackTrace = Util.getStackTrace(e); noticeConfigCenter("failure:\n" + stackTrace); log.error("fail to run spring app.", e); System.exit(-1); } return context; } /** * 创建 {@link AppSelf} 实例 * * @author WANGWEI * @return */ private static AppSelf buildAppSelf() { return new AppSelf() { @Override public Long getAppId() { return appId; } @Override public String getAppCode() { return appCode; } @Override public String getSecretKey() { return secretKey; } }; } /** * 通知配置中心 * * @author WANGWEI * @param message */ private static void noticeConfigCenter(String message) { String url = "http://" + configCenterHost + ":" + configCenterPort + "/configCenter/notice"; Map req = Maps.newHashMap(); req.put("active", active); req.put("appSimpleName", appSimpleName); req.put("startupCode", startupCode); req.put("message", message); Response resp = null; try { Map headers = Maps.newHashMap(); headers.put("Trace-Id", startupCode); resp = OKHttpUtil.call(HttpMethod.POST, url.toString(), headers, req); } catch (Exception e) { log.error("fail to notice config center.", e); System.exit(-1); } finally { IOUtils.closeQuietly(resp); } } /** * 向配置中心发送启动请求 * * @author WANGWEI */ private static void sendStartupRequest2ConfigCenter() { StringBuilder url = new StringBuilder( "http://" + configCenterHost + ":" + configCenterPort + "/configCenter/startup"); Map req = Maps.newHashMap(); req.put("active", active); req.put("appSimpleName", appSimpleName); req.put("startupCode", startupCode); Response resp = null; try { Map headers = Maps.newHashMap(); headers.put("Trace-Id", startupCode); resp = OKHttpUtil.call(HttpMethod.POST, url.toString(), headers, req); if (resp.code() != HttpStatus.OK.value()) { throw new ExamCloudRuntimeException("fail to get configuration"); } String body = resp.body().string(); String encryptedAppId = resp.header("App-Id"); String encryptedAppCode = resp.header("App-Code"); String encryptedSecretKey = resp.header("Secret-Key"); appId = StringUtil.toLong(BootstrapSecurityUtil.decrypt(encryptedAppId, startupCode)); appCode = BootstrapSecurityUtil.decrypt(encryptedAppCode, startupCode); secretKey = BootstrapSecurityUtil.decrypt(encryptedSecretKey, startupCode); String decryptedBody = BootstrapSecurityUtil.decrypt(body, startupCode); @SuppressWarnings("unchecked") Map map = JsonUtil.fromJson(decryptedBody, Map.class); properties = map; } catch (Exception e) { log.error("fail to send startup request to config center.", e); System.exit(-1); } finally { IOUtils.closeQuietly(resp); } } /** * 检查启动参数 * * @author WANGWEI * @param args * @return */ private static void checkBootstrapParams(String... args) { Properties props = new Properties(); PropertiesUtil.loadFromResource("application.properties", props); if (null != args) { for (String s : args) { s = s.trim(); if (s.startsWith("--spring.profiles.active=")) { active = s.substring(s.indexOf("=") + 1); } else if (s.startsWith("--examcloud.startup.startupCode=")) { startupCode = s.substring(s.indexOf("=") + 1); } else if (s.startsWith("--examcloud.startup.configCenterHost=")) { configCenterHost = s.substring(s.indexOf("=") + 1); } else if (s.startsWith("--examcloud.startup.configCenterPort=")) { configCenterPort = s.substring(s.indexOf("=") + 1); } else if (s.startsWith("--examcloud.startup.appSimpleName=")) { appSimpleName = s.substring(s.indexOf("=") + 1); } } } // active if (null == active) { String value = props.getProperty("spring.profiles.active"); if (StringUtils.isNotBlank(value)) { active = value.trim(); } } log.info("active=" + active); if (StringUtils.isBlank(active)) { log.error("property[spring.profiles.active] is not specified"); System.exit(-1); } // startupCode if (null == startupCode) { String value = props.getProperty("examcloud.startup.startupCode"); if (StringUtils.isNotBlank(value)) { startupCode = value.trim(); } } log.info("startupCode=" + startupCode); if (StringUtils.isBlank(startupCode)) { log.error("property[examcloud.startup.startupCode] is not specified"); System.exit(-1); } // appSimpleName if (null == appSimpleName) { String value = props.getProperty("examcloud.startup.appSimpleName"); if (StringUtils.isNotBlank(value)) { appSimpleName = value.trim(); } } log.info("appSimpleName=" + appSimpleName); if (StringUtils.isBlank(appSimpleName)) { log.error("property[examcloud.startup.appSimpleName] is not specified"); System.exit(-1); } // configCenterHost if (null == configCenterHost) { String value = props.getProperty("examcloud.startup.configCenterHost"); if (StringUtils.isNotBlank(value)) { configCenterHost = value.trim(); } } log.info("configCenterHost=" + configCenterHost); if (StringUtils.isBlank(configCenterHost)) { log.error("property[examcloud.startup.configCenterHost] is not specified"); System.exit(-1); } // configCenterPort if (null == configCenterPort) { String value = props.getProperty("examcloud.startup.configCenterPort"); if (StringUtils.isNotBlank(value)) { configCenterPort = value.trim(); } } log.info("configCenterPort=" + configCenterPort); if (null == configCenterPort) { log.error("property[examcloud.startup.configCenterPort] is not specified"); System.exit(-1); } } }