package trader; import java.io.File; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DriverManager; import java.util.Arrays; import java.util.List; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.sql.DataSource; import org.eclipse.jetty.util.thread.ExecutorThreadPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.server.ErrorPage; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.Primary; import org.springframework.http.HttpStatus; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.ResourceHttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.google.gson.GsonBuilder; import springfox.documentation.swagger2.annotations.EnableSwagger2; import trader.common.config.ConfigUtil; import trader.common.util.TraderHomeUtil; import trader.service.node.NodeMgmtService; import trader.service.node.NodeService; import trader.service.node.NodeServiceImpl; @Configuration @EnableScheduling @EnableAsync @EnableSwagger2 @ComponentScan( value={ "trader" }, excludeFilters= { @Filter(type = FilterType.ASSIGNABLE_TYPE, value=NodeMgmtService.class) } ) public class TraderMainConfiguration implements WebMvcConfigurer, SchedulingConfigurer, AsyncConfigurer, AsyncUncaughtExceptionHandler { private final static Logger logger = LoggerFactory.getLogger(TraderMainConfiguration.class); private ScheduledThreadPoolExecutor taskScheduler; private ThreadPoolExecutor asyncExecutor; public TraderMainConfiguration() { createThreadPools(); } @Bean public ConfigurableServletWebServerFactory webServerFactory() { JettyServletWebServerFactory factory = new JettyServletWebServerFactory(); int port = ConfigUtil.getInt("/BasisService/web.httpPort", 10080); factory.setPort(port); factory.setContextPath(""); factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html")); factory.setSelectors(1); factory.setAcceptors(1); factory.setThreadPool(new ExecutorThreadPool(executorService())); return factory; } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new StringHttpMessageConverter()); converters.add(new FormHttpMessageConverter()); GsonHttpMessageConverter c = new GsonHttpMessageConverter(); c.setGson(new GsonBuilder().disableHtmlEscaping().create()); converters.add(c); converters.add(new ResourceHttpMessageConverter()); } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return this; } @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { logger.error("Async invocation on "+method+" "+Arrays.asList(params)+"failed: "+ex.toString(), ex); } @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(taskScheduler); } @Bean public NodeService nodeService() { return new NodeServiceImpl(); } @Primary @Bean public java.util.concurrent.ThreadPoolExecutor executorService(){ return asyncExecutor; } @Bean public java.util.concurrent.ScheduledExecutorService scheduledExecutorService(){ return taskScheduler; } @Bean(name="dataSource") public DataSource dataSource() throws Exception { //先试着用remote方式连接 String url = detectRepositoryURL(); String usr = "sa"; String pwd = ""; if ( url==null ) { logger.error("Connect to repository database failed"); throw new Exception("Connect to repository database failed"); } logger.info("Connect to H2 repository database in "+(url.indexOf("tcp")>0?"remote":"local")+" mode: "+url); DataSource ds = DataSourceBuilder.create() .driverClassName("org.h2.Driver") .url(url) .username(usr) .password(pwd) .build(); ; return ds; } private void createThreadPools() { taskScheduler = new ScheduledThreadPoolExecutor(3, new DefaultThreadFactory("TaskScheduler")); asyncExecutor = new ThreadPoolExecutor(3, Integer.MAX_VALUE, 60 ,TimeUnit.SECONDS, new SynchronousQueue<Runnable>(false), new DefaultThreadFactory("async")); asyncExecutor.allowCoreThreadTimeOut(true); } /** * 探测H2行情数据库的连接URL */ private static String detectRepositoryURL() { String addr = ConfigUtil.getString0("/BasisService/h2db.addr", "127.0.0.1"); int tcpPort = ConfigUtil.getInt("/BasisService/h2db.tcpPort", 9092); boolean autoServer = ConfigUtil.getBoolean("/BasisService/h2db.autoServer", true); String url = "jdbc:h2:tcp://"+addr+":"+tcpPort+"/repository;IFEXISTS=FALSE"; String usr = "sa"; String pwd = ""; try { Class.forName("org.h2.Driver"); Connection conn = DriverManager.getConnection(url, usr, pwd); conn.close(); }catch(Throwable t) { url = null; if ( autoServer ) { File h2db = new File( TraderHomeUtil.getTraderHome(), "data/h2db"); url = "jdbc:h2:"+h2db.getAbsolutePath()+"/repository;IFEXISTS=FALSE;AUTO_SERVER=TRUE;AUTO_SERVER_PORT="+tcpPort; } } return url; } } class DefaultThreadFactory implements ThreadFactory { public static class PoolThreadGroup extends ThreadGroup{ private static final Logger logger = LoggerFactory.getLogger(PoolThreadGroup.class); public PoolThreadGroup(String name) { super(name); } @Override public void uncaughtException(Thread t, Throwable e) { logger.error("Thread "+t+" uncaught exception: "+e, e); } } private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private int priority; private String poolName; public DefaultThreadFactory(String poolName){ this(poolName, Thread.NORM_PRIORITY); } public DefaultThreadFactory(String poolName, int priority){ this.priority = priority; this.poolName = poolName; SecurityManager s = System.getSecurityManager(); //group = (s != null) ? s.getThreadGroup() : // Thread.currentThread().getThreadGroup(); group = new PoolThreadGroup(poolName); } @Override public Thread newThread(Runnable r) { Thread t = new Thread(group, r, poolName + "-" + threadNumber.getAndIncrement(), 0); t.setDaemon(true); t.setPriority(priority); return t; } }