Repository: alibaba/tac Branch: master Commit: 80f8f6126ed6 Files: 225 Total size: 1.6 MB Directory structure: gitextract_shwv98fg/ ├── .gitignore ├── LICENSE ├── README-ch.md ├── README.md ├── docs/ │ ├── arch_design.md │ ├── configs.md │ ├── custom_data_source.md │ ├── gitlab.md │ └── ide_source_start.md ├── extendlibs/ │ └── tac-custom-datasource-demo-0.0.4-SNAPSHOT.jar ├── override.properties ├── pom.xml ├── tac-console/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── tac/ │ │ │ └── console/ │ │ │ ├── ConsoleApplication.java │ │ │ ├── ConsoleBeanConfig.java │ │ │ ├── ConsoleConstants.java │ │ │ ├── TacApplicationContext.java │ │ │ ├── error/ │ │ │ │ ├── ConsoleError.java │ │ │ │ └── IErrorCode.java │ │ │ ├── sdk/ │ │ │ │ └── MenuOptionHandler.java │ │ │ └── web/ │ │ │ ├── HomeController.java │ │ │ ├── InstFileRO.java │ │ │ ├── TacInstController.java │ │ │ ├── TacMsController.java │ │ │ └── ro/ │ │ │ └── InstTestRO.java │ │ └── resources/ │ │ ├── application-admin.properties │ │ ├── application-simple.properties │ │ ├── application.properties │ │ ├── static/ │ │ │ ├── css/ │ │ │ │ └── app.797406e2fb84b15ea0b383ad60572f28.css │ │ │ ├── js/ │ │ │ │ ├── app.606b53e74ca0c7067159.js │ │ │ │ ├── manifest.58ce01f7a6fd036b4f8d.js │ │ │ │ └── vendor.8940c3c560d73d0a0b28.js │ │ │ └── main.css │ │ ├── tac-console.properties │ │ └── templates/ │ │ └── index.html │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── tac/ │ └── console/ │ ├── MenuOptionHandlerTest.java │ └── test/ │ ├── LancherTest.java │ ├── TacConsoleTest.java │ └── TestApplication.java ├── tac-console-web/ │ ├── .babelrc │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .postcssrc.js │ ├── README.md │ ├── build/ │ │ ├── build.js │ │ ├── check-versions.js │ │ ├── dev-client.js │ │ ├── dev-server.js │ │ ├── utils.js │ │ ├── vue-loader.conf.js │ │ ├── webpack.base.conf.js │ │ ├── webpack.dev.conf.js │ │ └── webpack.prod.conf.js │ ├── config/ │ │ ├── dev.env.js │ │ ├── index.js │ │ └── prod.env.js │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── App.vue │ │ ├── components/ │ │ │ ├── Hello.vue │ │ │ ├── Home.vue │ │ │ ├── TacConsole.vue │ │ │ ├── TacInst.vue │ │ │ ├── TacInstPublish.vue │ │ │ ├── TacInstPublishCheck.vue │ │ │ ├── TacJSONEditor.vue │ │ │ ├── TacMs.vue │ │ │ ├── TacMsEdit.vue │ │ │ └── TacMsList.vue │ │ ├── main.js │ │ └── router/ │ │ └── index.js │ └── static/ │ ├── .gitkeep │ └── main.css ├── tac-container/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── tac/ │ │ └── container/ │ │ ├── ContainerApplication.java │ │ ├── ContainerBeanConfig.java │ │ └── web/ │ │ └── TacApiController.java │ └── resources/ │ └── application.properties ├── tac-custom-datasource-demo/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── tmall/ │ │ └── itemcenter/ │ │ ├── ItemDO.java │ │ └── TmallItemService.java │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── tac/ │ └── AppTest.java ├── tac-dev-source-demo/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── tac/ │ └── biz/ │ └── processor/ │ └── HelloWorldTac.java ├── tac-engine/ │ ├── extendlibs/ │ │ ├── gson-2.8.2.jar │ │ └── tac-dev-source-0.0.1-SNAPSHOT.jar │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── tac/ │ │ │ └── engine/ │ │ │ ├── autoconfigure/ │ │ │ │ └── TacAutoConfiguration.java │ │ │ ├── bootlaucher/ │ │ │ │ └── BootJarLaucherUtils.java │ │ │ ├── code/ │ │ │ │ ├── CodeCompileService.java │ │ │ │ ├── CodeLoadService.java │ │ │ │ ├── CustomerClassLoader.java │ │ │ │ ├── SpringClassLoader.java │ │ │ │ └── TacFileService.java │ │ │ ├── common/ │ │ │ │ ├── DefaultTacIDGenerator.java │ │ │ │ ├── SequenceCounter.java │ │ │ │ ├── TacIDGenerator.java │ │ │ │ └── redis/ │ │ │ │ └── RedisSequenceCounter.java │ │ │ ├── compile/ │ │ │ │ ├── IJdkCompiler.java │ │ │ │ ├── InstCodeInfo.java │ │ │ │ ├── JavaSourceCode.java │ │ │ │ ├── JdkCompilerImpl.java │ │ │ │ └── TacJavaFileObject.java │ │ │ ├── event/ │ │ │ │ └── domain/ │ │ │ │ ├── AbstractMsEvent.java │ │ │ │ ├── GetAllMsEvent.java │ │ │ │ ├── MsOfflineEvent.java │ │ │ │ ├── MsPublishEvent.java │ │ │ │ └── MsReceivePublishEvent.java │ │ │ ├── git/ │ │ │ │ └── GitRepoService.java │ │ │ ├── inst/ │ │ │ │ ├── domain/ │ │ │ │ │ ├── TacInst.java │ │ │ │ │ ├── TacInstStatus.java │ │ │ │ │ └── TacInstanceInfo.java │ │ │ │ └── service/ │ │ │ │ ├── DevMsInstFileService.java │ │ │ │ ├── IMsInstFileService.java │ │ │ │ ├── IMsInstService.java │ │ │ │ ├── LocalMsInstFileService.java │ │ │ │ └── redis/ │ │ │ │ ├── RedisMsInstFileService.java │ │ │ │ └── RedisMsInstService.java │ │ │ ├── ms/ │ │ │ │ ├── domain/ │ │ │ │ │ ├── TacMs.java │ │ │ │ │ ├── TacMsDO.java │ │ │ │ │ ├── TacMsPublishMeta.java │ │ │ │ │ └── TacMsStatus.java │ │ │ │ └── service/ │ │ │ │ ├── AbstractDefaultMsPublisher.java │ │ │ │ ├── DefaultMsEventHandlers.java │ │ │ │ ├── IMsPublisher.java │ │ │ │ ├── IMsService.java │ │ │ │ ├── IMsSubscriber.java │ │ │ │ └── redis/ │ │ │ │ ├── RedisMsPublisher.java │ │ │ │ ├── RedisMsService.java │ │ │ │ └── RedisMsSubscriber.java │ │ │ ├── properties/ │ │ │ │ ├── TacDataPathProperties.java │ │ │ │ ├── TacGitlabProperties.java │ │ │ │ ├── TacMsConstants.java │ │ │ │ └── TacRedisConfigProperties.java │ │ │ ├── service/ │ │ │ │ ├── DefaultTacEngineService.java │ │ │ │ ├── EngineBeansConfig.java │ │ │ │ ├── RedisBeansConfig.java │ │ │ │ ├── TacEngineService.java │ │ │ │ ├── TacInstRunService.java │ │ │ │ ├── TacInstanceContainerService.java │ │ │ │ ├── TacInstanceLoadService.java │ │ │ │ └── TacPublishTestService.java │ │ │ └── util/ │ │ │ ├── Bytes.java │ │ │ ├── CollectionUtils.java │ │ │ ├── HConstants.java │ │ │ ├── IterableUtils.java │ │ │ ├── TacCompressUtils.java │ │ │ ├── TacLogUtils.java │ │ │ ├── ThreadPoolUtils.java │ │ │ └── ThreadUtils.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── spring.factories │ │ └── tac/ │ │ └── default-logback-spring.xml │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── tac/ │ │ ├── engine/ │ │ │ ├── common/ │ │ │ │ └── redis/ │ │ │ │ └── RedisSequenceCounterTest.java │ │ │ ├── inst/ │ │ │ │ └── service/ │ │ │ │ └── redis/ │ │ │ │ ├── RedisMsInstFileServiceTest.java │ │ │ │ └── RedisMsInstServiceTest.java │ │ │ ├── ms/ │ │ │ │ └── service/ │ │ │ │ └── redis/ │ │ │ │ ├── RedisMsPublisherTest.java │ │ │ │ └── RedisMsServiceTest.java │ │ │ ├── service/ │ │ │ │ ├── GitLabFeatureTest.java │ │ │ │ ├── JdkCompilerTest.java │ │ │ │ └── TacInstanceLoadServiceTest.java │ │ │ ├── test/ │ │ │ │ ├── TacEnginTest.java │ │ │ │ └── TestApplication.java │ │ │ └── utils/ │ │ │ └── TacFileUtilTest.java │ │ └── test/ │ │ ├── http/ │ │ │ └── HttpClientTest.java │ │ └── redis/ │ │ ├── RedisConfig.java │ │ ├── StringDataRedisTest.java │ │ └── TacRedisMessageListener.java │ ├── resources/ │ │ └── test.properties │ └── source/ │ └── test1/ │ └── com/ │ └── alibaba/ │ └── tac/ │ └── biz/ │ └── processor/ │ └── HelloTac.java ├── tac-infrastructure/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── tac/ │ │ └── infrastracture/ │ │ └── logger/ │ │ ├── TacLogConsts.java │ │ └── TacLoggerImpl.java │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── tac/ │ └── AppTest.java └── tac-sdk/ ├── pom.xml └── src/ └── main/ └── java/ └── com/ └── alibaba/ └── tac/ └── sdk/ ├── common/ │ ├── TacContants.java │ ├── TacParams.java │ ├── TacResult.java │ └── TacThreadLocals.java ├── domain/ │ ├── Context.java │ └── TacRequestContext.java ├── error/ │ ├── ErrorCode.java │ ├── IError.java │ └── ServiceException.java ├── factory/ │ ├── AbstractServiceFactory.java │ └── TacInfrasFactory.java ├── handler/ │ ├── DisposableHandler.java │ ├── InitializingHandler.java │ └── TacHandler.java ├── infrastracture/ │ └── TacLogger.java ├── tangram4tac/ │ ├── Cell.java │ ├── Container.java │ ├── FieldExcluder.java │ ├── FieldNameMapper.java │ ├── Style.java │ ├── lib/ │ │ ├── BannerContainer.java │ │ ├── BannerStyle.java │ │ ├── CellType.java │ │ ├── FiveColumnContainer.java │ │ ├── FixContainer.java │ │ ├── FixStyle.java │ │ ├── FloatContainer.java │ │ ├── FlowContainer.java │ │ ├── FlowStyle.java │ │ ├── FourColumnContainer.java │ │ ├── OneChildContainer.java │ │ ├── OneColumnContainer.java │ │ ├── OnePlusNContainer.java │ │ ├── OnePlusNStyle.java │ │ ├── ScrollContainer.java │ │ ├── ScrollFixBannerContainer.java │ │ ├── ScrollFixContainer.java │ │ ├── ScrollStyle.java │ │ ├── StickyContainer.java │ │ ├── StickyStyle.java │ │ ├── ThreeColumnContainer.java │ │ ├── TwoColumnContainer.java │ │ ├── WaterFallContainer.java │ │ └── WaterFallStyle.java │ ├── render/ │ │ ├── DefaultRender.java │ │ └── IRender.java │ └── utils/ │ ├── Pair.java │ └── Utils.java └── utils/ └── TacIPUtils.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### nbproject/private/ nbbuild/ dist/ nbdist/ bin/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2016 Alibaba Group Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README-ch.md ================================================ - [TAC](#tac) - [What is TAC ?](#what-is-tac-%EF%BC%9F) - [Features](#features) - [Why TAC?](#why-tac%EF%BC%9F) - [TAC 之前](#tac-%E4%B9%8B%E5%89%8D) - [TAC 之后](#tac-%E4%B9%8B%E5%90%8E) - [Quick Start](#quick-start) - [安装redis](#%E5%AE%89%E8%A3%85redis) - [运行 container](#%E8%BF%90%E8%A1%8C-container) - [运行 console 控制台](#%E8%BF%90%E8%A1%8C-console-%E6%8E%A7%E5%88%B6%E5%8F%B0) - [代码开发](#%E4%BB%A3%E7%A0%81%E5%BC%80%E5%8F%91) - [本地编译、打包](#%E6%9C%AC%E5%9C%B0%E7%BC%96%E8%AF%91%E3%80%81%E6%89%93%E5%8C%85) - [预发布](#%E9%A2%84%E5%8F%91%E5%B8%83) - [正式发布](#%E6%AD%A3%E5%BC%8F%E5%8F%91%E5%B8%83) - [启动配置参数](#%E5%90%AF%E5%8A%A8%E9%85%8D%E7%BD%AE%E5%8F%82%E6%95%B0) - [接入你自己的数据源](#%E6%8E%A5%E5%85%A5%E4%BD%A0%E8%87%AA%E5%B7%B1%E7%9A%84%E6%95%B0%E6%8D%AE%E6%BA%90) # TAC ## What is TAC ? * TAC (Tiny API Cloud ) 是与 tangram 配套的服务端解决方案。当然也支持脱离 tangram 使用; * TAC 不是平台,也不是框架,而是一种开发模式; ## Features * 快速发布; * 无需部署; * 灵活修改; * 快速添加数据源; * 客户端开发人员直接参与服务端逻辑; ## Why TAC? ### TAC 之前 * 在 TAC 诞生之前,天猫 app 大多数页面服务端的开发模式是这样的 。以首页为例: * 1.客户端与服务端同学约定接口数据类型,字段; * 2.服务端提供 mock 接口,两端并行开发; * 3.测试、部署、发布。 * 这种模式的弊端在于,由于页面依赖了各种数据源,发布是一个漫长的过程,如果遇到字段修改,整个应用重新编译、打包部署流程太长;不同的页面部署在不同的应用中,无法共享数据源 ### TAC 之后 * TAC 接入各个常用数据源; * 客户端同学直接在 TAC 上提交源码,编译、测试、并发布生效; * 客户端页面开发不需要服务端同学参与,免去沟通过程; * 服务端同学专注开发业务逻辑; ![tac流程](docs/imgs/tac1.png) ## Quick Start ### 安装[redis](https://redis.io/) ### 运行 container ``` java -jar tac-container.jar ``` ### 运行 console 控制台 ``` java -jar tac-console.jar --admin ``` * 成功后可打开控制台 ``` http://localhost:7001/#/tacMs/list ``` ### 代码开发 * 仓库地址 [oss.sonatype.org](https://oss.sonatype.org/#nexus-search;quick~tac-sdk) * 添加 SDK 依赖 ``` com.alibaba tac-sdk ${project.version} ``` * 编写代码 ```java public class HelloWorldTac implements TacHandler { /** * 引入日志服务 */ private TacLogger tacLogger = TacInfrasFactory.getLogger(); /** * 编写一个实现TacHandler接口的类 * * @param context * @return * @throws Exception */ @Override public TacResult execute(Context context) throws Exception { // 执行逻辑 tacLogger.info("Hello World"); Map data = new HashMap<>(); data.put("name", "hellotac"); data.put("platform", "iPhone"); data.put("clientVersion", "7.0.2"); data.put("userName", "tac-userName"); return TacResult.newResult(data); } } ``` ### 本地编译、打包 ```bash cd tac-dev-source java -jar tac-console.jar --package --msCode=helloworld ``` ![](docs/imgs/tac-package.png) ### 预发布 * 预发布 * 测试预发布 ![](docs/imgs/pre-test.png) ### 正式发布 * 线上验证 ``` curl http://localhost:8001/api/tac/execute/helloworld -s|json ``` * 结果 ```json { "success": true, "msgCode": null, "msgInfo": null, "data": { "helloworld": { "data": { "name": "hellotac", "clientVersion": "7.0.2", "userName": "tac-userName", "platform": "iPhone" }, "success": true, "msCode": "helloworld" } }, "hasMore": null, "ip": "127.0.0.1" } ``` ## [启动配置参数](/docs/configs.md) ## [接入你自己的数据源](/docs/custom_data_source.md) ## [与gitlab集成](/docs/gitlab.md) ## [IDE源码启动——quickstart](/docs/ide_source_start.md) ================================================ FILE: README.md ================================================ * [TAC](#tac) * [What is TAC ?](#what-is-tac-%EF%BC%9F) * [Features](#features) * [Why TAC?](#why-tac%EF%BC%9F) * [Before TAC](#before-tac) * [After TAC](#after-tac) * [Quick Start](#quick-start) * [Install redis](#install-redis) * [Run container](#run-container) * [Run console](#run-console) * [Code Develop](#code-develop) * [compile and package](#compile-and-package) * [Pre-Publish](#pre-publish) * [Online-Publish](#online-publish) * [The start params config](#the-start-params-config) * [Add your own datasource](#add-your-own-datasource) # [中文文档](README-ch.md) # TAC ## What is TAC ? + The TAC (Tiny API Cloud) is a server-side solution with tangram. Of course, it also supports the use of secession from tangram; TAC is not a platform, nor a framework, but a development model. ## Features * Quick publish; * Without deploy; * Flexible modification * Quickly add data sources * Client developers directly participate in server-side logic; ## Why TAC? ### Before TAC * Before the birth of TAC, the development mode of most app server-side on Tmall app was like this. Take the home page as an example: * Client and server developer discuss the interface data types, fields; * The server developer provides a mock interface with parallel development at both ends. * Test, deploy, release. - The disadvantage of this model is that since the page relies on various data sources, publishing is a long process. If the field is modified, the entire application will be recompiled and packaged. The deployment process is too long; different pages are deployed in different applications. unable to share data source ### After TAC * TAC access to various commonly used data sources; * Clients submit source code directly on TAC, compile, test, and publish; * Client development does not require the participation of server-side developer, eliminating the need for communication. * Server-side developer focus on developing business logic; ![tac develop progress](docs/imgs/tac1-en.png) ## Quick Start ### Install [redis](https://redis.io/) ### Run container ``` java -jar tac-container.jar ``` ### Run console ``` java -jar tac-console.jar --admin ``` * open console when succes ``` http://localhost:7001/#/tacMs/list ``` ### Code Develop * Repo Address [oss.sonatype.org](https://oss.sonatype.org/#nexus-search;quick~tac-sdk) * Add SDK Dependency ``` com.alibaba tac-sdk ${project.version} ``` * Write your code ```java public class HelloWorldTac implements TacHandler { /** * 引入日志服务 */ private TacLogger tacLogger = TacInfrasFactory.getLogger(); /** * 编写一个实现TacHandler接口的类 * * @param context * @return * @throws Exception */ @Override public TacResult execute(Context context) throws Exception { // 执行逻辑 tacLogger.info("Hello World"); Map data = new HashMap<>(); data.put("name", "hellotac"); data.put("platform", "iPhone"); data.put("clientVersion", "7.0.2"); data.put("userName", "tac-userName"); return TacResult.newResult(data); } } ``` ### compile and package ```bash cd tac-dev-source java -jar tac-console.jar --package --msCode=helloworld ``` ![](docs/imgs/tac-package.png) ### Pre-Publish * Pre-Publish * Test-Pre-Publish ![](docs/imgs/pre-test.png) ### Online-Publish * online check ``` curl http://localhost:8001/api/tac/execute/helloworld -s|json ``` * Result ```json { "success": true, "msgCode": null, "msgInfo": null, "data": { "helloworld": { "data": { "name": "hellotac", "clientVersion": "7.0.2", "userName": "tac-userName", "platform": "iPhone" }, "success": true, "msCode": "helloworld" } }, "hasMore": null, "ip": "127.0.0.1" } ``` ## [The start params config](/docs/configs.md) ## [Add your own datasource](/docs/custom_data_source.md) ## [Use with gitlab](/docs/gitlab.md) ================================================ FILE: docs/arch_design.md ================================================ ================================================ FILE: docs/configs.md ================================================ # TAC 配置 * TAC 使用 springboot 构建,可是用 springboot 的标准配置文件来替换其默认配置; * 如 启动参数 --spring.config.location=file:/override.properties ## 通用配置 ```properties # http 服务器端口 server.port=8001 # endpoint 配置 management.port=8002 # 使用的存储 redis tac.default.store=redis # 扩展点扫描的包名 逗号分隔 scan.package.name=com.tmall.tac.test # 扩展jar包路径 tac.extend.lib=extendlibs # 日志路径 logging.config=classpath:tac/default-logback-spring.xml # 编译相关 # 参考类 TacDataPathProperties # 编译结果路径 默认值 不建议修改 tac.data.path.outputPathPrefix=${user.home}/tac/data/classes # 运行时加载的类路径 不建议修改 tac.data.path.classLoadPathPrefix=${user.home}/tac/data/ms # 编译代码的包名 修改成自定义包名 tac.data.path.pkgPrefix=com.alibaba.tac.biz; # redis存储相关配置 参考类 TacRedisConfigProperties 以下配置不建议修改 # msInst元数据路径 tac.redis.config.msInstMetaDataPath=com.alibaba.tac.msInstMetaData # ms元数据路径 tac.redis.config.msMetaDataPath=com.alibaba.tac.msMetaData # 数据路径的前缀 tac.redis.config.dataPathPrefix=msInstFile # 服务列表的路径 tac.redis.config.msListPath=msPublishedList # 发布时候订阅的channel tac.redis.config.publishEventChannel=tac.inst.publish.channel ``` ## tac-console 配置 ```properties # tac-container的http接口 在线上验证时使用 tac.container.web.api=http://localhost:8001/api/tac/execute ``` ## gitlab配置 ```properties # gitlab服务器地址 tac.gitlab.config.hostURL=http://127.0.0.1 # gitlab帐号token tac.gitlab.config.token=xxxxx # gitlab仓库groupName tac.gitlab.config.groupName=tac-admin # gitlab仓库帐号名 tac.gitlab.config.userName=tac-admin # gitlab仓库帐号密码 tac.gitlab.config.password=tac-admin # gitlab代码下载存储路径 (各微服务代码会下载到这个路径下) tac.gitlab.config.basePath=/home/admin/tac/git_codes ``` ================================================ FILE: docs/custom_data_source.md ================================================ ## 接入你自己的数据源 * 以下以 idea 为例,描述 tac 源码级别添加数据源步骤 ### 代码拉取 ``` git clone git@github.com:alibaba/tac.git ``` ### ### 打开工程 * 为了方便大家理解,demo 模块加了 demo 字样; * 在这里我们添加天猫商品服务(当然是 mock 的) ![image.png | left | 827x406](imgs/sourcestart/1527908670979-41bc49e4-bee3-422b-9898-0089dfc9e3b8.png) ```java package com.alibaba.tac.infrastracture.demo.itemcenter; import org.springframework.stereotype.Service; /** * */ @Service public class TmallItemService { /** * get a item * * @param id * @return */ public ItemDO getItem(Long id) { // mock data 这里可以进行PRC、HTTP 调用 和自己的业务系统交互 return new ItemDO(id, "A Song of Ice and Fire", "¥222.00"); } } ``` ### 安装 jar 包到本地仓库 ```plain mvn clean -Dmaven.test.skip=true package install ``` ### 在微服务里引用新的数据源 * 在 仍然以 tac-dev-source 为例 【注意】在新的 pom 文件中引入了刚刚打包的 jar 包 tac-custom-datasource-demo ![image.png | left | 827x339](imgs/sourcestart/1527908958075-f5f22b21-87c0-4850-ac9e-2d48b6f8f4ca.png) * 实例代码 ```java package com.alibaba.tac.biz.processor; import com.alibaba.tac.sdk.common.TacResult; import com.alibaba.tac.sdk.domain.Context; import com.alibaba.tac.sdk.factory.TacInfrasFactory; import com.alibaba.tac.sdk.handler.TacHandler; import com.alibaba.tac.sdk.infrastracture.TacLogger; import com.tmall.itemcenter.ItemDO; import com.tmall.itemcenter.TmallItemService; import java.util.HashMap; import java.util.Map; /** * @author jinshuan.li */ public class HelloWorldTac implements TacHandler { /** * get the logger service */ private TacLogger tacLogger = TacInfrasFactory.getLogger(); private TmallItemService tmallItemService = TacInfrasFactory.getServiceBean(TmallItemService.class); /** * implement a class which implements TacHandler interface {@link TacHandler} * * @param context * @return * @throws Exception */ @Override public TacResult execute(Context context) throws Exception { // the code tacLogger.info("Hello World22"); Map data = new HashMap<>(); data.put("name", "hellotac"); data.put("platform", "iPhone"); data.put("clientVersion", "7.0.2"); data.put("userName", "tac-userName"); ItemDO item = tmallItemService.getItem(1L); data.put("item", item); return TacResult.newResult(data); } } ``` ### 从 IDEA 源码运行 * 在 tac-infrastructure 的 pom 文件中加入依赖 (理论上来说任意一个 pom 都行,保证在 classpath 里) ```xml         com.alibaba tac-custom-datasource-demo 0.0.4-SNAPSHOT ``` ![image.png | left | 827x298](imgs/sourcestart/1527909372611-d351cd8b-2429-4d3f-8ea9-e2dd1e172759.png) * 修改加载路径,让新的 bean 能改被加载 ![image.png | left | 799x289](imgs/sourcestart/1527909790663-00749a83-9d99-43a1-a08c-3e2df8060507.png) * 源码启动 console [参考](ide_source_start.md) * 像打包 helloworld 一样打包新实例代码   mvn clean -Dmaven.test.skip=true package * 正常预发布测试 ![image.png | left | 827x284](imgs/sourcestart/1527909925642-7c07329e-2a63-436e-8403-5a1bc87639a3.png) ### 从 Jar 包执行 * 为了实现对源码无侵入,tac 改造了 classloader 的顺序以支持从外部加载数据源的 jar 包; * 只需将数据源 jar 包放入 extendlibs 中即可 ![image.png | left | 827x276](imgs/sourcestart/1527910365188-43a1a4c3-fb9b-4fa5-bbbb-3f7f514fc1b9.png) ![image.png | left | 766x296](imgs/sourcestart/1527910444921-c5c32034-e174-431a-b7c8-59077c11577b.png) * 运行 java -jar tac-console-0.0.4.jar --admin ================================================ FILE: docs/gitlab.md ================================================ # 与 gitlab 集成 * 用户可以将 gitlab 与 tac 集成,方便管理微服务; ## Step 1 新建帐号 * tac 使用专门的 gitlab 帐号来管理相关代码;联系对应的 gitlab 服务器管理员,新建名如 tac-admin 的用户,并获取其 api token、userName、password. ## Step 2 tac 启动参数配置 * 在 tac 启动时配置如下参数 ```properties # gitlab服务器地址 tac.gitlab.config.hostURL=http://127.0.0.1 # gitlab帐号token tac.gitlab.config.token=xxxxx # gitlab仓库groupName tac.gitlab.config.groupName=tac-admin # gitlab仓库帐号名 tac.gitlab.config.userName=tac-admin # gitlab仓库帐号密码 tac.gitlab.config.password=tac-admin # gitlab代码下载存储路径 (各微服务代码会下载到这个路径下) tac.gitlab.config.basePath=/home/admin/tac/git_codes ``` ## Step 3 修改微服务的代码仓库地址 * 如下图所示,修改该微服务的仓库地址 ![gitlab_repo](imgs/tac-gitlab1.png) ## Step 4 置顶实例分支并发布 ![gitlab_repo](imgs/tac-gitlab3.png) ================================================ FILE: docs/ide_source_start.md ================================================ # 源码启动详细步骤 * 以下以 idea 为例,描述 tac 源码从 idea 启动步骤 ### 代码拉取 ``` git clone git@github.com:alibaba/tac.git ``` ### 打开工程 * 项目通过 springboot 编写 依赖 jdk1.8 * 使用了 lombok 包,idea 需要安装 lombok 插件; ![undefined](imgs/sourcestart/1527213111970-6a1b5031-63ef-4082-b602-4493555a40e8.png) ### 安装并启动 redis (本地默认配置) * ip : 127.0.0.1 * port : 6379 ### 启动 console * com.alibaba.tac.console.ConsoleApplication 带上 --admin 参数启动 ![undefined](imgs/sourcestart/1527213201547-8d16dd54-d32a-4cd9-927a-4ceb509773a6.png) * 成功后打开控制台 http://localhost:7001/#/tacMs/list ### 新建服务 ![undefined](imgs/sourcestart/1527213265713-e0e7611f-b1c2-43bd-8cf5-31dd0d9e9cc6.png) ### 编写代码 * 参考 tac-dev-source ![undefined](imgs/sourcestart/1527213324287-63726690-1df1-45fb-afc6-e931784855d1.png) ```java package com.alibaba.tac.biz.processor; import com.alibaba.tac.sdk.common.TacResult; import com.alibaba.tac.sdk.domain.Context; import com.alibaba.tac.sdk.factory.TacInfrasFactory; import com.alibaba.tac.sdk.handler.TacHandler; import com.alibaba.tac.sdk.infrastracture.TacLogger; import java.util.HashMap; import java.util.Map; /** * @author jinshuan.li */ public class HelloWorldTac implements TacHandler { /** * get the logger service */ private TacLogger tacLogger = TacInfrasFactory.getLogger(); /** * implement a class which implements TacHandler interface * {@link TacHandler} * @param context * @return * @throws Exception */ @Override public TacResult execute(Context context) throws Exception { // the code tacLogger.info("Hello World22"); Map data = new HashMap<>(); data.put("name", "hellotac"); data.put("platform", "iPhone"); data.put("clientVersion", "7.0.2"); data.put("userName", "tac-userName"); return TacResult.newResult(data); } } ``` ### 代码打包 ``` cd tac-dev-source mvn clean -Dmaven.test.skip=true package ``` ### 上传 jar 包 ![undefined](imgs/sourcestart/1527213524357-bae645a8-d865-472d-a89d-c6660aeade07.png) ### 预发布 ### 预发布测试 ![undefined](imgs/sourcestart/1527213630237-809d5801-c137-4e53-8709-3d4e772406df.png) ## 正式发布 ### 运行 * com.alibaba.tac.container.ContainerApplication ### 控制台操作发布 ![undefined](imgs/sourcestart/1527213761239-b3548ce2-6f0d-406d-af8b-1efaf688a45d.png) ================================================ FILE: override.properties ================================================ scan.package.name=com.tmall.itemcenter ================================================ FILE: pom.xml ================================================ 4.0.0 com.alibaba tac 0.0.4 pom tac-sdk tac-console tac-container tac-engine tac-infrastructure tac Tangram App Container https://github.com/alibaba/tac 2018 Alibaba Group https://github.com/alibaba ljinshuan ljinshuan@gmail.com alibaba https://github.com/alibaba https://github.com/alibaba/tac scm:git:https://github.com/alibaba/tac.git scm:git:https://github.com/alibaba/tac.git Github Issues https://github.com/alibaba/tac/issues The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt org.springframework.boot spring-boot-starter-parent 1.4.7.RELEASE UTF-8 UTF-8 1.8 1.8 3.0.2.RELEASE 2.1.1 junit junit 4.12 test com.alibaba tac-console ${project.version} com.alibaba tac-container ${project.version} com.alibaba tac-engine ${project.version} com.alibaba tac-infrastructure ${project.version} com.alibaba tac-sdk ${project.version} org.aspectj aspectjweaver 1.9.0 com.google.guava guava 20.0 org.apache.commons commons-collections4 4.1 commons-beanutils commons-beanutils 1.9.3 org.apache.commons commons-lang3 3.7 com.alibaba fastjson 1.2.39 org.apache.zookeeper zookeeper 3.4.9 org.slf4j slf4j-log4j12 log4j log4j org.asynchttpclient async-http-client 2.2.0 org.springframework.boot spring-boot-loader 1.4.7.RELEASE org.gitlab4j gitlab4j-api 4.8.9 org.eclipse.jgit org.eclipse.jgit 4.5.0.201609210915-r commons-io commons-io 2.6 org.springframework.boot spring-boot-maven-plugin org.apache.maven.plugins maven-compiler-plugin ${jdk.version} ${jdk.version} UTF-8 org.apache.maven.plugins maven-resources-plugin UTF-8 ossrh https://oss.sonatype.org/content/repositories/snapshots ossrh Maven Central Staging Repository https://oss.sonatype.org/service/local/staging/deploy/maven2/ release org.sonatype.plugins nexus-staging-maven-plugin 1.6.7 true ossrh https://oss.sonatype.org/ true org.apache.maven.plugins maven-release-plugin true false release deploy org.apache.maven.plugins maven-compiler-plugin ${jdk.version} ${jdk.version} UTF-8 org.apache.maven.plugins maven-gpg-plugin 1.5 sign-artifacts verify sign org.apache.maven.plugins maven-source-plugin 3.0.1 attach-sources jar-no-fork org.apache.maven.plugins maven-javadoc-plugin 3.0.0 attach-javadocs jar -Xdoclint:none ================================================ FILE: tac-console/pom.xml ================================================ tac com.alibaba 0.0.4 4.0.0 tac-console jar tac-console http://maven.apache.org UTF-8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-configuration-processor true com.alibaba tac-engine org.springframework.boot spring-boot-maven-plugin org.apache.maven.plugins maven-compiler-plugin ${jdk.version} ${jdk.version} UTF-8 org.apache.maven.plugins maven-resources-plugin UTF-8 ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/ConsoleApplication.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console; import com.alibaba.tac.console.sdk.MenuOptionHandler; import com.alibaba.tac.engine.bootlaucher.BootJarLaucherUtils; import com.alibaba.tac.engine.code.CodeLoadService; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.*; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.loader.jar.JarFile; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.*; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import javax.annotation.Resource; /** * @author jinshuan.li 07/02/2018 11:18 */ @SpringBootApplication(scanBasePackages = "${tac.app.scan.packages}") @PropertySources({@PropertySource("application.properties"), @PropertySource("tac-console.properties")}) @EnableAspectJAutoProxy(proxyTargetClass = true) @Import(ConsoleBeanConfig.class) @Slf4j public class ConsoleApplication implements CommandLineRunner { @Resource private ApplicationArguments applicationArguments; @Resource private MenuOptionHandler menuOptionHandler; public static void main(String[] args) throws Exception { // parse the args ApplicationArguments arguments = new DefaultApplicationArguments(args); boolean help = arguments.containsOption(ConsoleConstants.MENU_HELP); if (help) { MenuOptionHandler.printUsage(); return; } // the code must execute before spring start JarFile bootJarFile = BootJarLaucherUtils.getBootJarFile(); if (bootJarFile != null) { BootJarLaucherUtils.unpackBootLibs(bootJarFile); log.debug("the temp tac lib folder:{}", BootJarLaucherUtils.getTempUnpackFolder()); } // get command args and start spring boot Boolean webEnv = false; String additionProfile = ConsoleConstants.ADDDITION_PROFILE_SIMPLE; if (arguments.containsOption(ConsoleConstants.OPTION_ADMIN)) { webEnv = true; additionProfile = ConsoleConstants.ADDDITION_PROFILE_ADMIN; } SpringApplication springApplication = new SpringApplication(ConsoleApplication.class); springApplication.setWebEnvironment(webEnv); springApplication.setBannerMode(Banner.Mode.OFF); if (!webEnv) { // command model springApplication.setAdditionalProfiles(additionProfile); } else { // web model springApplication.setAdditionalProfiles(additionProfile); } springApplication.addListeners(new ApplicationListener() { @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { CodeLoadService.changeClassLoader(event.getEnvironment()); } }); springApplication.run(args); } @Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**").allowedMethods("GET", "POST", "PUT", "DELETE"); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { super.addResourceHandlers(registry); registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); } }; } @Bean public ExitCodeGenerator exitCodeGenerator() { return new ExitCodeGenerator() { @Override public int getExitCode() { return 0; } }; } @Override public void run(String... args) throws Exception { // handle the command Boolean webEnv = false; if (applicationArguments.containsOption(ConsoleConstants.OPTION_ADMIN)) { webEnv = true; } if (!webEnv) { menuOptionHandler.handleMenuOption(); } } } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/ConsoleBeanConfig.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console; import com.alibaba.tac.engine.service.EngineBeansConfig; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.*; /** * @author jinshuan.li 01/03/2018 17:21 * * this class is used to scan extend packages you can set the scan.package.name value from properties */ @Slf4j @ConditionalOnProperty(name = "scan.package.name") @Configuration @ComponentScan(basePackages = "${scan.package.name}") @Import(EngineBeansConfig.class) public class ConsoleBeanConfig { } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/ConsoleConstants.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console; /** * @author jinshuan.li 27/02/2018 21:00 */ public class ConsoleConstants { public static final String MENU_HELP = "help"; public static final String MENU_PACKAGE = "package"; public static final String MENU_PUBLISH = "publish"; public static final String OPTION_ADMIN = "admin"; public static final String ADDDITION_PROFILE_SIMPLE = "simple"; public static final String ADDDITION_PROFILE_ADMIN = "admin"; public static final String ADDDITION_PROFILE_CONSOLE = "debug"; } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/TacApplicationContext.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; /** * @author jinshuan.li 06/03/2018 20:51 */ @Slf4j @Deprecated public class TacApplicationContext extends AnnotationConfigEmbeddedWebApplicationContext { public TacApplicationContext() throws Exception { super(); } } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/error/ConsoleError.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console.error; /** * @author jinshuan.li 06/03/2018 12:19 */ public enum ConsoleError implements IErrorCode { /** * system error */ SYSTEM_ERROR("SYSTEM_ERROR", "system error"); private ConsoleError(String code, String msg) { this.code = code; this.msg = msg; } private String code; private String msg; @Override public String getCode() { return code; } @Override public String getMessage() { return msg; } } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/error/IErrorCode.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console.error; /** * @author jinshuan.li 06/03/2018 12:18 */ public interface IErrorCode { String getCode(); String getMessage(); } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/sdk/MenuOptionHandler.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console.sdk; import com.alibaba.tac.console.ConsoleConstants; import com.alibaba.tac.engine.code.CodeCompileService; import com.alibaba.tac.engine.code.TacFileService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.boot.ApplicationArguments; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.io.File; import java.io.IOException; import java.util.List; /** * @author jinshuan.li 27/02/2018 21:05 */ @Slf4j @Service public class MenuOptionHandler { @Resource private ApplicationArguments arguments; @Resource private CodeCompileService codeCompileService; @Resource private TacFileService tacFileService; @PostConstruct public void init() { } /** * handle the menu option *

* the is just a package command * * @throws IOException */ public void handleMenuOption() throws IOException { if (CollectionUtils.isEmpty(arguments.getOptionNames())) { printUsage(); return; } if (arguments.containsOption(ConsoleConstants.MENU_PACKAGE)) { System.out.println("handleo pacakge"); handlePackage(); } if (arguments.containsOption(ConsoleConstants.MENU_PUBLISH)) { //handlePublish(); } } /** * handle compile and package source */ protected void handlePackage() { String msCode = ""; List msCodes = arguments.getOptionValues("msCode"); List srcDirs = arguments.getOptionValues("sourceDir"); if (CollectionUtils.isEmpty(msCodes)) { printUsage(); return; } msCode = msCodes.get(0); String srcDir = ""; if (CollectionUtils.isEmpty(srcDirs)) { String absolutePath = new File("").getAbsolutePath(); srcDir = absolutePath; } else { srcDir = srcDirs.get(0); } try { // compile Boolean compile = codeCompileService.compile(msCode, srcDir); // package codeCompileService.getJarFile(msCode); log.info("package success . file:{}", tacFileService.getClassFileOutputPath(msCode) + ".zip"); } catch (Exception e) { log.error(e.getMessage(), e); } } public static void printUsage() { System.out.println("useage:"); System.out.println("--package --msCode=${msCode} --sourceDir=${sourceDir}"); // System.out.println("--publish --msCode=${msCode} --zipFile=${zipFile}"); } } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/web/HomeController.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console.web; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; /** * @author jinshuan.li 07/03/2018 17:18 */ @Controller @RequestMapping public class HomeController { @GetMapping("/") public String index() { return "index"; } } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/web/InstFileRO.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console.web; import lombok.Data; import org.springframework.web.multipart.MultipartFile; import java.io.Serializable; /** * @author jinshuan.li 05/03/2018 20:16 */ @Data public class InstFileRO implements Serializable{ private static final long serialVersionUID = -7650755238417075767L; private String name; private MultipartFile file; private Integer age; } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/web/TacInstController.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console.web; import com.alibaba.tac.console.error.ConsoleError; import com.alibaba.tac.console.web.ro.InstTestRO; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.service.IMsInstFileService; import com.alibaba.tac.engine.inst.service.IMsInstService; import com.alibaba.tac.engine.ms.domain.TacMsDO; import com.alibaba.tac.engine.ms.service.IMsPublisher; import com.alibaba.tac.engine.ms.service.IMsService; import com.alibaba.tac.engine.service.TacPublishTestService; import com.alibaba.tac.engine.code.TacFileService; import com.alibaba.tac.sdk.common.TacResult; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.util.List; import java.util.Map; import java.util.Optional; /** * @author jinshuan.li 05/03/2018 20:14 */ @Slf4j @RestController @RequestMapping("/api/inst") public class TacInstController { @Resource private IMsService msService; @Resource private IMsInstService msInstService; @Resource private IMsPublisher msPublisher; @Resource private TacPublishTestService tacPublishTestService; @Resource(name = "prePublishMsInstFileService") private IMsInstFileService prePublishMsInstFileService; @PostMapping(value = "/uploadFile") public TacResult> uploadFile(@RequestParam("file") MultipartFile instFileRO, @RequestParam("msCode") String msCode) { return TacResult.newResult(null); } @GetMapping(value = "/info/{msCode}") public TacResult getMsInst(@PathVariable("msCode") String msCode) { try { TacMsDO ms = msService.getMs(msCode); if (ms == null) { throw new IllegalArgumentException("the service is not exist"); } TacInst tacInst = this.getExistTacInst(ms, ""); return TacResult.newResult(tacInst); } catch (Exception e) { return TacResult.errorResult(ConsoleError.SYSTEM_ERROR.getCode(), e.getMessage()); } } @PostMapping("/create") public TacResult create(@RequestBody TacInst tacInst) { String msCode = tacInst.getMsCode(); try { if (StringUtils.isEmpty(msCode)) { throw new IllegalArgumentException("invalid params"); } TacMsDO ms = msService.getMs(msCode); if (ms == null) { throw new IllegalStateException("the service with code " + msCode + " not exist"); } String name = tacInst.getName(); String gitBranch = tacInst.getGitBranch(); if (StringUtils.isEmpty(name) || StringUtils.isEmpty(gitBranch)) { throw new IllegalArgumentException("invalid params"); } msInstService.createGitTacMsInst(msCode, name, gitBranch); return TacResult.newResult(ms); } catch (Exception e) { return TacResult.errorResult(ConsoleError.SYSTEM_ERROR.getCode(), e.getMessage()); } } @PostMapping("/update") public TacResult update(@RequestBody TacInst tacInst) { String msCode = tacInst.getMsCode(); long instId = tacInst.getId(); try { if (StringUtils.isEmpty(msCode)) { throw new IllegalArgumentException("invalid params"); } TacMsDO ms = msService.getMs(msCode); if (ms == null) { throw new IllegalStateException("the service with code " + msCode + " not exist"); } TacInst tacMsInst = msInstService.getTacMsInst(instId); if (tacMsInst == null) { throw new IllegalStateException("inst not exist"); } String name = tacInst.getName(); String gitBranch = tacInst.getGitBranch(); if (StringUtils.isEmpty(name) || StringUtils.isEmpty(gitBranch)) { throw new IllegalArgumentException("invalid params"); } tacMsInst.setGitBranch(gitBranch); tacMsInst.setName(name); msInstService.updateTacMsInst(instId, tacMsInst); return TacResult.newResult(ms); } catch (Exception e) { return TacResult.errorResult(ConsoleError.SYSTEM_ERROR.getCode(), e.getMessage()); } } @GetMapping(value = "/list/{msCode}") public TacResult> getMsInstList(@PathVariable("msCode") String msCode) { try { TacMsDO ms = msService.getMs(msCode); if (ms == null) { throw new IllegalArgumentException("the service is not exist"); } List msInsts = msInstService.getMsInsts(msCode); Optional.ofNullable(msInsts).ifPresent(items -> { items.stream().forEach(d -> { if (d.getStatus() == null) { d.setStatus(TacInst.STATUS_NEW); } }); }); return TacResult.newResult(msInsts); } catch (Exception e) { return TacResult.errorResult(ConsoleError.SYSTEM_ERROR.getCode(), e.getMessage()); } } private TacInst getExistTacInst(TacMsDO ms, String jarVersion) { String msCode = ms.getCode(); Long publishedInstId = ms.getPublishedInstId(); TacInst tacMsInst = null; if (publishedInstId == null || publishedInstId.equals(0)) { tacMsInst = msInstService.createTacMsInst(msCode, ms.getName(), jarVersion); // update service data ms.setPublishedInstId(tacMsInst.getId()); msService.updateMs(msCode, ms); publishedInstId = ms.getPublishedInstId(); } tacMsInst = msInstService.getTacMsInst(publishedInstId); if (tacMsInst == null) { throw new IllegalStateException("can't find the instance " + publishedInstId); } return tacMsInst; } @PostMapping(value = "/prePublish") public TacResult prePublish(@RequestParam("file") MultipartFile instFileRO, @RequestParam("msCode") String msCode, @RequestParam(value = "instId", required = false) Long instId) { try { byte[] bytes = instFileRO.getBytes(); String md5 = TacFileService.getMd5(bytes); TacMsDO ms = msService.getMs(msCode); if (ms == null) { throw new IllegalArgumentException("the service is not exist"); } TacInst tacMsInst = this.getExistTacInst(ms, md5); // prepublish msPublisher.prePublish(tacMsInst, bytes); return TacResult.newResult(msInstService.getTacMsInst(tacMsInst.getId())); } catch (Exception e) { return TacResult.errorResult(ConsoleError.SYSTEM_ERROR.getCode(), e.getMessage()); } } @PostMapping(value = "/publish") public TacResult publish(@RequestParam("msCode") String msCode, @RequestParam(value = "instId") Long instId) { try { TacInst tacMsInst = msInstService.getTacMsInst(instId); if (tacMsInst == null) { throw new IllegalArgumentException("the instance is not exist " + instId); } if (!StringUtils.equalsIgnoreCase(tacMsInst.getMsCode(), msCode)) { throw new IllegalArgumentException("the msCode is not match"); } // 取预发布的数据 byte[] instanceFile = prePublishMsInstFileService.getInstanceFile(instId); if (instanceFile == null) { throw new IllegalStateException("can't find prePublish data"); } // 正式发布 msPublisher.publish(tacMsInst, instanceFile); } catch (Exception e) { return TacResult.errorResult(ConsoleError.SYSTEM_ERROR.getCode(), e.getMessage()); } return TacResult.newResult(null); } @PostMapping(value = "/preTest") public TacResult preTest(@RequestBody InstTestRO instTestRO) { Long instId = instTestRO.getInstId(); Map params = instTestRO.getParams(); try { TacResult result = tacPublishTestService.prePublishTest(instId, instTestRO.getMsCode(), params); TacResult response = new TacResult<>(result); return response; } catch (Exception e) { log.info("preTest error. {} {}", instTestRO, e.getMessage(), e); return TacResult.errorResult(ConsoleError.SYSTEM_ERROR.getCode(), e.getMessage()); } } @PostMapping(value = "/onlineTest") public TacResult onlineTest(@RequestBody InstTestRO instTestRO) { Long instId = instTestRO.getInstId(); Map params = instTestRO.getParams(); try { TacResult result = tacPublishTestService.onlinePublishTest(instId, instTestRO.getMsCode(), params); return result; } catch (Exception e) { log.info("preTest error. {} {}", instTestRO, e.getMessage(), e); return TacResult.errorResult(ConsoleError.SYSTEM_ERROR.getCode(), e.getMessage()); } } @GetMapping(value = "/gitPrePublish") public TacResult prePublish(@RequestParam(value = "instId") Long instId) { try { TacInst tacMsInst = msInstService.getTacMsInst(instId); if (tacMsInst == null) { throw new IllegalArgumentException("inst not exist"); } TacMsDO ms = msService.getMs(tacMsInst.getMsCode()); if (ms == null) { throw new IllegalArgumentException("ms not eist"); } tacMsInst = msPublisher.gitPrePublish(ms, tacMsInst); return TacResult.newResult(tacMsInst); } catch (Exception e) { return TacResult.errorResult(ConsoleError.SYSTEM_ERROR.getCode(), e.getMessage()); } } } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/web/TacMsController.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console.web; import com.alibaba.tac.console.error.ConsoleError; import com.alibaba.tac.engine.ms.domain.TacMsDO; import com.alibaba.tac.engine.ms.service.IMsService; import com.alibaba.tac.sdk.common.TacResult; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; /** * @author jinshuan.li 05/03/2018 19:30 */ @RestController @RequestMapping("/api/ms") public class TacMsController { @Resource private IMsService msService; @GetMapping("/list") public TacResult> list() { List allMs = msService.getAllMs(); return TacResult.newResult(allMs); } @PostMapping("/create") public TacResult create(@RequestBody TacMsDO tacMsDO) { try { TacMsDO ms = msService.getMs(tacMsDO.getCode()); if (ms != null) { throw new IllegalStateException("the service with code " + tacMsDO.getCode() + " already exist"); } ms = msService.createMs(tacMsDO); return TacResult.newResult(ms); } catch (Exception e) { return TacResult.errorResult(ConsoleError.SYSTEM_ERROR.getCode(), e.getMessage()); } } @PostMapping("/update") public TacResult update(@RequestBody TacMsDO tacMsDO) { try { msService.checkMsDO(tacMsDO); TacMsDO ms = msService.getMs(tacMsDO.getCode()); if (ms == null) { throw new IllegalStateException("该服务不存在"); } ms.setName(tacMsDO.getName()); ms.setGitSupport(tacMsDO.getGitSupport()); ms.setGitRepo(tacMsDO.getGitRepo()); msService.updateMs(tacMsDO.getCode(), ms); return TacResult.newResult(tacMsDO); } catch (Exception e) { return TacResult.errorResult("system", e.getMessage()); } } @PostMapping("/offline") public TacResult offline(@RequestParam("msCode") String msCode) { throw new UnsupportedOperationException("不支持该功能"); } } ================================================ FILE: tac-console/src/main/java/com/alibaba/tac/console/web/ro/InstTestRO.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.console.web.ro; import lombok.Data; import java.io.Serializable; import java.util.Map; /** * @author jinshuan.li 06/03/2018 14:21 */ @Data public class InstTestRO implements Serializable { private Long instId; private String msCode; private Map params; } ================================================ FILE: tac-console/src/main/resources/application-admin.properties ================================================ # the properites when run in admin model name.simple=tac-admin # the scan packages , don't change it if necessary tac.app.scan.packages=com.alibaba.tac ================================================ FILE: tac-console/src/main/resources/application-simple.properties ================================================ # the properites when run in command model name.simple=tac-simple # the scan packages , don't change it if necessary tac.app.scan.packages=com.alibaba.tac.console.sdk,com.alibaba.tac.engine.code,com.alibaba.tac.engine.compile ================================================ FILE: tac-console/src/main/resources/application.properties ================================================ # the port when use web model server.port=7001 # scan the extend package names scan.package.name=com.tmall.itemcenter # the location of extend jars tac.extend.lib=extendlibs # the default store , when use "redis" you should install and config redis server , use "zookeeper" you should install and config zookeeper server tac.default.store=redis # the location of logback config file logging.config=classpath:tac/default-logback-spring.xml # the http url prefix when you run the tac-container, the is used to online publish check; tac.container.web.api=http://localhost:8001/api/tac/execute tac.app.scan.packages=com.alibaba.tac ================================================ FILE: tac-console/src/main/resources/static/css/app.797406e2fb84b15ea0b383ad60572f28.css ================================================ #app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50}h1[data-v-afa07f3c],h2[data-v-afa07f3c]{font-weight:400}ul[data-v-afa07f3c]{list-style-type:none;padding:0}li[data-v-afa07f3c]{display:inline-block;margin:0 10px}a[data-v-afa07f3c]{color:#42b983}.tacMs[data-v-7b1ca360]{text-align:left}.tacInst[data-v-3bfbdc0d]{text-align:left;padding:30px}.logResult[data-v-13be33e9]{background-color:#000;color:#ff0;padding:30px;max-height:600px;overflow:scroll}/*! * Bootstrap v4.0.0 (https://getbootstrap.com) * Copyright 2011-2018 The Bootstrap Authors * Copyright 2011-2018 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{font-style:normal;line-height:inherit}address,dl,ol,ul{margin-bottom:1rem}dl,ol,ul{margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]),a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem}.display-1,.display-2{font-weight:300;line-height:1.2}.display-2{font-size:5.5rem}.display-3{font-size:4.5rem}.display-3,.display-4{font-weight:300;line-height:1.2}.display-4{font-size:3.5rem}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer:before{content:"\2014 \A0"}.img-fluid,.img-thumbnail{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-auto,.col-lg,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-auto,.col-md,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md-auto,.col-sm,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1,.col-auto{-webkit-box-flex:0}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-2,.col-3{-webkit-box-flex:0}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-4,.col-5{-webkit-box-flex:0}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-6,.col-7{-webkit-box-flex:0}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-8,.col-9{-webkit-box-flex:0}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-10,.col-11{-webkit-box-flex:0}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;max-width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.table-sm td,.table-sm th{padding:.3rem}.table-bordered,.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th,.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:hsla(0,0%,100%,.05)}.table-dark.table-hover tbody tr:hover{background-color:hsla(0,0%,100%,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder,.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:not([size]):not([multiple]){height:calc(2.25rem + 2px)}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm,.input-group-lg>.form-control-plaintext.form-control,.input-group-lg>.input-group-append>.form-control-plaintext.btn,.input-group-lg>.input-group-append>.form-control-plaintext.input-group-text,.input-group-lg>.input-group-prepend>.form-control-plaintext.btn,.input-group-lg>.input-group-prepend>.form-control-plaintext.input-group-text,.input-group-sm>.form-control-plaintext.form-control,.input-group-sm>.input-group-append>.form-control-plaintext.btn,.input-group-sm>.input-group-append>.form-control-plaintext.input-group-text,.input-group-sm>.input-group-prepend>.form-control-plaintext.btn,.input-group-sm>.input-group-prepend>.form-control-plaintext.input-group-text{padding-right:0;padding-left:0}.form-control-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-sm>.input-group-append>select.btn:not([size]):not([multiple]),.input-group-sm>.input-group-append>select.input-group-text:not([size]):not([multiple]),.input-group-sm>.input-group-prepend>select.btn:not([size]):not([multiple]),.input-group-sm>.input-group-prepend>select.input-group-text:not([size]):not([multiple]),.input-group-sm>select.form-control:not([size]):not([multiple]),select.form-control-sm:not([size]):not([multiple]){height:calc(1.8125rem + 2px)}.form-control-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-lg>.input-group-append>select.btn:not([size]):not([multiple]),.input-group-lg>.input-group-append>select.input-group-text:not([size]):not([multiple]),.input-group-lg>.input-group-prepend>select.btn:not([size]):not([multiple]),.input-group-lg>.input-group-prepend>select.input-group-text:not([size]):not([multiple]),.input-group-lg>select.form-control:not([size]):not([multiple]),select.form-control-lg:not([size]):not([multiple]){height:calc(2.875rem + 2px)}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem;margin-top:.1rem;font-size:.875rem;line-height:1;color:#fff;background-color:rgba(40,167,69,.8);border-radius:.2rem}.custom-select.is-valid,.form-control.is-valid,.was-validated .custom-select:valid,.was-validated .form-control:valid{border-color:#28a745}.custom-select.is-valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label:before,.was-validated .custom-control-input:valid~.custom-control-label:before{background-color:#71dd8a}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label:before,.was-validated .custom-control-input:valid:checked~.custom-control-label:before{background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label:before,.was-validated .custom-control-input:valid:focus~.custom-control-label:before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(40,167,69,.25)}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label:before,.was-validated .custom-file-input:valid~.custom-file-label:before{border-color:inherit}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem;margin-top:.1rem;font-size:.875rem;line-height:1;color:#fff;background-color:rgba(220,53,69,.8);border-radius:.2rem}.custom-select.is-invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.was-validated .form-control:invalid{border-color:#dc3545}.custom-select.is-invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label:before,.was-validated .custom-control-input:invalid~.custom-control-label:before{background-color:#efa2a9}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label:before,.was-validated .custom-control-input:invalid:checked~.custom-control-label:before{background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus~.custom-control-label:before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(220,53,69,.25)}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label:before,.was-validated .custom-file-input:invalid~.custom-file-label:before{border-color:inherit}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{-ms-flex-align:center;-ms-flex-pack:center;justify-content:center}.form-inline .form-group,.form-inline label{display:-ms-flexbox;display:flex;align-items:center;margin-bottom:0}.form-inline .form-group{-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.btn:focus,.btn:hover{text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}.btn:not(:disabled):not(.disabled).active,.btn:not(:disabled):not(.disabled):active{background-image:none}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;background-color:transparent;background-image:none;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem hsla(208,7%,46%,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff}.btn-link,.btn-link:hover{background-color:transparent}.btn-link:hover{color:#0056b3}.btn-link.focus,.btn-link:focus,.btn-link:hover{text-decoration:underline;border-color:transparent}.btn-link.focus,.btn-link:focus{box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;transition:opacity .15s linear}.fade.show{opacity:1}.collapse{display:none}.collapse.show{display:block}tr.collapse.show{display:table-row}tbody.collapse.show{display:table-row-group}.collapsing{height:0;overflow:hidden;transition:height .35s ease}.collapsing,.dropdown,.dropup{position:relative}.dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty:after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropup .dropdown-menu{margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-menu{margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-toggle:after{vertical-align:0}.dropleft .dropdown-menu{margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";display:none}.dropleft .dropdown-toggle:before{display:inline-block;width:0;height:0;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty:after{margin-left:0}.dropleft .dropdown-toggle:before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:0 1 auto;flex:0 1 auto}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group,.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split:after{margin-left:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio],.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file:focus,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control{margin-left:-1px}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label:before{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label,.input-group>.custom-file:not(:first-child) .custom-file-label:before{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label:before{color:#fff;background-color:#007bff}.custom-control-input:focus~.custom-control-label:before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:active~.custom-control-label:before{color:#fff;background-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label:before{background-color:#e9ecef}.custom-control-label{margin-bottom:0}.custom-control-label:before{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#dee2e6}.custom-control-label:after,.custom-control-label:before{position:absolute;top:.25rem;left:0;display:block;width:1rem;height:1rem;content:""}.custom-control-label:after{background-repeat:no-repeat;background-position:50%;background-size:50% 50%}.custom-checkbox .custom-control-label:before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label:before{background-color:#007bff}.custom-checkbox .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:before{background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label:before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label:before{background-color:#007bff}.custom-radio .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center;background-size:8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:inset 0 1px 2px rgba(0,0,0,.075),0 0 5px rgba(128,189,255,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);font-size:75%}.custom-select-lg,.custom-select-sm{padding-top:.375rem;padding-bottom:.375rem}.custom-select-lg{height:calc(2.875rem + 2px);font-size:125%}.custom-file{display:inline-block;margin-bottom:0}.custom-file,.custom-file-input{position:relative;width:100%;height:calc(2.25rem + 2px)}.custom-file-input{z-index:2;margin:0;opacity:0}.custom-file-input:focus~.custom-file-control{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:focus~.custom-file-control:before{border-color:#80bdff}.custom-file-input:lang(en)~.custom-file-label:after{content:"Browse"}.custom-file-label{left:0;z-index:1;height:calc(2.25rem + 2px);background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label,.custom-file-label:after{position:absolute;top:0;right:0;padding:.375rem .75rem;line-height:1.5;color:#495057}.custom-file-label:after{bottom:0;z-index:3;display:block;height:calc((2.25rem + 2px) - 1px * 2);content:"Browse";background-color:#e9ecef;border-left:1px solid #ced4da;border-radius:0 .25rem .25rem 0}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar,.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat 50%;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .dropup .dropdown-menu{top:auto;bottom:100%}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .dropup .dropdown-menu{top:auto;bottom:100%}.navbar-light .navbar-brand,.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand,.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:hsla(0,0%,100%,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:hsla(0,0%,100%,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:hsla(0,0%,100%,.5);border-color:hsla(0,0%,100%,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-bottom:-.75rem;border-bottom:0}.card-header-pills,.card-header-tabs{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.25rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child),.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;column-gap:1.25rem}.card-columns .card{display:inline-block;width:100%}}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item:before{display:inline-block;padding-right:.5rem;padding-left:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover:before{text-decoration:underline;text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}.badge-primary[href]:focus,.badge-primary[href]:hover{color:#fff;text-decoration:none;background-color:#0062cc}.badge-secondary{color:#fff;background-color:#6c757d}.badge-secondary[href]:focus,.badge-secondary[href]:hover{color:#fff;text-decoration:none;background-color:#545b62}.badge-success{color:#fff;background-color:#28a745}.badge-success[href]:focus,.badge-success[href]:hover{color:#fff;text-decoration:none;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}.badge-info[href]:focus,.badge-info[href]:hover{color:#fff;text-decoration:none;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}.badge-warning[href]:focus,.badge-warning[href]:hover{color:#212529;text-decoration:none;background-color:#d39e00}.badge-danger{color:#fff;background-color:#dc3545}.badge-danger[href]:focus,.badge-danger[href]:hover{color:#fff;text-decoration:none;background-color:#bd2130}.badge-light{color:#212529;background-color:#f8f9fa}.badge-light[href]:focus,.badge-light[href]:hover{color:#212529;text-decoration:none;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}.badge-dark[href]:focus,.badge-dark[href]:hover{color:#fff;text-decoration:none;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}.progress{height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress,.progress-bar{display:-ms-flexbox;display:flex}.progress-bar{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;background-color:#007bff;transition:width .6s ease}.progress-bar-striped{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem}.progress-bar-animated{animation:progress-bar-stripes 1s linear infinite}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:focus,.close:hover{color:#000;text-decoration:none;opacity:.75}.close:not(:disabled):not(.disabled){cursor:pointer}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none}.modal,.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;outline:0}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translateY(-25%)}.modal.show .modal-dialog{transform:translate(0)}.modal-dialog-centered{-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-content,.modal-dialog-centered{display:-ms-flexbox;display:flex}.modal-content{position:relative;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow:before,.bs-tooltip-top .arrow:before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow:before,.bs-tooltip-right .arrow:before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.bs-tooltip-bottom .arrow:before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow:before,.bs-tooltip-left .arrow:before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{top:0;left:0;z-index:1060;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover,.popover .arrow{position:absolute;display:block}.popover .arrow{width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow:after,.popover .arrow:before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top] .arrow:after,.bs-popover-auto[x-placement^=top] .arrow:before,.bs-popover-top .arrow:after,.bs-popover-top .arrow:before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow:before,.bs-popover-top .arrow:before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top] .arrow:after,.bs-popover-top .arrow:after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow:after,.bs-popover-auto[x-placement^=right] .arrow:before,.bs-popover-right .arrow:after,.bs-popover-right .arrow:before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow:before,.bs-popover-right .arrow:before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right] .arrow:after,.bs-popover-right .arrow:after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom] .arrow:after,.bs-popover-auto[x-placement^=bottom] .arrow:before,.bs-popover-bottom .arrow:after,.bs-popover-bottom .arrow:before{border-width:0 .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow:before,.bs-popover-bottom .arrow:before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow:after,.bs-popover-bottom .arrow:after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header:before,.bs-popover-bottom .popover-header:before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow:after,.bs-popover-auto[x-placement^=left] .arrow:before,.bs-popover-left .arrow:after,.bs-popover-left .arrow:before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow:before,.bs-popover-left .arrow:before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow:after,.bs-popover-left .arrow:after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;-ms-flex-align:center;align-items:center;width:100%;transition:transform .6s ease;-webkit-backface-visibility:hidden;backface-visibility:hidden;perspective:1000px}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{transform:translateZ(0)}}.active.carousel-item-right,.carousel-item-next{transform:translateX(100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-right,.carousel-item-next{transform:translate3d(100%,0,0)}}.active.carousel-item-left,.carousel-item-prev{transform:translateX(-100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-left,.carousel-item-prev{transform:translate3d(-100%,0,0)}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat 50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;background-color:hsla(0,0%,100%,.5)}.carousel-indicators li:before{top:-10px}.carousel-indicators li:after,.carousel-indicators li:before{position:absolute;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li:after{bottom:-10px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important}.rounded-right,.rounded-top{border-top-right-radius:.25rem!important}.rounded-bottom,.rounded-right{border-bottom-right-radius:.25rem!important}.rounded-bottom,.rounded-left{border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important}.rounded-circle{border-radius:50%!important}.rounded-0{border-radius:0!important}.clearfix:after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive:before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9:before{padding-top:42.857143%}.embed-responsive-16by9:before{padding-top:56.25%}.embed-responsive-4by3:before{padding-top:75%}.embed-responsive-1by1:before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{top:0}.fixed-bottom,.fixed-top{position:fixed;right:0;left:0;z-index:1030}.fixed-bottom{bottom:0}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;-webkit-clip-path:inset(50%);clip-path:inset(50%);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal;-webkit-clip-path:none;clip-path:none}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0062cc!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#545b62!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#1e7e34!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#117a8b!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#d39e00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#bd2130!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#dae0e5!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#1d2124!important}.text-muted{color:#6c757d!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,:after,:before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]:after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}.container,body{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}.input-group>.input-group-append:last-child>.b-dropdown:not(:last-child):not(.dropdown-toggle)>.btn,.input-group>.input-group-append:not(:last-child)>.b-dropdown>.btn,.input-group>.input-group-prepend>.b-dropdown>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.b-dropdown>.btn,.input-group>.input-group-prepend:first-child>.b-dropdown:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.b-dropdown>.btn{border-top-left-radius:0;border-bottom-left-radius:0}input.form-control[type=color],input.form-control[type=range]{height:2.25rem}input.form-control.form-control-sm[type=color],input.form-control.form-control-sm[type=range]{height:1.9375rem}input.form-control.form-control-lg[type=color],input.form-control.form-control-lg[type=range]{height:3rem}input.form-control[type=color]{padding:.25rem}input.form-control.form-control-sm[type=color]{padding:.125rem}table.b-table.b-table-fixed{table-layout:fixed}table.b-table[aria-busy=false]{opacity:1}table.b-table[aria-busy=true]{opacity:.6}table.b-table>tfoot>tr>th,table.b-table>thead>tr>th{position:relative}table.b-table>tfoot>tr>th.sorting,table.b-table>thead>tr>th.sorting{padding-right:1.5em;cursor:pointer}table.b-table>tfoot>tr>th.sorting:after,table.b-table>tfoot>tr>th.sorting:before,table.b-table>thead>tr>th.sorting:after,table.b-table>thead>tr>th.sorting:before{position:absolute;bottom:0;display:block;opacity:.4;padding-bottom:inherit;font-size:inherit;line-height:180%}table.b-table>tfoot>tr>th.sorting:before,table.b-table>thead>tr>th.sorting:before{right:.75em;content:"\2191"}table.b-table>tfoot>tr>th.sorting:after,table.b-table>thead>tr>th.sorting:after{right:.25em;content:"\2193"}table.b-table>tfoot>tr>th.sorting_asc:after,table.b-table>tfoot>tr>th.sorting_desc:before,table.b-table>thead>tr>th.sorting_asc:after,table.b-table>thead>tr>th.sorting_desc:before{opacity:1}table.b-table.b-table-stacked{width:100%}table.b-table.b-table-stacked,table.b-table.b-table-stacked>caption,table.b-table.b-table-stacked>tbody,table.b-table.b-table-stacked>tbody>tr,table.b-table.b-table-stacked>tbody>tr>td,table.b-table.b-table-stacked>tbody>tr>th{display:block}table.b-table.b-table-stacked>tbody>tr.b-table-bottom-row,table.b-table.b-table-stacked>tbody>tr.b-table-top-row,table.b-table.b-table-stacked>tfoot,table.b-table.b-table-stacked>thead{display:none}table.b-table.b-table-stacked>tbody>tr>:first-child{border-top-width:.4rem}table.b-table.b-table-stacked>tbody>tr>[data-label]{display:grid;grid-template-columns:40% auto;grid-gap:.25rem 1rem}table.b-table.b-table-stacked>tbody>tr>[data-label]:before{content:attr(data-label);display:inline;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal}@media (max-width:575.99px){table.b-table.b-table-stacked-sm{width:100%}table.b-table.b-table-stacked-sm,table.b-table.b-table-stacked-sm>caption,table.b-table.b-table-stacked-sm>tbody,table.b-table.b-table-stacked-sm>tbody>tr,table.b-table.b-table-stacked-sm>tbody>tr>td,table.b-table.b-table-stacked-sm>tbody>tr>th{display:block}table.b-table.b-table-stacked-sm>tbody>tr.b-table-bottom-row,table.b-table.b-table-stacked-sm>tbody>tr.b-table-top-row,table.b-table.b-table-stacked-sm>tfoot,table.b-table.b-table-stacked-sm>thead{display:none}table.b-table.b-table-stacked-sm>tbody>tr>:first-child{border-top-width:.4rem}table.b-table.b-table-stacked-sm>tbody>tr>[data-label]{display:grid;grid-template-columns:40% auto;grid-gap:.25rem 1rem}table.b-table.b-table-stacked-sm>tbody>tr>[data-label]:before{content:attr(data-label);display:inline;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal}}@media (max-width:767.99px){table.b-table.b-table-stacked-md{width:100%}table.b-table.b-table-stacked-md,table.b-table.b-table-stacked-md>caption,table.b-table.b-table-stacked-md>tbody,table.b-table.b-table-stacked-md>tbody>tr,table.b-table.b-table-stacked-md>tbody>tr>td,table.b-table.b-table-stacked-md>tbody>tr>th{display:block}table.b-table.b-table-stacked-md>tbody>tr.b-table-bottom-row,table.b-table.b-table-stacked-md>tbody>tr.b-table-top-row,table.b-table.b-table-stacked-md>tfoot,table.b-table.b-table-stacked-md>thead{display:none}table.b-table.b-table-stacked-md>tbody>tr>:first-child{border-top-width:.4rem}table.b-table.b-table-stacked-md>tbody>tr>[data-label]{display:grid;grid-template-columns:40% auto;grid-gap:.25rem 1rem}table.b-table.b-table-stacked-md>tbody>tr>[data-label]:before{content:attr(data-label);display:inline;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal}}@media (max-width:991.99px){table.b-table.b-table-stacked-lg{width:100%}table.b-table.b-table-stacked-lg,table.b-table.b-table-stacked-lg>caption,table.b-table.b-table-stacked-lg>tbody,table.b-table.b-table-stacked-lg>tbody>tr,table.b-table.b-table-stacked-lg>tbody>tr>td,table.b-table.b-table-stacked-lg>tbody>tr>th{display:block}table.b-table.b-table-stacked-lg>tbody>tr.b-table-bottom-row,table.b-table.b-table-stacked-lg>tbody>tr.b-table-top-row,table.b-table.b-table-stacked-lg>tfoot,table.b-table.b-table-stacked-lg>thead{display:none}table.b-table.b-table-stacked-lg>tbody>tr>:first-child{border-top-width:.4rem}table.b-table.b-table-stacked-lg>tbody>tr>[data-label]{display:grid;grid-template-columns:40% auto;grid-gap:.25rem 1rem}table.b-table.b-table-stacked-lg>tbody>tr>[data-label]:before{content:attr(data-label);display:inline;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal}}@media (max-width:1199.99px){table.b-table.b-table-stacked-xl{width:100%}table.b-table.b-table-stacked-xl,table.b-table.b-table-stacked-xl>caption,table.b-table.b-table-stacked-xl>tbody,table.b-table.b-table-stacked-xl>tbody>tr,table.b-table.b-table-stacked-xl>tbody>tr>td,table.b-table.b-table-stacked-xl>tbody>tr>th{display:block}table.b-table.b-table-stacked-xl>tbody>tr.b-table-bottom-row,table.b-table.b-table-stacked-xl>tbody>tr.b-table-top-row,table.b-table.b-table-stacked-xl>tfoot,table.b-table.b-table-stacked-xl>thead{display:none}table.b-table.b-table-stacked-xl>tbody>tr>:first-child{border-top-width:.4rem}table.b-table.b-table-stacked-xl>tbody>tr>[data-label]{display:grid;grid-template-columns:40% auto;grid-gap:.25rem 1rem}table.b-table.b-table-stacked-xl>tbody>tr>[data-label]:before{content:attr(data-label);display:inline;text-align:right;overflow-wrap:break-word;font-weight:700;font-style:normal}}table.b-table>tbody>tr.b-table-details>td{border-top:none}div.jsoneditor .jsoneditor-search input{height:auto;border:inherit}div.jsoneditor .jsoneditor-search input:focus{border:none!important;box-shadow:none!important}div.jsoneditor table{border-collapse:collapse;width:auto}div.jsoneditor td,div.jsoneditor th{padding:0;display:table-cell;text-align:left;vertical-align:inherit;border-radius:inherit}div.jsoneditor-field,div.jsoneditor-readonly,div.jsoneditor-value{border:1px solid transparent;min-height:16px;min-width:32px;padding:2px;margin:1px;word-wrap:break-word;float:left}div.jsoneditor-field p,div.jsoneditor-value p{margin:0}div.jsoneditor-value{word-break:break-word}div.jsoneditor-readonly{min-width:16px;color:gray}div.jsoneditor-empty{border-color:#d3d3d3;border-style:dashed;border-radius:2px}div.jsoneditor-field.jsoneditor-empty:after,div.jsoneditor-value.jsoneditor-empty:after{pointer-events:none;color:#d3d3d3;font-size:8pt}div.jsoneditor-field.jsoneditor-empty:after{content:"field"}div.jsoneditor-value.jsoneditor-empty:after{content:"value"}a.jsoneditor-value.jsoneditor-url,div.jsoneditor-value.jsoneditor-url{color:green;text-decoration:underline}a.jsoneditor-value.jsoneditor-url{display:inline-block;padding:2px;margin:2px}a.jsoneditor-value.jsoneditor-url:focus,a.jsoneditor-value.jsoneditor-url:hover{color:#ee422e}div.jsoneditor td.jsoneditor-separator{padding:3px 0;vertical-align:top;color:gray}div.jsoneditor-field.jsoneditor-highlight,div.jsoneditor-field[contenteditable=true]:focus,div.jsoneditor-field[contenteditable=true]:hover,div.jsoneditor-value.jsoneditor-highlight,div.jsoneditor-value[contenteditable=true]:focus,div.jsoneditor-value[contenteditable=true]:hover{background-color:#ffffab;border:1px solid #ff0;border-radius:2px}div.jsoneditor-field.jsoneditor-highlight-active,div.jsoneditor-field.jsoneditor-highlight-active:focus,div.jsoneditor-field.jsoneditor-highlight-active:hover,div.jsoneditor-value.jsoneditor-highlight-active,div.jsoneditor-value.jsoneditor-highlight-active:focus,div.jsoneditor-value.jsoneditor-highlight-active:hover{background-color:#fe0;border:1px solid #ffc700;border-radius:2px}div.jsoneditor-value.jsoneditor-string{color:green}div.jsoneditor-value.jsoneditor-array,div.jsoneditor-value.jsoneditor-object{min-width:16px;color:gray}div.jsoneditor-value.jsoneditor-number{color:#ee422e}div.jsoneditor-value.jsoneditor-boolean{color:#ff8c00}div.jsoneditor-value.jsoneditor-null{color:#004ed0}div.jsoneditor-value.jsoneditor-invalid{color:#000}div.jsoneditor-tree button{width:24px;height:24px;padding:0;margin:0;border:none;cursor:pointer;background:transparent url(/static/img/jsoneditor-icons.bfab7b1.svg)}div.jsoneditor-mode-form tr.jsoneditor-expandable td.jsoneditor-tree,div.jsoneditor-mode-view tr.jsoneditor-expandable td.jsoneditor-tree{cursor:pointer}div.jsoneditor-tree button.jsoneditor-collapsed{background-position:0 -48px}div.jsoneditor-tree button.jsoneditor-expanded{background-position:0 -72px}div.jsoneditor-tree button.jsoneditor-contextmenu{background-position:-48px -72px}div.jsoneditor-tree button.jsoneditor-contextmenu.jsoneditor-selected,div.jsoneditor-tree button.jsoneditor-contextmenu:focus,div.jsoneditor-tree button.jsoneditor-contextmenu:hover,tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu{background-position:-48px -48px}div.jsoneditor-tree :focus{outline:none}div.jsoneditor-tree button:focus{background-color:#f5f5f5;outline:1px solid #e5e5e5}div.jsoneditor-tree button.jsoneditor-invisible{visibility:hidden;background:none}div.jsoneditor{color:#1a1a1a;border:1px solid #3883fa;box-sizing:border-box;width:100%;height:100%;position:relative;padding:0;line-height:100%}div.jsoneditor-tree table.jsoneditor-tree{border-collapse:collapse;border-spacing:0;width:100%;margin:0}div.jsoneditor-outer{position:static;width:100%;height:100%;margin:-35px 0 0;padding:35px 0 0;box-sizing:border-box}div.jsoneditor-outer.has-nav-bar{margin:-61px 0 0;padding:61px 0 0}div.jsoneditor-outer.has-status-bar{margin:-35px 0 -26px;padding:35px 0 26px}.ace-jsoneditor,textarea.jsoneditor-text{min-height:150px}div.jsoneditor-tree{width:100%;height:100%;position:relative;overflow:auto}textarea.jsoneditor-text{width:100%;height:100%;margin:0;box-sizing:border-box;outline-width:0;border:none;background-color:#fff;resize:none}tr.jsoneditor-highlight,tr.jsoneditor-selected{background-color:#d3d3d3}tr.jsoneditor-selected button.jsoneditor-contextmenu,tr.jsoneditor-selected button.jsoneditor-dragarea{visibility:hidden}tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-contextmenu,tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea{visibility:visible}div.jsoneditor-tree button.jsoneditor-dragarea{background:url(/static/img/jsoneditor-icons.bfab7b1.svg) -72px -72px;cursor:move}div.jsoneditor-tree button.jsoneditor-dragarea:focus,div.jsoneditor-tree button.jsoneditor-dragarea:hover,tr.jsoneditor-selected.jsoneditor-first button.jsoneditor-dragarea{background-position:-72px -48px}div.jsoneditor td,div.jsoneditor th,div.jsoneditor tr{padding:0;margin:0}div.jsoneditor td,div.jsoneditor td.jsoneditor-tree{vertical-align:top}.jsoneditor-schema-error,div.jsoneditor-field,div.jsoneditor-value,div.jsoneditor td,div.jsoneditor textarea,div.jsoneditor th{font-family:dejavu sans mono,droid sans mono,consolas,monaco,lucida console,courier new,courier,monospace,sans-serif;font-size:10pt;color:#1a1a1a}.jsoneditor-schema-error{cursor:default;display:inline-block;height:24px;line-height:24px;position:relative;text-align:center;width:24px}div.jsoneditor-tree .jsoneditor-schema-error{width:24px;height:24px;padding:0;margin:0 4px 0 0;background:url(/static/img/jsoneditor-icons.bfab7b1.svg) -168px -48px}.jsoneditor-schema-error .jsoneditor-popover{background-color:#4c4c4c;border-radius:3px;box-shadow:0 0 5px rgba(0,0,0,.4);color:#fff;display:none;padding:7px 10px;position:absolute;width:200px;z-index:4}.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above{bottom:32px;left:-98px}.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below{top:32px;left:-98px}.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left{top:-7px;right:32px}.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right{top:-7px;left:32px}.jsoneditor-schema-error .jsoneditor-popover:before{border-right:7px solid transparent;border-left:7px solid transparent;content:"";display:block;left:50%;margin-left:-7px;position:absolute}.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-above:before{border-top:7px solid #4c4c4c;bottom:-7px}.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-below:before{border-bottom:7px solid #4c4c4c;top:-7px}.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left:before{border-left:7px solid #4c4c4c;right:-14px;left:inherit}.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-left:before,.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right:before{border-top:7px solid transparent;border-bottom:7px solid transparent;content:"";top:19px;margin-left:inherit;margin-top:-7px;position:absolute}.jsoneditor-schema-error .jsoneditor-popover.jsoneditor-right:before{border-right:7px solid #4c4c4c;left:-14px}.jsoneditor-schema-error:focus .jsoneditor-popover,.jsoneditor-schema-error:hover .jsoneditor-popover{display:block;-webkit-animation:fade-in .3s linear 1,move-up .3s linear 1;-moz-animation:fade-in .3s linear 1,move-up .3s linear 1;-ms-animation:fade-in .3s linear 1,move-up .3s linear 1}.jsoneditor .jsoneditor-text-errors{width:100%;border-collapse:collapse;background-color:#ffef8b;border-top:1px solid gold}.jsoneditor .jsoneditor-text-errors td{padding:3px 6px;vertical-align:middle}.jsoneditor-text-errors .jsoneditor-schema-error{border:none;width:24px;height:24px;padding:0;margin:0 4px 0 0;background:url(/static/img/jsoneditor-icons.bfab7b1.svg) -168px -48px}div.jsoneditor-contextmenu-root{position:relative;width:0;height:0}div.jsoneditor-contextmenu{position:absolute;box-sizing:content-box;z-index:99999}div.jsoneditor-contextmenu li,div.jsoneditor-contextmenu ul{box-sizing:content-box;position:relative}div.jsoneditor-contextmenu ul{position:relative;left:0;top:0;width:128px;background:#fff;border:1px solid #d3d3d3;box-shadow:2px 2px 12px hsla(0,0%,50%,.3);list-style:none;margin:0;padding:0}div.jsoneditor-contextmenu ul li button{position:relative;padding:0 4px 0 0;margin:0;width:128px;height:auto;border:none;cursor:pointer;color:#4d4d4d;background:transparent;font-size:10pt;font-family:arial,sans-serif;box-sizing:border-box;text-align:left}div.jsoneditor-contextmenu ul li button::-moz-focus-inner{padding:0;border:0}div.jsoneditor-contextmenu ul li button:focus,div.jsoneditor-contextmenu ul li button:hover{color:#1a1a1a;background-color:#f5f5f5;outline:none}div.jsoneditor-contextmenu ul li button.jsoneditor-default{width:96px}div.jsoneditor-contextmenu ul li button.jsoneditor-expand{float:right;width:32px;height:24px;border-left:1px solid #e5e5e5}div.jsoneditor-contextmenu div.jsoneditor-icon{position:absolute;top:0;left:0;width:24px;height:24px;border:none;padding:0;margin:0;background-image:url(/static/img/jsoneditor-icons.bfab7b1.svg)}div.jsoneditor-contextmenu ul li ul div.jsoneditor-icon{margin-left:24px}div.jsoneditor-contextmenu div.jsoneditor-text{padding:4px 0 4px 24px;word-wrap:break-word}div.jsoneditor-contextmenu div.jsoneditor-text.jsoneditor-right-margin{padding-right:24px}div.jsoneditor-contextmenu ul li button div.jsoneditor-expand{position:absolute;top:0;right:0;width:24px;height:24px;padding:0;margin:0 4px 0 0;background:url(/static/img/jsoneditor-icons.bfab7b1.svg) 0 -72px;opacity:.4}div.jsoneditor-contextmenu ul li.jsoneditor-selected div.jsoneditor-expand,div.jsoneditor-contextmenu ul li button.jsoneditor-expand:focus div.jsoneditor-expand,div.jsoneditor-contextmenu ul li button.jsoneditor-expand:hover div.jsoneditor-expand,div.jsoneditor-contextmenu ul li button:focus div.jsoneditor-expand,div.jsoneditor-contextmenu ul li button:hover div.jsoneditor-expand{opacity:1}div.jsoneditor-contextmenu div.jsoneditor-separator{height:0;border-top:1px solid #e5e5e5;padding-top:5px;margin-top:5px}div.jsoneditor-contextmenu button.jsoneditor-remove>div.jsoneditor-icon{background-position:-24px -24px}div.jsoneditor-contextmenu button.jsoneditor-remove:focus>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-remove:hover>div.jsoneditor-icon{background-position:-24px 0}div.jsoneditor-contextmenu button.jsoneditor-append>div.jsoneditor-icon{background-position:0 -24px}div.jsoneditor-contextmenu button.jsoneditor-append:focus>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-append:hover>div.jsoneditor-icon{background-position:0 0}div.jsoneditor-contextmenu button.jsoneditor-insert>div.jsoneditor-icon{background-position:0 -24px}div.jsoneditor-contextmenu button.jsoneditor-insert:focus>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-insert:hover>div.jsoneditor-icon{background-position:0 0}div.jsoneditor-contextmenu button.jsoneditor-duplicate>div.jsoneditor-icon{background-position:-48px -24px}div.jsoneditor-contextmenu button.jsoneditor-duplicate:focus>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-duplicate:hover>div.jsoneditor-icon{background-position:-48px 0}div.jsoneditor-contextmenu button.jsoneditor-sort-asc>div.jsoneditor-icon{background-position:-168px -24px}div.jsoneditor-contextmenu button.jsoneditor-sort-asc:focus>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-sort-asc:hover>div.jsoneditor-icon{background-position:-168px 0}div.jsoneditor-contextmenu button.jsoneditor-sort-desc>div.jsoneditor-icon{background-position:-192px -24px}div.jsoneditor-contextmenu button.jsoneditor-sort-desc:focus>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-sort-desc:hover>div.jsoneditor-icon{background-position:-192px 0}div.jsoneditor-contextmenu ul li button.jsoneditor-selected,div.jsoneditor-contextmenu ul li button.jsoneditor-selected:focus,div.jsoneditor-contextmenu ul li button.jsoneditor-selected:hover{color:#fff;background-color:#ee422e}div.jsoneditor-contextmenu ul li{overflow:hidden}div.jsoneditor-contextmenu ul li ul{display:none;position:relative;left:-10px;top:0;border:none;box-shadow:inset 0 0 10px hsla(0,0%,50%,.5);padding:0 10px;transition:all .3s ease-out}div.jsoneditor-contextmenu ul li ul li button{padding-left:24px;animation:all ease-in-out 1s}div.jsoneditor-contextmenu ul li ul li button:focus,div.jsoneditor-contextmenu ul li ul li button:hover{background-color:#f5f5f5}div.jsoneditor-contextmenu button.jsoneditor-type-string>div.jsoneditor-icon{background-position:-144px -24px}div.jsoneditor-contextmenu button.jsoneditor-type-string.jsoneditor-selected>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-type-string:focus>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-type-string:hover>div.jsoneditor-icon{background-position:-144px 0}div.jsoneditor-contextmenu button.jsoneditor-type-auto>div.jsoneditor-icon{background-position:-120px -24px}div.jsoneditor-contextmenu button.jsoneditor-type-auto.jsoneditor-selected>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-type-auto:focus>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-type-auto:hover>div.jsoneditor-icon{background-position:-120px 0}div.jsoneditor-contextmenu button.jsoneditor-type-object>div.jsoneditor-icon{background-position:-72px -24px}div.jsoneditor-contextmenu button.jsoneditor-type-object.jsoneditor-selected>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-type-object:focus>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-type-object:hover>div.jsoneditor-icon{background-position:-72px 0}div.jsoneditor-contextmenu button.jsoneditor-type-array>div.jsoneditor-icon{background-position:-96px -24px}div.jsoneditor-contextmenu button.jsoneditor-type-array.jsoneditor-selected>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-type-array:focus>div.jsoneditor-icon,div.jsoneditor-contextmenu button.jsoneditor-type-array:hover>div.jsoneditor-icon{background-position:-96px 0}div.jsoneditor-contextmenu button.jsoneditor-type-modes>div.jsoneditor-icon{background-image:none;width:6px}div.jsoneditor-menu{width:100%;height:35px;padding:2px;margin:0;box-sizing:border-box;color:#fff;background-color:#3883fa;border-bottom:1px solid #3883fa}div.jsoneditor-menu>button,div.jsoneditor-menu>div.jsoneditor-modes>button{width:26px;height:26px;margin:2px;padding:0;border-radius:2px;border:1px solid transparent;background:transparent url(/static/img/jsoneditor-icons.bfab7b1.svg);color:#fff;opacity:.8;font-family:arial,sans-serif;font-size:10pt;float:left}div.jsoneditor-menu>button:hover,div.jsoneditor-menu>div.jsoneditor-modes>button:hover{background-color:hsla(0,0%,100%,.2);border:1px solid hsla(0,0%,100%,.4)}div.jsoneditor-menu>button:active,div.jsoneditor-menu>button:focus,div.jsoneditor-menu>div.jsoneditor-modes>button:active,div.jsoneditor-menu>div.jsoneditor-modes>button:focus{background-color:hsla(0,0%,100%,.3)}div.jsoneditor-menu>button:disabled,div.jsoneditor-menu>div.jsoneditor-modes>button:disabled{opacity:.5}div.jsoneditor-menu>button.jsoneditor-collapse-all{background-position:0 -96px}div.jsoneditor-menu>button.jsoneditor-expand-all{background-position:0 -120px}div.jsoneditor-menu>button.jsoneditor-undo{background-position:-24px -96px}div.jsoneditor-menu>button.jsoneditor-undo:disabled{background-position:-24px -120px}div.jsoneditor-menu>button.jsoneditor-redo{background-position:-48px -96px}div.jsoneditor-menu>button.jsoneditor-redo:disabled{background-position:-48px -120px}div.jsoneditor-menu>button.jsoneditor-compact{background-position:-72px -96px}div.jsoneditor-menu>button.jsoneditor-format{background-position:-72px -120px}div.jsoneditor-menu>button.jsoneditor-repair{background-position:-96px -96px}div.jsoneditor-menu>div.jsoneditor-modes{display:inline-block;float:left}div.jsoneditor-menu>div.jsoneditor-modes>button{background-image:none;width:auto;padding-left:6px;padding-right:6px}div.jsoneditor-menu>button.jsoneditor-separator,div.jsoneditor-menu>div.jsoneditor-modes>button.jsoneditor-separator{margin-left:10px}div.jsoneditor-menu a{font-family:arial,sans-serif;font-size:10pt;color:#fff;opacity:.8;vertical-align:middle}div.jsoneditor-menu a:hover{opacity:1}div.jsoneditor-menu a.jsoneditor-poweredBy{font-size:8pt;position:absolute;right:0;top:0;padding:10px}table.jsoneditor-search div.jsoneditor-results,table.jsoneditor-search input{font-family:arial,sans-serif;font-size:10pt;color:#1a1a1a;background:transparent}table.jsoneditor-search div.jsoneditor-results{color:#fff;padding-right:5px;line-height:24px}table.jsoneditor-search{position:absolute;right:4px;top:4px;border-collapse:collapse;border-spacing:0}table.jsoneditor-search div.jsoneditor-frame{border:1px solid transparent;background-color:#fff;padding:0 2px;margin:0}table.jsoneditor-search div.jsoneditor-frame table{border-collapse:collapse}table.jsoneditor-search input{width:120px;border:none;outline:none;margin:1px;line-height:20px}table.jsoneditor-search button{width:16px;height:24px;padding:0;margin:0;border:none;background:url(/static/img/jsoneditor-icons.bfab7b1.svg);vertical-align:top}table.jsoneditor-search button:hover{background-color:transparent}table.jsoneditor-search button.jsoneditor-refresh{width:18px;background-position:-99px -73px}table.jsoneditor-search button.jsoneditor-next{cursor:pointer;background-position:-124px -73px}table.jsoneditor-search button.jsoneditor-next:hover{background-position:-124px -49px}table.jsoneditor-search button.jsoneditor-previous{cursor:pointer;background-position:-148px -73px;margin-right:2px}table.jsoneditor-search button.jsoneditor-previous:hover{background-position:-148px -49px}div.jsoneditor div.autocomplete.dropdown{position:absolute;background:#fff;box-shadow:2px 2px 12px hsla(0,0%,50%,.3);border:1px solid #d3d3d3;z-index:100;overflow-x:hidden;overflow-y:auto;cursor:default;margin:0;padding-left:2pt;padding-right:5pt;text-align:left;outline:0;font-family:dejavu sans mono,droid sans mono,consolas,monaco,lucida console,courier new,courier,monospace,sans-serif;font-size:10pt}div.jsoneditor div.autocomplete.dropdown .item{color:#333}div.jsoneditor div.autocomplete.dropdown .item.hover{background-color:#ddd}div.jsoneditor div.autocomplete.hint{color:#aaa;top:4px;left:4px}div.jsoneditor-treepath{padding:0 5px;overflow:hidden}div.jsoneditor-treepath div.jsoneditor-contextmenu-root{position:absolute;left:0}div.jsoneditor-treepath span.jsoneditor-treepath-element{margin:1px;font-family:arial,sans-serif;font-size:10pt}div.jsoneditor-treepath span.jsoneditor-treepath-seperator{margin:2px;font-size:9pt;font-family:arial,sans-serif}div.jsoneditor-treepath span.jsoneditor-treepath-element:hover,div.jsoneditor-treepath span.jsoneditor-treepath-seperator:hover{cursor:pointer;text-decoration:underline}div.jsoneditor-statusbar{line-height:26px;height:26px;margin-top:-1px;color:gray;background-color:#ebebeb;border-top:1px solid #d3d3d3;box-sizing:border-box;font-size:10pt}div.jsoneditor-statusbar>.jsoneditor-curserinfo-label{margin:0 2px 0 4px}div.jsoneditor-statusbar>.jsoneditor-curserinfo-val{margin-right:12px}div.jsoneditor-statusbar>.jsoneditor-curserinfo-count{margin-left:4px}div.jsoneditor-navigation-bar{width:100%;height:26px;line-height:26px;padding:0;margin:0;border-bottom:1px solid #d3d3d3;box-sizing:border-box;color:gray;background-color:#ebebeb;font-size:10pt}div.jsoneditor-navigation-bar.nav-bar-empty:after{content:"Select a node ...";color:hsla(60,7%,38%,.56);position:absolute;margin-left:5px}.toast-title{font-weight:700}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8}.toast-close-button:focus,.toast-close-button:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4}button.toast-close-button{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}.toast-container{position:fixed;z-index:999999;pointer-events:none}.toast-container *{box-sizing:border-box}.toast-container>div{position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;border-radius:3px 3px 3px 3px;background-position:15px;background-repeat:no-repeat;box-shadow:0 0 12px #999;color:#fff;opacity:.8}.toast-container>:hover{box-shadow:0 0 12px #000;opacity:1;cursor:pointer}.toast-container>.toast-info{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=")!important}.toast-container>.toast-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=")!important}.toast-container>.toast-success{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==")!important}.toast-container>.toast-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=")!important}.toast-container.toast-bottom-center>div,.toast-container.toast-top-center>div{width:300px;margin-left:auto;margin-right:auto}.toast-container.toast-bottom-full-width>div,.toast-container.toast-top-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{background-color:#51a351}.toast-error{background-color:#bd362f}.toast-info{background-color:#2f96b4}.toast-warning{background-color:#f89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4}@media (max-width:240px){.toast-container>div{padding:8px 8px 8px 50px;width:11em}.toast-container .toast-close-button{right:-.2em;top:-.2em}}@media (min-width:241px) and (max-width:480px){.toast-container>div{padding:8px 8px 8px 50px;width:18em}.toast-container .toast-close-button{right:-.2em;top:-.2em}}@media (min-width:481px) and (max-width:768px){.toast-container>div{padding:15px 15px 15px 50px;width:25em}}.spanButton{margin-left:10px} ================================================ FILE: tac-console/src/main/resources/static/js/app.606b53e74ca0c7067159.js ================================================ webpackJsonp([1],{"/lMo":function(t,e,n){"use strict";e.a={name:"TacMs",data:function(){return{}}}},0:function(t,e){},"1/oy":function(t,e){},"2Y9o":function(t,e){},"5njQ":function(t,e,n){"use strict";e.a={name:"TacInst"}},"68Sp":function(t,e){t.exports={$schema:"http://json-schema.org/draft-06/schema#",$id:"https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#",description:"Meta-schema for $data reference (JSON-schema extension proposal)",type:"object",required:["$data"],properties:{$data:{type:"string",anyOf:[{format:"relative-json-pointer"},{format:"json-pointer"}]}},additionalProperties:!1}},"9M+g":function(t,e){},Fs8J:function(t,e,n){"use strict";e.a={name:"Home",data:function(){return{msg:"Welcome to Tac App"}}}},GWm9:function(t,e,n){"use strict";var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{},[n("b-navbar",{attrs:{toggleable:"md",type:"dark",variant:"info"}},[n("b-navbar-toggle",{attrs:{target:"nav_collapse"}}),t._v(" "),n("b-navbar-brand",{attrs:{href:"#"}},[t._v("TacAdmin")]),t._v(" "),n("b-collapse",{attrs:{"is-nav":"",id:"nav_collapse"}},[n("b-navbar-nav",[n("b-nav-item",[n("router-link",{attrs:{to:"/"}},[t._v("Home")])],1),t._v(" "),n("b-nav-item",[n("router-link",{attrs:{to:"/tacMs"}},[t._v("TacMicroService")])],1),t._v(" "),n("b-nav-item",{attrs:{href:"#"}},[t._v("Github")])],1)],1)],1),t._v(" "),n("div",{staticClass:"app-container"},[n("router-view")],1)],1)},i=[],a={render:s,staticRenderFns:i};e.a=a},Id91:function(t,e){},"JK/Y":function(t,e,n){"use strict";var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{attrs:{id:"app"}},[n("TacConsole")],1)},i=[],a={render:s,staticRenderFns:i};e.a=a},JhBm:function(t,e){},KJjl:function(t,e){},LJGJ:function(t,e){},M93x:function(t,e,n){"use strict";function s(t){n("kllW")}var i=n("xJD8"),a=n("JK/Y"),r=n("VU/8"),o=s,c=r(i.a,a.a,!1,o,null,null);e.a=c.exports},MyJP:function(t,e){},NHnr:function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var s=n("7+uW"),i=n("e6fC"),a=n("8+8L"),r=n("fGLG"),o=n.n(r),c=n("M93x"),u=n("YaEn"),l=n("qb6w"),d=(n.n(l),n("9M+g")),m=(n.n(d),n("mERs")),f=(n.n(m),n("gkSs")),p=(n.n(f),n("i+dh")),h=n("nyIL"),v=n("MyJP");n.n(v);s.a.use(o.a),s.a.use(i.a),s.a.use(a.a),s.a.component("TacConsole",p.a),s.a.component("TacJSONEditor",h.a),s.a.config.productionTip=!1,new s.a({el:"#app",router:u.a,template:"",components:{App:c.a}})},Pibc:function(t,e,n){"use strict";var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"tacInst"},[n("h1",[t._v("发布流程")]),t._v(" "),n("div",[n("b-alert",{attrs:{show:"",variant:"primary"}},[n("b-badge",{attrs:{variant:"danger"}},[t._v("1")]),t._v("\n 上传编译好的zip文件\n "),n("b-badge",{attrs:{variant:"danger"}},[t._v("2")]),t._v("\n 预发布\n "),n("b-badge",{attrs:{variant:"danger"}},[t._v("3")]),t._v("\n 测试验证\n "),n("b-badge",{attrs:{variant:"danger"}},[t._v("4")]),t._v("\n 线上发布\n "),n("b-badge",{attrs:{variant:"danger"}},[t._v("5")]),t._v("\n 回归验证\n ")],1)],1),t._v(" "),n("router-view")],1)},i=[],a={render:s,staticRenderFns:i};e.a=a},QsjY:function(t,e,n){"use strict";var s=n("Dd8w"),i=n.n(s),a=[{key:"id",label:"实例ID"},{key:"name",label:"实例名称"},{key:"gitBranch",label:"git分支"},{key:"status",label:"状态"},"operation"];e.a={name:"TacInstPublish",data:function(){return{instId:0,msCode:"",prePublish:{jarVersion:""},publish:{jarVersion:""},file:null,fields:a,msInstListItems:[],currentInst:{id:0,name:"",gitBranch:"",action:1}}},mounted:function(){if(this.msCode=this.$route.params.msCode,!this.msCode)return void this.$router.push({path:"/tacMs/list"});this.getMsInstInfo(this.msCode),this.getMsInstList(this.msCode)},methods:{onGitPrePublish:function(t){var e=this;this.$http.get("/api/inst/gitPrePublish",{params:{instId:t}}).then(function(t){t.json().then(function(t){console.log(t),t.success?(e.$toastr.s("预发布成功"),e.getMsInstList(e.msCode)):e.$toastr.e(data.msgInfo)})})},onClickIntEdit:function(t){this.currentInst={name:t.name,gitBranch:t.gitBranch,action:2,id:t.id},this.$refs.modalinst.show()},handleInstOk:function(t){var e=this;t.preventDefault();var n=i()({},this.currentInst),s=n.name,a=n.gitBranch,r=n.id;if(s&&a){var o={name:s,gitBranch:a,msCode:this.msCode,id:r};1==this.currentInst.action?this.$http.post("/api/inst/create",o).then(function(t){t.json().then(function(t){t.success?(e.$toastr.s("创建成功"),e.$refs.modalinst.hide(),e.getMsInstList(e.msCode)):e.$toastr.e(t.msgInfo)})}):(o.id=r,this.$http.post("/api/inst/update",o).then(function(t){t.json().then(function(t){t.success?(e.$refs.modalinst.hide(),e.getMsInstList(e.msCode)):e.$toastr.e(t.msgInfo)})}))}},getMsInstInfo:function(t){var e=this;this.$http.get("/api/inst/info/"+t).then(function(t){t.json().then(function(t){var n=t.data;null!=n&&(e.instId=n.id,e.prePublish.jarVersion=n.prePublishJarVersion,e.publish.jarVersion=n.jarVersion)})})},getMsInstList:function(t){var e=this;this.$http.get("/api/inst/list/"+t).then(function(t){t.json().then(function(t){e.msInstListItems=t.data,console.log(t.data)})})},onPrePublish:function(t){var e=this;if(null==this.file)return void this.$toastr.e("缺少文件");var n=new FormData;n.append("msCode",t),n.append("file",this.file),n.append("instId",this.instId);var s={headers:{"Content-Type":"multipart/form-data"}};this.$http.post("/api/inst/prePublish",n,s).then(function(n){n.json().then(function(n){n.success?(e.getMsInstInfo(t),e.$toastr.s("预发布成功")):e.$toastr.e(n.msgInfo)})})},onPublish:function(t,e){var n=this;this.$http.post("/api/inst/publish",null,{params:{msCode:t,instId:e}}).then(function(t){t.json().then(function(t){t.success?(n.$toastr.s("发布成功"),n.getMsInstList(n.msCode)):n.$toastr.e(t.msgInfo)})})}}}},Qzvh:function(t,e){},R7F2:function(t,e,n){"use strict";var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[n("span",[t._v("服务编码: "+t._s(t.msCode))]),t._v(" "),n("span",[t._v("实例ID: "+t._s(t.instId))]),t._v(" "),n("b-row",[n("b-col",{attrs:{cols:"3"}},[n("span",[t._v("\n 请求参数\n ")]),t._v(" "),n("TacJSONEditor",{ref:"checkParamsEditor"})],1),t._v(" "),n("b-col",{attrs:{cols:"1"}},[n("b-button",{style:{width:"100%"},attrs:{size:"sm",variant:"danger"},on:{click:function(e){t.test()}}},[t._v("\n 测试\n ")])],1),t._v(" "),n("b-col",{attrs:{cols:"8"}},[n("span",[t._v("\n 结果\n ")]),t._v(" "),n("TacJSONEditor",{ref:"checkResultEditor"})],1)],1),t._v(" "),n("hr"),t._v(" "),n("div",[n("h5",[t._v("日志")]),t._v(" "),n("p",{staticClass:"logResult"},[t._v("\n "+t._s(t.logResult)+"\n ")])])],1)},i=[],a={render:s,staticRenderFns:i};e.a=a},UJD0:function(t,e){},WLU6:function(t,e,n){"use strict";function s(t){n("KJjl")}var i=n("/lMo"),a=n("rooJ"),r=n("VU/8"),o=s,c=r(i.a,a.a,!1,o,"data-v-7b1ca360",null);e.a=c.exports},WOXM:function(t,e,n){"use strict";function s(t){n("LJGJ")}var i=n("5njQ"),a=n("Pibc"),r=n("VU/8"),o=s,c=r(i.a,a.a,!1,o,"data-v-3bfbdc0d",null);e.a=c.exports},WSQo:function(t,e,n){"use strict";var s=function(){var t=this,e=t.$createElement;return(t._self._c||e)("div",{ref:"jsoneditor",style:t.styleObj})},i=[],a={render:s,staticRenderFns:i};e.a=a},YCz7:function(t,e,n){"use strict";var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("b-jumbotron",{attrs:{header:"TAC-Console",lead:"The Tangram App Container"}},[n("router-link",{staticClass:"btn btn-primary",attrs:{to:"/tacMs"}},[t._v("TacMicroService")])],1)},i=[],a={render:s,staticRenderFns:i};e.a=a},YaEn:function(t,e,n){"use strict";var s=n("7+uW"),i=n("/ocq"),a=(n("i+dh"),n("lO7g")),r=n("WLU6"),o=n("s22R"),c=n("mQlP"),u=n("WOXM"),l=n("qYvc"),d=n("v276");s.a.component("Home",a.a),s.a.component("TacMs",r.a),s.a.use(i.a),e.a=new i.a({routes:[{path:"/",redirect:"/home"},{path:"/home",name:"home",component:a.a},{path:"/tacMs",redirect:"/tacMs/list",component:r.a,children:[{path:"list",component:o.a},{path:"new",name:"newMs",component:c.a},{path:"edit/:code",name:"editMs",component:c.a}]},{path:"/tacInst",component:u.a,redirect:"/tacMs/list",children:[{name:"msInstPublish",path:"publish/:msCode",component:l.a},{path:"publishcheck",name:"instTest",component:d.a}]}]})},a0Fk:function(t,e,n){"use strict";var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[t.show?n("b-form",{on:{submit:t.onSubmit,reset:t.onReset}},[n("b-form-group",{directives:[{name:"show",rawName:"v-show",value:!1,expression:"false"}],attrs:{label:"服务ID"}},[n("b-form-input",{attrs:{id:"msId",type:"text",readonly:""},model:{value:t.form.id,callback:function(e){t.$set(t.form,"id",e)},expression:"form.id"}})],1),t._v(" "),n("b-form-group",{attrs:{id:"msCode",label:"服务编码",description:"服务编码,唯一"}},[n("b-form-input",{attrs:{id:"msCode",type:"text",required:"",placeholder:"msCode",readonly:t.isEdit},model:{value:t.form.code,callback:function(e){t.$set(t.form,"code",e)},expression:"form.code"}})],1),t._v(" "),n("b-form-group",{attrs:{id:"name",label:"名称",description:"服务名称"}},[n("b-form-input",{attrs:{id:"name",type:"text",required:"",placeholder:"name"},model:{value:t.form.name,callback:function(e){t.$set(t.form,"name",e)},expression:"form.name"}})],1),t._v(" "),n("b-form-group",{attrs:{id:"gitRepo",label:"git仓库",description:"git仓库地址"}},[n("b-form-input",{attrs:{id:"gitRepo",type:"text",placeholder:""},model:{value:t.form.gitRepo,callback:function(e){t.$set(t.form,"gitRepo",e)},expression:"form.gitRepo"}})],1),t._v(" "),n("b-button",{attrs:{type:"submit",variant:"primary"}},[t._v("Submit")]),t._v(" "),n("b-button",{attrs:{type:"reset",variant:"danger"}},[t._v("Reset")])],1):t._e()],1)},i=[],a={render:s,staticRenderFns:i};e.a=a},cGpD:function(t,e,n){"use strict";var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[n("div",{staticClass:"panel panel-warning"},[n("div",{staticClass:"panel-heading"},[n("h3",{staticClass:"panel-title"},[t._v("服务列表")]),t._v(" "),n("router-link",{staticClass:"btn btn-warning",attrs:{to:{name:"newMs"}}},[t._v("新建服务")])],1),t._v(" "),n("div",{staticClass:"panel-body"},[n("b-table",{attrs:{striped:"",hover:"",items:t.items,fields:t.fields},scopedSlots:t._u([{key:"operation",fn:function(e){return[n("b-button-group",{staticClass:"spanButtons",attrs:{size:"sm"}},[n("router-link",{staticClass:"btn btn-warning",attrs:{to:{name:"editMs",params:e.item}}},[t._v("编辑")]),t._v(" "),n("router-link",{staticClass:"btn btn-success",attrs:{to:{name:"msInstPublish",params:{msCode:e.item.code}}}},[t._v("实例发布")])],1)]}}])})],1)])])},i=[],a={render:s,staticRenderFns:i};e.a=a},eA4R:function(t,e,n){"use strict";var s=n("Dd8w"),i=n.n(s);e.a={data:function(){return{form:{id:0,code:"",name:"",gitRepo:""},show:!0,isEdit:!1}},mounted:function(){var t=this.$route.params;t&&t.code&&(this.isEdit=!0,this.form=i()({},this.$route.params))},methods:{handleSave:function(){var t=this,e={id:this.form.id,code:this.form.code,name:this.form.name,gitRepo:this.form.gitRepo};this.$http.post("/api/ms/update",e).then(function(e){e.json().then(function(e){e.success?(t.$toastr.s("保存成功"),t.$router.push({path:"/tacMs/list"})):t.$toastr.e(e.msgInfo)})})},handleCreate:function(){var t=this,e={code:this.form.code,name:this.form.name};this.$http.post("/api/ms/create",e).then(function(e){e.json().then(function(e){e.success?(t.$toastr.s("新增成功"),t.$router.push({path:"/tacMs/list"})):t.$toastr.e(e.msgInfo)})})},onSubmit:function(t){t.preventDefault(),this.isEdit?this.handleSave():this.handleCreate()},onReset:function(t){var e=this;t.preventDefault(),this.form.code="",this.form.name="",this.form.gitRepo="",this.$nextTick(function(){e.show=!0})}}}},gkSs:function(t,e){},"i+dh":function(t,e,n){"use strict";function s(t){n("JhBm")}var i=n("yNMo"),a=n("GWm9"),r=n("VU/8"),o=s,c=r(i.a,a.a,!1,o,"data-v-afa07f3c",null);e.a=c.exports},jEJH:function(t,e,n){"use strict";var s=n("mK26"),i=n.n(s);e.a={name:"TacJSONEditor",props:{styleObj:{type:Object,default:function(){return{width:"100%",height:"500px"}}},options:{type:Object,default:function(){return{modes:["tree","view","code"]}}}},editor:null,data:function(){return{}},methods:{setJSON:function(t){this.editor.set(t),console.log(this)},getJSON:function(){return this.editor.get()}},mounted:function(){var t=this.$refs.jsoneditor;this.editor=new i.a(t,this.options)}}},kLHE:function(t,e,n){"use strict";var s=[],i=[{key:"code",label:"服务编码"},{key:"name",label:"服务名称"},"operation"];e.a={name:"TacMsList",data:function(){return{items:s,fields:i}},methods:{offlineMs:function(t){var e=this;this.$http.post("/api/ms/offline",null,{params:{msCode:t}}).then(function(t){e.loadAllMs()})},loadAllMs:function(){var t=this;this.$http.get("/api/ms/list").then(function(e){e.json().then(function(e){e.success&&(t.items=e.data)})})}},mounted:function(){this.loadAllMs()}}},kgf9:function(t,e,n){"use strict";var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"tacInstPublish"},[n("b-row",[n("b-col",[n("h3",[t._v("服务编码:"+t._s(t.msCode)+" 实例ID: "+t._s(t.instId)+" ")])])],1),t._v(" "),n("hr"),t._v(" "),n("b-row",[n("b-col",[n("b-row",[n("b-col",[n("h4",[t._v("预发布")]),t._v(" 版本: "+t._s(t.prePublish.jarVersion)+" ")]),t._v(" "),n("b-col",[n("b-form-file",{attrs:{state:Boolean(t.file),placeholder:"Choose a file..."},model:{value:t.file,callback:function(e){t.file=e},expression:"file"}})],1),t._v(" "),n("b-col",[n("b-button",{attrs:{size:"sm",variant:"warning"},on:{click:function(e){t.onPrePublish(t.msCode)}}},[t._v("\n 预发布\n ")]),t._v(" "),n("router-link",{attrs:{to:{path:"/tacInst/publishcheck",query:{msCode:t.msCode,instId:t.instId,action:"preTest"}},target:"_blank"}},[n("b-button",{attrs:{size:"sm",variant:"warning"}},[t._v("\n 预发布测试\n ")])],1)],1)],1)],1),t._v(" "),n("b-col",[n("b-row",[n("b-col",[n("h4",[t._v("线上发布:")]),t._v(" 版本: "+t._s(t.publish.jarVersion)+" ")]),t._v(" "),n("b-col",[n("b-button",{attrs:{size:"sm",variant:"danger"},on:{click:function(e){t.onPublish(t.msCode,t.instId)}}},[t._v("\n 正式发布\n ")]),t._v(" "),n("router-link",{attrs:{to:{path:"/tacInst/publishcheck",query:{msCode:t.msCode,instId:t.instId,action:"onlineTest"}},target:"_blank"}},[n("b-button",{attrs:{size:"sm",variant:"danger"}},[t._v("\n 线上回归\n ")])],1)],1)],1)],1)],1),t._v(" "),n("hr"),t._v(" "),n("div",[n("b-row",[n("b-col",[n("h4",[t._v("Git分支实例\n "),n("b-button",{directives:[{name:"b-modal",rawName:"v-b-modal.modal-inst",modifiers:{"modal-inst":!0}}],attrs:{size:"sm",variant:"warning"}},[t._v("\n 新建实例\n ")])],1)])],1),t._v(" "),n("b-row",[n("b-col",[n("div",{staticClass:"panel panel-warning"},[n("div",{staticClass:"panel-body"},[n("b-table",{attrs:{striped:"",hover:"",items:t.msInstListItems,fields:t.fields},scopedSlots:t._u([{key:"status",fn:function(e){return[0==e.item.status?n("span",[t._v("新建")]):1==e.item.status?n("span",[t._v("预发布")]):2==e.item.status?n("span",[t._v("正式发布")]):t._e()]}},{key:"operation",fn:function(e){return[n("b-button-group",{staticClass:"spanButtons",attrs:{size:"sm"}},[n("b-button",{attrs:{size:"sm",variant:"warning"},on:{click:function(n){t.onClickIntEdit(e.item)}}},[t._v("\n 编辑\n ")])],1),t._v(" "),n("b-button-group",{staticClass:"spanButtons",attrs:{size:"sm"}},[n("b-button",{attrs:{size:"sm",variant:"warning"},on:{click:function(n){t.onGitPrePublish(e.item.id)}}},[t._v("\n 预发布\n ")]),t._v(" "),n("router-link",{attrs:{to:{path:"/tacInst/publishcheck",query:{msCode:t.msCode,instId:e.item.id,action:"preTest"}},target:"_blank"}},[n("b-button",{attrs:{size:"sm",variant:"warning"}},[t._v("\n 预发布测试\n ")])],1)],1),t._v(" "),n("b-button-group",{staticClass:"spanButtons",attrs:{size:"sm"}},[n("b-button",{attrs:{size:"sm",variant:"danger"},on:{click:function(n){t.onPublish(t.msCode,e.item.id)}}},[t._v("\n 正式发布\n ")]),t._v(" "),n("router-link",{attrs:{to:{path:"/tacInst/publishcheck",query:{msCode:t.msCode,instId:e.item.id,action:"onlineTest"}},target:"_blank"}},[n("b-button",{attrs:{size:"sm",variant:"danger"}},[t._v("\n 线上回归\n ")])],1)],1)]}}])})],1)])])],1)],1),t._v(" "),n("b-modal",{ref:"modalinst",attrs:{id:"modal-inst",title:"实例"},on:{ok:t.handleInstOk}},[n("form",{on:{submit:function(e){e.stopPropagation(),e.preventDefault(),t.handleInstSubmit(e)}}},[n("b-form-group",{attrs:{id:"name",label:"实例名称",description:""}},[n("b-form-input",{attrs:{type:"text",placeholder:"实例名称"},model:{value:t.currentInst.name,callback:function(e){t.$set(t.currentInst,"name",e)},expression:"currentInst.name"}})],1),t._v(" "),n("b-form-group",{attrs:{id:"gitBranch",label:"git分支",description:""}},[n("b-form-input",{attrs:{type:"text",placeholder:"git分支"},model:{value:t.currentInst.gitBranch,callback:function(e){t.$set(t.currentInst,"gitBranch",e)},expression:"currentInst.gitBranch"}})],1)],1)])],1)},i=[],a={render:s,staticRenderFns:i};e.a=a},kllW:function(t,e){},l8M3:function(t,e){t.exports={$schema:"http://json-schema.org/draft-06/schema#",$id:"http://json-schema.org/draft-06/schema#",title:"Core schema meta-schema",definitions:{schemaArray:{type:"array",minItems:1,items:{$ref:"#"}},nonNegativeInteger:{type:"integer",minimum:0},nonNegativeIntegerDefault0:{allOf:[{$ref:"#/definitions/nonNegativeInteger"},{default:0}]},simpleTypes:{enum:["array","boolean","integer","null","number","object","string"]},stringArray:{type:"array",items:{type:"string"},uniqueItems:!0,default:[]}},type:["object","boolean"],properties:{$id:{type:"string",format:"uri-reference"},$schema:{type:"string",format:"uri"},$ref:{type:"string",format:"uri-reference"},title:{type:"string"},description:{type:"string"},default:{},examples:{type:"array",items:{}},multipleOf:{type:"number",exclusiveMinimum:0},maximum:{type:"number"},exclusiveMaximum:{type:"number"},minimum:{type:"number"},exclusiveMinimum:{type:"number"},maxLength:{$ref:"#/definitions/nonNegativeInteger"},minLength:{$ref:"#/definitions/nonNegativeIntegerDefault0"},pattern:{type:"string",format:"regex"},additionalItems:{$ref:"#"},items:{anyOf:[{$ref:"#"},{$ref:"#/definitions/schemaArray"}],default:{}},maxItems:{$ref:"#/definitions/nonNegativeInteger"},minItems:{$ref:"#/definitions/nonNegativeIntegerDefault0"},uniqueItems:{type:"boolean",default:!1},contains:{$ref:"#"},maxProperties:{$ref:"#/definitions/nonNegativeInteger"},minProperties:{$ref:"#/definitions/nonNegativeIntegerDefault0"},required:{$ref:"#/definitions/stringArray"},additionalProperties:{$ref:"#"},definitions:{type:"object",additionalProperties:{$ref:"#"},default:{}},properties:{type:"object",additionalProperties:{$ref:"#"},default:{}},patternProperties:{type:"object",additionalProperties:{$ref:"#"},default:{}},dependencies:{type:"object",additionalProperties:{anyOf:[{$ref:"#"},{$ref:"#/definitions/stringArray"}]}},propertyNames:{$ref:"#"},const:{},enum:{type:"array",minItems:1,uniqueItems:!0},type:{anyOf:[{$ref:"#/definitions/simpleTypes"},{type:"array",items:{$ref:"#/definitions/simpleTypes"},minItems:1,uniqueItems:!0}]},format:{type:"string"},allOf:{$ref:"#/definitions/schemaArray"},anyOf:{$ref:"#/definitions/schemaArray"},oneOf:{$ref:"#/definitions/schemaArray"},not:{$ref:"#"}},default:{}}},lO7g:function(t,e,n){"use strict";function s(t){n("2Y9o")}var i=n("Fs8J"),a=n("YCz7"),r=n("VU/8"),o=s,c=r(i.a,a.a,!1,o,"data-v-919c02ee",null);e.a=c.exports},mERs:function(t,e){},mQlP:function(t,e,n){"use strict";function s(t){n("Qzvh")}var i=n("eA4R"),a=n("a0Fk"),r=n("VU/8"),o=s,c=r(i.a,a.a,!1,o,"data-v-45bd5a34",null);e.a=c.exports},mbCN:function(t,e){},nyIL:function(t,e,n){"use strict";function s(t){n("UJD0")}var i=n("jEJH"),a=n("WSQo"),r=n("VU/8"),o=s,c=r(i.a,a.a,!1,o,"data-v-3d28f83e",null);e.a=c.exports},q6u6:function(t,e,n){"use strict";n("nyIL");e.a={name:"TacInstPublishCheck",mounted:function(){this.paramsEditor=this.$refs.checkParamsEditor,this.resultEditor=this.$refs.checkResultEditor;var t=this.$route.query,e=t.instId,n=t.msCode,s=t.action;this.instId=e,this.msCode=n,this.action=s},methods:{test:function(){var t=this.paramsEditor.getJSON();"preTest"==this.action?this.handlePreTest(t):this.handleOnlineTest(t)},handlePreTest:function(t){var e=this,n={instId:this.instId,msCode:this.msCode,params:t};this.$http.post("/api/inst/preTest",n).then(function(t){t.json().then(function(t){if(t.success){console.log(t);var n=t.data.msgInfo;t.data.msgInfo="",e.logResult=n,e.resultEditor.setJSON(t.data)}else e.$toastr.e(t.msgInfo)})})},handleOnlineTest:function(t){var e=this,n={instId:this.instId,msCode:this.msCode,params:t};this.$http.post("/api/inst/onlineTest",n).then(function(t){t.json().then(function(t){if(t.success){console.log(t);var n=t.msgInfo;t.msgInfo="",e.logResult=n,e.resultEditor.setJSON(t)}else e.$toastr.e(t.msgInfo)})})}},data:function(){return{instId:0,msCode:"",action:"",logResult:""}}}},qYvc:function(t,e,n){"use strict";function s(t){n("wJiV")}var i=n("QsjY"),a=n("kgf9"),r=n("VU/8"),o=s,c=r(i.a,a.a,!1,o,"data-v-334bc87c",null);e.a=c.exports},qb6w:function(t,e){},rooJ:function(t,e,n){"use strict";var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("b-container",{staticClass:"tacMs"},[n("router-view")],1)},i=[],a={render:s,staticRenderFns:i};e.a=a},s22R:function(t,e,n){"use strict";function s(t){n("mbCN")}var i=n("kLHE"),a=n("cGpD"),r=n("VU/8"),o=s,c=r(i.a,a.a,!1,o,"data-v-5621612b",null);e.a=c.exports},v276:function(t,e,n){"use strict";function s(t){n("wpUF")}var i=n("q6u6"),a=n("R7F2"),r=n("VU/8"),o=s,c=r(i.a,a.a,!1,o,"data-v-13be33e9",null);e.a=c.exports},wJiV:function(t,e){},wpUF:function(t,e){},xJD8:function(t,e,n){"use strict";e.a={name:"app"}},yNMo:function(t,e,n){"use strict";n("lO7g");e.a={name:"TacConsole",data:function(){return{msg:"Welcome to Your Vue.js App"}},mounted:function(){this.$toastr.defaultPosition="toast-top-center"}}},zj2Q:function(t,e){}},["NHnr"]); //# sourceMappingURL=app.606b53e74ca0c7067159.js.map ================================================ FILE: tac-console/src/main/resources/static/js/manifest.58ce01f7a6fd036b4f8d.js ================================================ !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,i){for(var u,a,f,s=0,l=[];s5)i+=" || validate.schema"+a+"["+g+"] ";else{var P=C;if(P)for(var M,I=-1,j=P.length-1;I= "+ve+"; ",l=e.errSchemaPath+"/patternGroups/minimum",i+=" if (!"+h+") { ";var Q=Q||[];Q.push(i),i="",!1!==e.createErrors?(i+=" { keyword: 'patternGroups' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { reason: '"+we+"', limit: "+be+", pattern: '"+e.util.escapeQuotes(U)+"' } ",!1!==e.opts.messages&&(i+=" , message: 'should NOT have "+Ce+" than "+be+' properties matching pattern "'+e.util.escapeQuotes(U)+"\"' "),e.opts.verbose&&(i+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+u+" "),i+=" } "):i+=" {} ";var Y=i;i=Q.pop(),!e.compositeRule&&c?e.async?i+=" throw new ValidationError(["+Y+"]); ":i+=" validate.errors = ["+Y+"]; return false; ":i+=" var err = "+Y+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",i+=" } ",void 0!==ye&&(i+=" else ")}if(void 0!==ye){var be=ye,we="maximum",Ce="more";i+=" "+h+" = pgPropCount"+r+" <= "+ye+"; ",l=e.errSchemaPath+"/patternGroups/maximum",i+=" if (!"+h+") { ";var Q=Q||[];Q.push(i),i="",!1!==e.createErrors?(i+=" { keyword: 'patternGroups' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { reason: '"+we+"', limit: "+be+", pattern: '"+e.util.escapeQuotes(U)+"' } ",!1!==e.opts.messages&&(i+=" , message: 'should NOT have "+Ce+" than "+be+' properties matching pattern "'+e.util.escapeQuotes(U)+"\"' "),e.opts.verbose&&(i+=" , schema: validate.schema"+a+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+u+" "),i+=" } "):i+=" {} ";var Y=i;i=Q.pop(),!e.compositeRule&&c?e.async?i+=" throw new ValidationError(["+Y+"]); ":i+=" validate.errors = ["+Y+"]; return false; ":i+=" var err = "+Y+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",i+=" } "}l=J,c&&(i+=" if ("+h+") { ",p+="}")}}}}return c&&(i+=" "+p+" if ("+d+" == errors) {"),i=e.util.cleanUpCode(i)}},"+E39":function(e,t,n){e.exports=!n("S82l")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},"+FfA":function(e,t,n){"use strict";function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var r=n("2PZM"),o=n("WnFU"),s={fluid:{type:Boolean,default:!1},containerFluid:{type:Boolean,default:!1},header:{type:String,default:null},headerTag:{type:String,default:"h1"},headerLevel:{type:[Number,String],default:"3"},lead:{type:String,default:null},leadTag:{type:String,default:"p"},tag:{type:String,default:"div"},bgVariant:{type:String,default:null},borderVariant:{type:String,default:null},textVariant:{type:String,default:null}};t.a={functional:!0,props:s,render:function(e,t){var s,a=t.props,l=t.data,c=t.slots,u=[],h=c();return(a.header||h.header)&&u.push(e(a.headerTag,{class:i({},"display-"+a.headerLevel,Boolean(a.headerLevel))},h.header||a.header)),(a.lead||h.lead)&&u.push(e(a.leadTag,{staticClass:"lead"},h.lead||a.lead)),h.default&&u.push(h.default),a.fluid&&(u=[e(o.a,{props:{fluid:a.containerFluid}},u)]),e(a.tag,n.i(r.a)(l,{staticClass:"jumbotron",class:(s={"jumbotron-fluid":a.fluid},i(s,"text-"+a.textVariant,Boolean(a.textVariant)),i(s,"bg-"+a.bgVariant,Boolean(a.bgVariant)),i(s,"border-"+a.borderVariant,Boolean(a.borderVariant)),i(s,"border",Boolean(a.borderVariant)),s)}),u)}}},"+LpG":function(e,t,n){"use strict";var i,r=["en","pt-BR"],o={en:{array:"Array",auto:"Auto",appendText:"Append",appendTitle:"Append a new field with type 'auto' after this field (Ctrl+Shift+Ins)",appendSubmenuTitle:"Select the type of the field to be appended",appendTitleAuto:"Append a new field with type 'auto' (Ctrl+Shift+Ins)",ascending:"Ascending",ascendingTitle:"Sort the childs of this ${type} in ascending order",actionsMenu:"Click to open the actions menu (Ctrl+M)",collapseAll:"Collapse all fields",descending:"Descending",descendingTitle:"Sort the childs of this ${type} in descending order",drag:"Drag to move this field (Alt+Shift+Arrows)",duplicateKey:"duplicate key",duplicateText:"Duplicate",duplicateTitle:"Duplicate selected fields (Ctrl+D)",duplicateField:"Duplicate this field (Ctrl+D)",empty:"empty",expandAll:"Expand all fields",expandTitle:"Click to expand/collapse this field (Ctrl+E). \nCtrl+Click to expand/collapse including all childs.",insert:"Insert",insertTitle:"Insert a new field with type 'auto' before this field (Ctrl+Ins)",insertSub:"Select the type of the field to be inserted",object:"Object",redo:"Redo (Ctrl+Shift+Z)",removeText:"Remove",removeTitle:"Remove selected fields (Ctrl+Del)",removeField:"Remove this field (Ctrl+Del)",sort:"Sort",sortTitle:"Sort the childs of this ",string:"String",type:"Type",typeTitle:"Change the type of this field",openUrl:"Ctrl+Click or Ctrl+Enter to open url in new window",undo:"Undo last action (Ctrl+Z)",validationCannotMove:"Cannot move a field into a child of itself",autoType:'Field type "auto". The field type is automatically determined from the value and can be a string, number, boolean, or null.',objectType:'Field type "object". An object contains an unordered set of key/value pairs.',arrayType:'Field type "array". An array contains an ordered collection of values.',stringType:'Field type "string". Field type is not determined from the value, but always returned as string.'},"pt-BR":{array:"Lista",auto:"Automatico",appendText:"Adicionar",appendTitle:"Adicionar novo campo com tipo 'auto' depois deste campo (Ctrl+Shift+Ins)",appendSubmenuTitle:"Selecione o tipo do campo a ser adicionado",appendTitleAuto:"Adicionar novo campo com tipo 'auto' (Ctrl+Shift+Ins)",ascending:"Ascendente",ascendingTitle:"Organizar filhor do tipo ${type} em crescente",actionsMenu:"Clique para abrir o menu de ações (Ctrl+M)",collapseAll:"Fechar todos campos",descending:"Descendente",descendingTitle:"Organizar o filhos do tipo ${type} em decrescente",duplicateKey:"chave duplicada",drag:"Arraste para mover este campo (Alt+Shift+Arrows)",duplicateText:"Duplicar",duplicateTitle:"Duplicar campos selecionados (Ctrl+D)",duplicateField:"Duplicar este campo (Ctrl+D)",empty:"vazio",expandAll:"Expandir todos campos",expandTitle:"Clique para expandir/encolher este campo (Ctrl+E). \nCtrl+Click para expandir/encolher incluindo todos os filhos.",insert:"Inserir",insertTitle:"Inserir um novo campo do tipo 'auto' antes deste campo (Ctrl+Ins)",insertSub:"Selecionar o tipo de campo a ser inserido",object:"Objeto",redo:"Refazer (Ctrl+Shift+Z)",removeText:"Remover",removeTitle:"Remover campos selecionados (Ctrl+Del)",removeField:"Remover este campo (Ctrl+Del)",sort:"Organizar",sortTitle:"Organizar os filhos deste ",string:"Texto",type:"Tipo",typeTitle:"Mudar o tipo deste campo",openUrl:"Ctrl+Click ou Ctrl+Enter para abrir link em nova janela",undo:"Desfazer último ação (Ctrl+Z)",validationCannotMove:"Não pode mover um campo como filho dele mesmo",autoType:'Campo do tipo "auto". O tipo do campo é determinao automaticamente a partir do seu valor e pode ser texto, número, verdade/falso ou nulo.',objectType:'Campo do tipo "objeto". Um objeto contém uma lista de pares com chave e valor.',arrayType:'Campo do tipo "lista". Uma lista contem uma coleção de valores ordenados.',stringType:'Campo do tipo "string". Campo do tipo nao é determinado através do seu valor, mas sempre retornara um texto.'}},s=navigator.language||navigator.userLanguage;i=r.find(function(e){return e===s}),i||(i="en"),e.exports={_locales:r,_defs:o,_lang:i,setLanguage:function(e){if(e){var t=r.find(function(t){return t===e});t?i=t:console.error("Language not found")}},setLanguages:function(e){if(e)for(var t in e){var n=r.find(function(e){return e===t});n||r.push(t),o[t]=Object.assign({},o.en,o[t],e[t])}},translate:function(e,t,n){n||(n=i);var r=o[n][e];if(t)for(e in t)r=r.replace("${"+e+"}",t[e]);return r||e}}},"+ZMJ":function(e,t,n){var i=n("lOnJ");e.exports=function(e,t,n){if(i(e),void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 2:return function(n,i){return e.call(t,n,i)};case 3:return function(n,i,r){return e.call(t,n,i,r)}}return function(){return e.apply(t,arguments)}}},"/CAh":function(e,t,n){"use strict";var i=e.exports=function(){this._cache={}};i.prototype.put=function(e,t){this._cache[e]=t},i.prototype.get=function(e){return this._cache[e]},i.prototype.del=function(e){delete this._cache[e]},i.prototype.clear=function(){this._cache={}}},"/CDJ":function(e,t,n){"use strict";function i(){return{enumerable:!0,configurable:!1,writable:!1}}n.d(t,"a",function(){return r}),n.d(t,"b",function(){return o}),n.d(t,"c",function(){return s}),n.d(t,"e",function(){return a}),n.d(t,"f",function(){return l}),t.d=i,"function"!=typeof Object.assign&&(Object.assign=function(e,t){if(null==e)throw new TypeError("Cannot convert undefined or null to object");for(var n=Object(e),i=1;i-1}function o(e,t){switch(typeof t){case"undefined":return;case"object":return t;case"function":return t(e);case"boolean":return t?e.params:void 0}}function s(e,t){for(var n in t)e[n]=t[n];return e}function a(e,t,n){void 0===t&&(t={});var i,r=n||l;try{i=r(e||"")}catch(e){i={}}for(var o in t)i[o]=t[o];return i}function l(e){var t={};return(e=e.trim().replace(/^(\?|#|&)/,""))?(e.split("&").forEach(function(e){var n=e.replace(/\+/g," ").split("="),i=Ne(n.shift()),r=n.length>0?Ne(n.join("=")):null;void 0===t[i]?t[i]=r:Array.isArray(t[i])?t[i].push(r):t[i]=[t[i],r]}),t):t}function c(e){var t=e?Object.keys(e).map(function(t){var n=e[t];if(void 0===n)return"";if(null===n)return je(t);if(Array.isArray(n)){var i=[];return n.forEach(function(e){void 0!==e&&(null===e?i.push(je(t)):i.push(je(t)+"="+je(e)))}),i.join("&")}return je(t)+"="+je(n)}).filter(function(e){return e.length>0}).join("&"):null;return t?"?"+t:""}function u(e,t,n,i){var r=i&&i.options.stringifyQuery,o=t.query||{};try{o=h(o)}catch(e){}var s={name:t.name||e&&e.name,meta:e&&e.meta||{},path:t.path||"/",hash:t.hash||"",query:o,params:t.params||{},fullPath:f(t,r),matched:e?d(e):[]};return n&&(s.redirectedFrom=f(n,r)),Object.freeze(s)}function h(e){if(Array.isArray(e))return e.map(h);if(e&&"object"==typeof e){var t={};for(var n in e)t[n]=h(e[n]);return t}return e}function d(e){for(var t=[];e;)t.unshift(e),e=e.parent;return t}function f(e,t){var n=e.path,i=e.query;void 0===i&&(i={});var r=e.hash;void 0===r&&(r="");var o=t||c;return(n||"/")+o(i)+r}function p(e,t){return t===Ve?e===t:!!t&&(e.path&&t.path?e.path.replace(He,"")===t.path.replace(He,"")&&e.hash===t.hash&&m(e.query,t.query):!(!e.name||!t.name)&&(e.name===t.name&&e.hash===t.hash&&m(e.query,t.query)&&m(e.params,t.params)))}function m(e,t){if(void 0===e&&(e={}),void 0===t&&(t={}),!e||!t)return e===t;var n=Object.keys(e),i=Object.keys(t);return n.length===i.length&&n.every(function(n){var i=e[n],r=t[n];return"object"==typeof i&&"object"==typeof r?m(i,r):String(i)===String(r)})}function g(e,t){return 0===e.path.replace(He,"/").indexOf(t.path.replace(He,"/"))&&(!t.hash||e.hash===t.hash)&&v(e.query,t.query)}function v(e,t){for(var n in t)if(!(n in e))return!1;return!0}function y(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey||e.defaultPrevented||void 0!==e.button&&0!==e.button)){if(e.currentTarget&&e.currentTarget.getAttribute){if(/\b_blank\b/i.test(e.currentTarget.getAttribute("target")))return}return e.preventDefault&&e.preventDefault(),!0}}function b(e){if(e)for(var t,n=0;n=0&&(t=e.slice(i),e=e.slice(0,i));var r=e.indexOf("?");return r>=0&&(n=e.slice(r+1),e=e.slice(0,r)),{path:e,query:n,hash:t}}function E(e){return e.replace(/\/\//g,"/")}function x(e,t){for(var n,i=[],r=0,o=0,s="",a=t&&t.delimiter||"/";null!=(n=Ze.exec(e));){var l=n[0],c=n[1],u=n.index;if(s+=e.slice(o,u),o=u+l.length,c)s+=c[1];else{var h=e[o],d=n[2],f=n[3],p=n[4],m=n[5],g=n[6],v=n[7];s&&(i.push(s),s="");var y=null!=d&&null!=h&&h!==d,b="+"===g||"*"===g,w="?"===g||"*"===g,C=n[2]||a,A=p||m;i.push({name:f||r++,prefix:d||"",delimiter:C,optional:w,repeat:b,partial:y,asterisk:!!v,pattern:A?B(A):v?".*":"[^"+_(C)+"]+?"})}}return o-1&&(r.params[d]=n.params[d]);if(a)return r.path=I(a.path,r.params,'named route "'+o+'"'),s(a,r,i)}else if(r.path){r.params={};for(var f=0;f=e.length?n():e[r]?t(e[r],function(){i(r+1)}):i(r+1)};i(0)}function ue(e){return function(t,n,i){var o=!1,s=0,a=null;he(e,function(e,t,n,l){if("function"==typeof e&&void 0===e.cid){o=!0,s++;var c,u=pe(function(t){fe(t)&&(t=t.default),e.resolved="function"==typeof t?t:Oe.extend(t),n.components[l]=t,--s<=0&&i()}),h=pe(function(e){var t="Failed to resolve async component "+l+": "+e;a||(a=r(e)?e:new Error(t),i(a))});try{c=e(u,h)}catch(e){h(e)}if(c)if("function"==typeof c.then)c.then(u,h);else{var d=c.component;d&&"function"==typeof d.then&&d.then(u,h)}}}),o||i()}}function he(e,t){return de(e.map(function(e){return Object.keys(e.components).map(function(n){return t(e.components[n],e.instances[n],e,n)})}))}function de(e){return Array.prototype.concat.apply([],e)}function fe(e){return e.__esModule||ot&&"Module"===e[Symbol.toStringTag]}function pe(e){var t=!1;return function(){for(var n=[],i=arguments.length;i--;)n[i]=arguments[i];if(!t)return t=!0,e.apply(this,n)}}function me(e){if(!e)if(Ke){var t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^https?:\/\/[^\/]+/,"")}else e="/";return"/"!==e.charAt(0)&&(e="/"+e),e.replace(/\/$/,"")}function ge(e,t){var n,i=Math.max(e.length,t.length);for(n=0;n=0?t.slice(0,n):t)+"#"+e}function Be(e){nt?ae(_e(e)):window.location.hash=e}function De(e){nt?le(_e(e)):window.location.replace(_e(e))}function Te(e,t){return e.push(t),function(){var n=e.indexOf(t);n>-1&&e.splice(n,1)}}function Le(e,t,n){var i="hash"===n?"#"+t:t;return e?E(e+"/"+i):i}var Oe,Re={name:"router-view",functional:!0,props:{name:{type:String,default:"default"}},render:function(e,t){var n=t.props,i=t.children,r=t.parent,a=t.data;a.routerView=!0;for(var l=r.$createElement,c=n.name,u=r.$route,h=r._routerViewCache||(r._routerViewCache={}),d=0,f=!1;r&&r._routerRoot!==r;)r.$vnode&&r.$vnode.data.routerView&&d++,r._inactive&&(f=!0),r=r.$parent;if(a.routerViewDepth=d,f)return l(h[c],a,i);var p=u.matched[d];if(!p)return h[c]=null,l();var m=h[c]=p.components[c];a.registerRouteInstance=function(e,t){var n=p.instances[c];(t&&n!==e||!t&&n===e)&&(p.instances[c]=t)},(a.hook||(a.hook={})).prepatch=function(e,t){p.instances[c]=t.componentInstance};var g=a.props=o(u,p.props&&p.props[c]);if(g){g=a.props=s({},g);var v=a.attrs=a.attrs||{};for(var y in g)m.props&&y in m.props||(v[y]=g[y],delete g[y])}return l(m,a,i)}},Pe=/[!'()*]/g,Me=function(e){return"%"+e.charCodeAt(0).toString(16)},Ie=/%2C/g,je=function(e){return encodeURIComponent(e).replace(Pe,Me).replace(Ie,",")},Ne=decodeURIComponent,He=/\/?$/,Ve=u(null,{path:"/"}),We=[String,Object],ze=[String,Array],Ue={name:"router-link",props:{to:{type:We,required:!0},tag:{type:String,default:"a"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,event:{type:ze,default:"click"}},render:function(e){var t=this,n=this.$router,i=this.$route,r=n.resolve(this.to,i,this.append),o=r.location,s=r.route,a=r.href,l={},c=n.options.linkActiveClass,h=n.options.linkExactActiveClass,d=null==c?"router-link-active":c,f=null==h?"router-link-exact-active":h,m=null==this.activeClass?d:this.activeClass,v=null==this.exactActiveClass?f:this.exactActiveClass,w=o.path?u(null,o,null,n):s;l[v]=p(i,w),l[m]=this.exact?l[v]:g(i,w);var C=function(e){y(e)&&(t.replace?n.replace(o):n.push(o))},A={click:y};Array.isArray(this.event)?this.event.forEach(function(e){A[e]=C}):A[this.event]=C;var E={class:l};if("a"===this.tag)E.on=A,E.attrs={href:a};else{var x=b(this.$slots.default);if(x){x.isStatic=!1;var F=Oe.util.extend;(x.data=F({},x.data)).on=A;(x.data.attrs=F({},x.data.attrs)).href=a}else E.on=A}return e(this.tag,E,this.$slots.default)}},Ke="undefined"!=typeof window,qe=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)},Ge=M,Je=x,Qe=F,Ye=k,Xe=P,Ze=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");Ge.parse=Je,Ge.compile=Qe,Ge.tokensToFunction=Ye,Ge.tokensToRegExp=Xe;var et=Object.create(null),tt=Object.create(null),nt=Ke&&function(){var e=window.navigator.userAgent;return(-1===e.indexOf("Android 2.")&&-1===e.indexOf("Android 4.0")||-1===e.indexOf("Mobile Safari")||-1!==e.indexOf("Chrome")||-1!==e.indexOf("Windows Phone"))&&(window.history&&"pushState"in window.history)}(),it=Ke&&window.performance&&window.performance.now?window.performance:Date,rt=re(),ot="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag,st=function(e,t){this.router=e,this.base=me(t),this.current=Ve,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[]};st.prototype.listen=function(e){this.cb=e},st.prototype.onReady=function(e,t){this.ready?e():(this.readyCbs.push(e),t&&this.readyErrorCbs.push(t))},st.prototype.onError=function(e){this.errorCbs.push(e)},st.prototype.transitionTo=function(e,t,n){var i=this,r=this.router.match(e,this.current);this.confirmTransition(r,function(){i.updateRoute(r),t&&t(r),i.ensureURL(),i.ready||(i.ready=!0,i.readyCbs.forEach(function(e){e(r)}))},function(e){n&&n(e),e&&!i.ready&&(i.ready=!0,i.readyErrorCbs.forEach(function(t){t(e)}))})},st.prototype.confirmTransition=function(e,t,n){var o=this,s=this.current,a=function(e){r(e)&&(o.errorCbs.length?o.errorCbs.forEach(function(t){t(e)}):(i(!1,"uncaught error during route navigation:"),console.error(e))),n&&n(e)};if(p(e,s)&&e.matched.length===s.matched.length)return this.ensureURL(),a();var l=ge(this.current.matched,e.matched),c=l.updated,u=l.deactivated,h=l.activated,d=[].concat(be(u),this.router.beforeHooks,we(c),h.map(function(e){return e.beforeEnter}),ue(h));this.pending=e;var f=function(t,n){if(o.pending!==e)return a();try{t(e,s,function(e){!1===e||r(e)?(o.ensureURL(!0),a(e)):"string"==typeof e||"object"==typeof e&&("string"==typeof e.path||"string"==typeof e.name)?(a(),"object"==typeof e&&e.replace?o.replace(e):o.push(e)):n(e)})}catch(e){a(e)}};ce(d,f,function(){var n=[];ce(Ae(h,n,function(){return o.current===e}).concat(o.router.resolveHooks),f,function(){if(o.pending!==e)return a();o.pending=null,t(e),o.router.app&&o.router.app.$nextTick(function(){n.forEach(function(e){e()})})})})},st.prototype.updateRoute=function(e){var t=this.current;this.current=e,this.cb&&this.cb(e),this.router.afterHooks.forEach(function(n){n&&n(e,t)})};var at=function(e){function t(t,n){var i=this;e.call(this,t,n);var r=t.options.scrollBehavior;r&&G();var o=Fe(this.base);window.addEventListener("popstate",function(e){var n=i.current,s=Fe(i.base);i.current===Ve&&s===o||i.transitionTo(s,function(e){r&&J(t,e,n,!0)})})}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.go=function(e){window.history.go(e)},t.prototype.push=function(e,t,n){var i=this,r=this,o=r.current;this.transitionTo(e,function(e){ae(E(i.base+e.fullPath)),J(i.router,e,o,!1),t&&t(e)},n)},t.prototype.replace=function(e,t,n){var i=this,r=this,o=r.current;this.transitionTo(e,function(e){le(E(i.base+e.fullPath)),J(i.router,e,o,!1),t&&t(e)},n)},t.prototype.ensureURL=function(e){if(Fe(this.base)!==this.current.fullPath){var t=E(this.base+this.current.fullPath);e?ae(t):le(t)}},t.prototype.getCurrentLocation=function(){return Fe(this.base)},t}(st),lt=function(e){function t(t,n,i){e.call(this,t,n),i&&Se(this.base)||$e()}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.setupListeners=function(){var e=this,t=this.router,n=t.options.scrollBehavior,i=nt&&n;i&&G(),window.addEventListener(nt?"popstate":"hashchange",function(){var t=e.current;$e()&&e.transitionTo(ke(),function(n){i&&J(e.router,n,t,!0),nt||De(n.fullPath)})})},t.prototype.push=function(e,t,n){var i=this,r=this,o=r.current;this.transitionTo(e,function(e){Be(e.fullPath),J(i.router,e,o,!1),t&&t(e)},n)},t.prototype.replace=function(e,t,n){var i=this,r=this,o=r.current;this.transitionTo(e,function(e){De(e.fullPath),J(i.router,e,o,!1),t&&t(e)},n)},t.prototype.go=function(e){window.history.go(e)},t.prototype.ensureURL=function(e){var t=this.current.fullPath;ke()!==t&&(e?Be(t):De(t))},t.prototype.getCurrentLocation=function(){return ke()},t}(st),ct=function(e){function t(t,n){e.call(this,t,n),this.stack=[],this.index=-1}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.push=function(e,t,n){var i=this;this.transitionTo(e,function(e){i.stack=i.stack.slice(0,i.index+1).concat(e),i.index++,t&&t(e)},n)},t.prototype.replace=function(e,t,n){var i=this;this.transitionTo(e,function(e){i.stack=i.stack.slice(0,i.index).concat(e),t&&t(e)},n)},t.prototype.go=function(e){var t=this,n=this.index+e;if(!(n<0||n>=this.stack.length)){var i=this.stack[n];this.confirmTransition(i,function(){t.index=n,t.updateRoute(i)})}},t.prototype.getCurrentLocation=function(){var e=this.stack[this.stack.length-1];return e?e.fullPath:"/"},t.prototype.ensureURL=function(){},t}(st),ut=function(e){void 0===e&&(e={}),this.app=null,this.apps=[],this.options=e,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=U(e.routes||[],this);var t=e.mode||"hash";switch(this.fallback="history"===t&&!nt&&!1!==e.fallback,this.fallback&&(t="hash"),Ke||(t="abstract"),this.mode=t,t){case"history":this.history=new at(this,e.base);break;case"hash":this.history=new lt(this,e.base,this.fallback);break;case"abstract":this.history=new ct(this,e.base)}},ht={currentRoute:{configurable:!0}};ut.prototype.match=function(e,t,n){return this.matcher.match(e,t,n)},ht.currentRoute.get=function(){return this.history&&this.history.current},ut.prototype.init=function(e){var t=this;if(this.apps.push(e),!this.app){this.app=e;var n=this.history;if(n instanceof at)n.transitionTo(n.getCurrentLocation());else if(n instanceof lt){var i=function(){n.setupListeners()};n.transitionTo(n.getCurrentLocation(),i,i)}n.listen(function(e){t.apps.forEach(function(t){t._route=e})})}},ut.prototype.beforeEach=function(e){return Te(this.beforeHooks,e)},ut.prototype.beforeResolve=function(e){return Te(this.resolveHooks,e)},ut.prototype.afterEach=function(e){return Te(this.afterHooks,e)},ut.prototype.onReady=function(e,t){this.history.onReady(e,t)},ut.prototype.onError=function(e){this.history.onError(e)},ut.prototype.push=function(e,t,n){this.history.push(e,t,n)},ut.prototype.replace=function(e,t,n){this.history.replace(e,t,n)},ut.prototype.go=function(e){this.history.go(e)},ut.prototype.back=function(){this.go(-1)},ut.prototype.forward=function(){this.go(1)},ut.prototype.getMatchedComponents=function(e){var t=e?e.matched?e:this.resolve(e).route:this.currentRoute;return t?[].concat.apply([],t.matched.map(function(e){return Object.keys(e.components).map(function(t){return e.components[t]})})):[]},ut.prototype.resolve=function(e,t,n){var i=W(e,t||this.history.current,n,this),r=this.match(i,t),o=r.redirectedFrom||r.fullPath;return{location:i,route:r,href:Le(this.history.base,o,this.mode),normalizedTo:i,resolved:r}},ut.prototype.addRoutes=function(e){this.matcher.addRoutes(e),this.history.current!==Ve&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(ut.prototype,ht),ut.install=w,ut.version="3.0.1",Ke&&window.Vue&&window.Vue.use(ut),t.a=ut},"/sUu":function(e,t,n){"use strict";function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var r=n("2PZM"),o=n("7+8w"),s=n("ab1q"),a=n("m9Yj"),l=n("il3A"),c=n("/CDJ"),u=n("Ch1r"),h=n("Z09Z"),d=n("2I3h"),f=n("X/jh"),p=n("WF/B"),m=n.i(a.a)(p.b,o.a.bind(null,"img"));m.imgSrc.required=!1;var g=n.i(c.a)({},h.b,d.b,f.b,m,n.i(a.a)(u.a.props),{align:{type:String,default:null},noBody:{type:Boolean,default:!1}});t.a={functional:!0,props:g,render:function(e,t){var o,a=t.props,c=t.data,u=t.slots,g=[],v=u(),y=a.imgSrc?e(p.a,{props:n.i(l.a)(m,a,s.a.bind(null,"img"))}):null;return y&&(!a.imgTop&&a.imgBottom||g.push(y)),(a.header||v.header)&&g.push(e(d.a,{props:n.i(l.a)(d.b,a)},v.header)),a.noBody?g.push(v.default):g.push(e(h.a,{props:n.i(l.a)(h.b,a)},v.default)),(a.footer||v.footer)&&g.push(e(f.a,{props:n.i(l.a)(f.b,a)},v.footer)),y&&a.imgBottom&&g.push(y),e(a.tag,n.i(r.a)(c,{staticClass:"card",class:(o={},i(o,"text-"+a.align,Boolean(a.align)),i(o,"bg-"+a.bgVariant,Boolean(a.bgVariant)),i(o,"border-"+a.borderVariant,Boolean(a.borderVariant)),i(o,"text-"+a.textVariant,Boolean(a.textVariant)),o)}),g)}}},"04Eq":function(e,t,n){"use strict";function i(e,t,n,s,a,l,c,u,h){if(n&&"object"==typeof n&&!Array.isArray(n)){t(n,s,a,l,c,u,h);for(var d in n){var f=n[d];if(Array.isArray(f)){if(d in o.arrayKeywords)for(var p=0;p=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n("mypn"),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(t,n("DuR2"))},"1etr":function(e,t,n){"use strict";var i=n("KXjV"),r=n("q21c"),o={bBadge:i.a},s={install:function(e){n.i(r.c)(e,o)}};n.i(r.a)(s),t.a=s},"1kS7":function(e,t){t.f=Object.getOwnPropertySymbols},"1m3n":function(e,t,n){"use strict";var i=n("2PZM"),r=n("GnGf"),o=n("/CDJ"),s=n("Teo5"),a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},l={items:{type:Array,default:null}};t.a={functional:!0,props:l,render:function(e,t){var l=t.props,c=t.data,u=t.children,h=u;if(n.i(r.b)(l.items)){var d=!1;h=l.items.map(function(t,i){"object"!==(void 0===t?"undefined":a(t))&&(t={text:t});var r=t.active;return r&&(d=!0),r||d||(r=i+1===l.items.length),e(s.a,{props:n.i(o.a)({},t,{active:r})})})}return e("ol",n.i(i.a)(c,{staticClass:"breadcrumb"}),h)}}},"1nuA":function(e,t,n){"use strict";t.decode=t.parse=n("kMPS"),t.encode=t.stringify=n("xaZU")},"2I3h":function(e,t,n){"use strict";function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,"b",function(){return c});var r=n("2PZM"),o=n("7+8w"),s=n("m9Yj"),a=n("/CDJ"),l=n("Ch1r"),c=n.i(a.a)({},n.i(s.a)(l.a.props,o.a.bind(null,"header")),{header:{type:String,default:null},headerClass:{type:[String,Object,Array],default:null}});t.a={functional:!0,props:c,render:function(e,t){var o,s=t.props,a=t.data,l=t.slots;return e(s.headerTag,n.i(r.a)(a,{staticClass:"card-header",class:[s.headerClass,(o={},i(o,"bg-"+s.headerBgVariant,Boolean(s.headerBgVariant)),i(o,"border-"+s.headerBorderVariant,Boolean(s.headerBorderVariant)),i(o,"text-"+s.headerTextVariant,Boolean(s.headerTextVariant)),o)]}),l().default||[e("div",{domProps:{innerHTML:s.header}})])}}},"2PZM":function(e,t,n){"use strict";function i(){for(var e,t,n={},i=arguments.length;i--;)for(var o=0,s=Object.keys(arguments[i]);othis.results.length-1&&(t=0),this._setActiveResult(t,e)}},i.prototype.previous=function(e){if(void 0!=this.results){var t=this.results.length-1,n=void 0!=this.resultIndex?this.resultIndex-1:t;n<0&&(n=t),this._setActiveResult(n,e)}},i.prototype._setActiveResult=function(e,t){if(this.activeResult){var n=this.activeResult.node;"field"==this.activeResult.elem?delete n.searchFieldActive:delete n.searchValueActive,n.updateDom()}if(!this.results||!this.results[e])return this.resultIndex=void 0,void(this.activeResult=void 0);this.resultIndex=e;var i=this.results[this.resultIndex].node,r=this.results[this.resultIndex].elem;"field"==r?i.searchFieldActive=!0:i.searchValueActive=!0,this.activeResult=this.results[this.resultIndex],i.updateDom(),i.scrollTo(function(){t&&i.focus(r)})},i.prototype._clearDelay=function(){void 0!=this.timeout&&(clearTimeout(this.timeout),delete this.timeout)},i.prototype._onDelayedSearch=function(e){this._clearDelay();var t=this;this.timeout=setTimeout(function(e){t._onSearch()},this.delay)},i.prototype._onSearch=function(e){this._clearDelay();var t=this.dom.search.value,n=t.length>0?t:void 0;if(n!=this.lastText||e)if(this.lastText=n,this.results=this.editor.search(n),this._setActiveResult(void 0),void 0!=n){var i=this.results.length;switch(i){case 0:this.dom.results.innerHTML="no results";break;case 1:this.dom.results.innerHTML="1 result";break;default:this.dom.results.innerHTML=i+" results"}}else this.dom.results.innerHTML=""},i.prototype._onKeyDown=function(e){var t=e.which;27==t?(this.dom.search.value="",this._onSearch(),e.preventDefault(),e.stopPropagation()):13==t&&(e.ctrlKey?this._onSearch(!0):e.shiftKey?this.previous():this.next(),e.preventDefault(),e.stopPropagation())},i.prototype._onKeyUp=function(e){var t=e.keyCode;27!=t&&13!=t&&this._onDelayedSearch(e)},i.prototype.clear=function(){this.dom.search.value="",this._onSearch()},i.prototype.destroy=function(){this.editor=null,this.dom.container.removeChild(this.dom.table),this.dom=null,this.results=null,this.activeResult=null,this._clearDelay()},e.exports=i},"3+B3":function(e,t,n){"use strict";var i=n("OfYj"),r=n("wen1"),o=n("/gqF"),s=n("60E9"),a=n("TMTb"),l=n("dfnb"),c=n("GnGf");t.a={mixins:[i.a,o.a,s.a,a.a,l.a,r.a],render:function(e){var t=this,i=t.$slots,r=t.formOptions.map(function(t,n){return e("option",{key:"option_"+n+"_opt",attrs:{disabled:Boolean(t.disabled)},domProps:{innerHTML:t.text,value:t.value}})});return e("select",{ref:"input",class:t.inputClass,directives:[{name:"model",rawName:"v-model",value:t.localValue,expression:"localValue"}],attrs:{id:t.safeId(),name:t.name,multiple:t.multiple||null,size:t.isMultiple?t.selectSize:null,disabled:t.disabled,required:t.required,"aria-required":t.required?"true":null,"aria-invalid":t.computedAriaInvalid},on:{change:function(e){var i=e.target,r=n.i(c.a)(i.options).filter(function(e){return e.selected}).map(function(e){return"_value"in e?e._value:e.value});t.localValue=i.multiple?r:r[0],t.$emit("change",t.localValue)}}},[i.first,r,i.default])},data:function(){return{localValue:this.value}},watch:{value:function(e,t){this.localValue=e},localValue:function(e,t){this.$emit("input",this.localValue)}},props:{value:{},multiple:{type:Boolean,default:!1},selectSize:{type:Number,default:0},ariaInvalid:{type:[Boolean,String],default:!1}},computed:{inputClass:function(){return["form-control",this.stateClass,this.sizeFormClass,this.isPlain?null:"custom-select",this.isPlain||!this.size?null:"custom-select-"+this.size]},isPlain:function(){return this.plain||this.isMultiple},isMultiple:function(){return Boolean(this.multiple&&this.selectSize>1)},computedAriaInvalid:function(){return!0===this.ariaInvalid||"true"===this.ariaInvalid?"true":"is-invalid"===this.stateClass?"true":null}}}},"33mL":function(e,t,n){"use strict";var i=n("WnFU"),r=n("ghhx"),o=n("UgDT"),s=n("yCm2"),a=n("q21c"),l={bContainer:i.a,bRow:r.a,bCol:o.a,bFormRow:s.a},c={install:function(e){n.i(a.c)(e,l)}};n.i(a.a)(c),t.a=c},"34sA":function(e,t,n){"use strict";function i(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t1?n-1:0),o=1;o0;){var e=this[o].shift(),t=e.event,i=e.callback;this.$root.$off(t,i)}}}},"3Eo+":function(e,t){var n=0,i=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+i).toString(36))}},"3IRH":function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},"3oid":function(e,t,n){"use strict";var i=n("uM87"),r=n("q21c"),o={bButtonToolbar:i.a,bBtnToolbar:i.a},s={install:function(e){n.i(r.c)(e,o)}};n.i(r.a)(s),t.a=s},"3pcS":function(e,t,n){"use strict";e.exports=function(e,t,n){function i(e){for(var t=e.rules,n=0;n1&&void 0!==arguments[1]?arguments[1]:{};if(i(this,e),!t)throw new TypeError("Failed to construct '"+this.constructor.name+"'. 1 argument required, "+arguments.length+" given.");n.i(r.a)(this,e.defaults(),o,{type:t}),n.i(r.c)(this,{type:n.i(r.d)(),cancelable:n.i(r.d)(),nativeEvent:n.i(r.d)(),target:n.i(r.d)(),relatedTarget:n.i(r.d)(),vueTarget:n.i(r.d)()});var s=!1;this.preventDefault=function(){this.cancelable&&(s=!0)},n.i(r.e)(this,"defaultPrevented",{enumerable:!0,get:function(){return s}})}return o(e,null,[{key:"defaults",value:function(){return{type:"",cancelable:!0,nativeEvent:null,target:null,relatedTarget:null,vueTarget:null}}}]),e}();t.a=s},"5osV":function(e,t,n){"use strict";function i(e,t,i,s){var a=n.i(r.b)(t.modifiers||{}).filter(function(e){return!o[e]});t.value&&a.push(t.value);var l=function(){s({targets:a,vnode:e})};return n.i(r.b)(o).forEach(function(n){(i[n]||t.modifiers[n])&&e.elm.addEventListener(n,l)}),a}t.a=i;var r=n("/CDJ"),o={hover:!0,click:!0,focus:!0}},"60E9":function(e,t,n){"use strict";t.a={props:{size:{type:String,default:null}},computed:{sizeFormClass:function(){return[this.size?"form-control-"+this.size:null]},sizeBtnClass:function(){return[this.size?"btn-"+this.size:null]}}}},"6nDI":function(e,t,n){"use strict";e.exports=function(e,t,n){var i=" ",r=e.level,o=e.dataLevel,s=e.schema[t],a=e.schemaPath+e.util.getProperty(t),l=e.errSchemaPath+"/"+t,c=!e.opts.allErrors,u="data"+(o||"");if(!1===e.opts.format)return c&&(i+=" if (true) { "),i;var h,d=e.opts.$data&&s&&s.$data;d?(i+=" var schema"+r+" = "+e.util.getData(s.$data,o,e.dataPathArr)+"; ",h="schema"+r):h=s;var f=e.opts.unknownFormats,p=Array.isArray(f);if(d){var m="format"+r,g="isObject"+r,v="formatType"+r;i+=" var "+m+" = formats["+h+"]; var "+g+" = typeof "+m+" == 'object' && !("+m+" instanceof RegExp) && "+m+".validate; var "+v+" = "+g+" && "+m+".type || 'string'; if ("+g+") { ",e.async&&(i+=" var async"+r+" = "+m+".async; "),i+=" "+m+" = "+m+".validate; } if ( ",d&&(i+=" ("+h+" !== undefined && typeof "+h+" != 'string') || "),i+=" (","ignore"!=f&&(i+=" ("+h+" && !"+m+" ",p&&(i+=" && self._opts.unknownFormats.indexOf("+h+") == -1 "),i+=") || "),i+=" ("+m+" && "+v+" == '"+n+"' && !(typeof "+m+" == 'function' ? ",e.async?i+=" (async"+r+" ? "+e.yieldAwait+" "+m+"("+u+") : "+m+"("+u+")) ":i+=" "+m+"("+u+") ",i+=" : "+m+".test("+u+"))))) {"}else{var m=e.formats[s];if(!m){if("ignore"==f)return e.logger.warn('unknown format "'+s+'" ignored in schema at path "'+e.errSchemaPath+'"'),c&&(i+=" if (true) { "),i;if(p&&f.indexOf(s)>=0)return c&&(i+=" if (true) { "),i;throw new Error('unknown format "'+s+'" is used in schema at path "'+e.errSchemaPath+'"')}var g="object"==typeof m&&!(m instanceof RegExp)&&m.validate,v=g&&m.type||"string";if(g){var y=!0===m.async;m=m.validate}if(v!=n)return c&&(i+=" if (true) { "),i;if(y){if(!e.async)throw new Error("async format in sync schema");var b="formats"+e.util.getProperty(s)+".validate";i+=" if (!("+e.yieldAwait+" "+b+"("+u+"))) { "}else{i+=" if (! ";var b="formats"+e.util.getProperty(s);g&&(b+=".validate"),i+="function"==typeof m?" "+b+"("+u+") ":" "+b+".test("+u+") ",i+=") { "}}var w=w||[];w.push(i),i="",!1!==e.createErrors?(i+=" { keyword: 'format' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(l)+" , params: { format: ",i+=d?""+h:""+e.util.toQuotedString(s),i+=" } ",!1!==e.opts.messages&&(i+=" , message: 'should match format \"",i+=d?"' + "+h+" + '":""+e.util.escapeQuotes(s),i+="\"' "),e.opts.verbose&&(i+=" , schema: ",i+=d?"validate.schema"+a:""+e.util.toQuotedString(s),i+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+u+" "),i+=" } "):i+=" {} ";var C=i;return i=w.pop(),!e.compositeRule&&c?e.async?i+=" throw new ValidationError(["+C+"]); ":i+=" validate.errors = ["+C+"]; return false; ":i+=" var err = "+C+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",i+=" } ",c&&(i+=" else { "),i}},"7+8w":function(e,t,n){"use strict";function i(e,t){return e+n.i(r.a)(t)}t.a=i;var r=n("7J+J")},"7+uW":function(e,t,n){"use strict";(function(e,n){function i(e){return void 0===e||null===e}function r(e){return void 0!==e&&null!==e}function o(e){return!0===e}function s(e){return!1===e}function a(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function l(e){return null!==e&&"object"==typeof e}function c(e){return"[object Object]"===no.call(e)}function u(e){return"[object RegExp]"===no.call(e)}function h(e){var t=parseFloat(String(e));return t>=0&&Math.floor(t)===t&&isFinite(e)}function d(e){return null==e?"":"object"==typeof e?JSON.stringify(e,null,2):String(e)}function f(e){var t=parseFloat(e);return isNaN(t)?e:t}function p(e,t){for(var n=Object.create(null),i=e.split(","),r=0;r-1)return e.splice(n,1)}}function g(e,t){return oo.call(e,t)}function v(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}function y(e,t){function n(n){var i=arguments.length;return i?i>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n}function b(e,t){t=t||0;for(var n=e.length-t,i=new Array(n);n--;)i[n]=e[n+t];return i}function w(e,t){for(var n in t)e[n]=t[n];return e}function C(e){for(var t={},n=0;n0&&(s=ve(s,(t||"")+"_"+n),ge(s[0])&&ge(c)&&(u[l]=T(c.text+s[0].text),s.shift()),u.push.apply(u,s)):a(s)?ge(c)?u[l]=T(c.text+s):""!==s&&u.push(T(s)):ge(s)&&ge(c)?u[l]=T(c.text+s.text):(o(e._isVList)&&r(s.tag)&&i(s.key)&&r(t)&&(s.key="__vlist"+t+"_"+n+"__"),u.push(s)));return u}function ye(e,t){return(e.__esModule||Po&&"Module"===e[Symbol.toStringTag])&&(e=e.default),l(e)?t.extend(e):e}function be(e,t,n,i,r){var o=Wo();return o.asyncFactory=e,o.asyncMeta={data:t,context:n,children:i,tag:r},o}function we(e,t,n){if(o(e.error)&&r(e.errorComp))return e.errorComp;if(r(e.resolved))return e.resolved;if(o(e.loading)&&r(e.loadingComp))return e.loadingComp;if(!r(e.contexts)){var s=e.contexts=[n],a=!0,c=function(){for(var e=0,t=s.length;eps&&cs[n].id>e.id;)n--;cs.splice(n+1,0,e)}else cs.push(e);ds||(ds=!0,se(Ie))}}function We(e,t,n){vs.get=function(){return this[t][n]},vs.set=function(e){this[t][n]=e},Object.defineProperty(e,n,vs)}function ze(e){e._watchers=[];var t=e.$options;t.props&&Ue(e,t.props),t.methods&&Ye(e,t.methods),t.data?Ke(e):M(e._data={},!0),t.computed&&Ge(e,t.computed),t.watch&&t.watch!==_o&&Xe(e,t.watch)}function Ue(e,t){var n=e.$options.propsData||{},i=e._props={},r=e.$options._propKeys=[],o=!e.$parent;qo.shouldConvert=o;for(var s in t)!function(o){r.push(o);var s=Y(o,t,n,e);I(i,o,s),o in e||We(e,"_props",o)}(s);qo.shouldConvert=!0}function Ke(e){var t=e.$options.data;t=e._data="function"==typeof t?qe(t,e):t||{},c(t)||(t={});for(var n=Object.keys(t),i=e.$options.props,r=(e.$options.methods,n.length);r--;){var o=n[r];i&&g(i,o)||S(o)||We(e,"_data",o)}M(t,!0)}function qe(e,t){try{return e.call(t,t)}catch(e){return te(e,t,"data()"),{}}}function Ge(e,t){var n=e._computedWatchers=Object.create(null),i=Oo();for(var r in t){var o=t[r],s="function"==typeof o?o:o.get;i||(n[r]=new gs(e,s||A,A,ys)),r in e||Je(e,r,o)}}function Je(e,t,n){var i=!Oo();"function"==typeof n?(vs.get=i?Qe(t):n,vs.set=A):(vs.get=n.get?i&&!1!==n.cache?Qe(t):n.get:A,vs.set=n.set?n.set:A),Object.defineProperty(e,t,vs)}function Qe(e){return function(){var t=this._computedWatchers&&this._computedWatchers[e];if(t)return t.dirty&&t.evaluate(),jo.target&&t.depend(),t.value}}function Ye(e,t){e.$options.props;for(var n in t)e[n]=null==t[n]?A:y(t[n],e)}function Xe(e,t){for(var n in t){var i=t[n];if(Array.isArray(i))for(var r=0;r=0||n.indexOf(e[r])<0)&&i.push(e[r]);return i}return e}function Bt(e){this._init(e)}function Dt(e){e.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(t.indexOf(e)>-1)return this;var n=b(arguments,1);return n.unshift(this),"function"==typeof e.install?e.install.apply(e,n):"function"==typeof e&&e.apply(null,n),t.push(e),this}}function Tt(e){e.mixin=function(e){return this.options=J(this.options,e),this}}function Lt(e){e.cid=0;var t=1;e.extend=function(e){e=e||{};var n=this,i=n.cid,r=e._Ctor||(e._Ctor={});if(r[i])return r[i];var o=e.name||n.options.name,s=function(e){this._init(e)};return s.prototype=Object.create(n.prototype),s.prototype.constructor=s,s.cid=t++,s.options=J(n.options,e),s.super=n,s.options.props&&Ot(s),s.options.computed&&Rt(s),s.extend=n.extend,s.mixin=n.mixin,s.use=n.use,mo.forEach(function(e){s[e]=n[e]}),o&&(s.options.components[o]=s),s.superOptions=n.options,s.extendOptions=e,s.sealedOptions=w({},s.options),r[i]=s,s}}function Ot(e){var t=e.options.props;for(var n in t)We(e.prototype,"_props",n)}function Rt(e){var t=e.options.computed;for(var n in t)Je(e.prototype,n,t[n])}function Pt(e){mo.forEach(function(t){e[t]=function(e,n){return n?("component"===t&&c(n)&&(n.name=n.name||e,n=this.options._base.extend(n)),"directive"===t&&"function"==typeof n&&(n={bind:n,update:n}),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}})}function Mt(e){return e&&(e.Ctor.options.name||e.tag)}function It(e,t){return Array.isArray(e)?e.indexOf(t)>-1:"string"==typeof e?e.split(",").indexOf(t)>-1:!!u(e)&&e.test(t)}function jt(e,t){var n=e.cache,i=e.keys,r=e._vnode;for(var o in n){var s=n[o];if(s){var a=Mt(s.componentOptions);a&&!t(a)&&Nt(n,o,i,r)}}}function Nt(e,t,n,i){var r=e[t];!r||i&&r.tag===i.tag||r.componentInstance.$destroy(),e[t]=null,m(n,t)}function Ht(e){for(var t=e.data,n=e,i=e;r(i.componentInstance);)(i=i.componentInstance._vnode)&&i.data&&(t=Vt(i.data,t));for(;r(n=n.parent);)n&&n.data&&(t=Vt(t,n.data));return Wt(t.staticClass,t.class)}function Vt(e,t){return{staticClass:zt(e.staticClass,t.staticClass),class:r(e.class)?[e.class,t.class]:t.class}}function Wt(e,t){return r(e)||r(t)?zt(e,Ut(t)):""}function zt(e,t){return e?t?e+" "+t:e:t||""}function Ut(e){return Array.isArray(e)?Kt(e):l(e)?qt(e):"string"==typeof e?e:""}function Kt(e){for(var t,n="",i=0,o=e.length;i-1?Qs[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:Qs[e]=/HTMLUnknownElement/.test(t.toString())}function Qt(e){if("string"==typeof e){var t=document.querySelector(e);return t||document.createElement("div")}return e}function Yt(e,t){var n=document.createElement(e);return"select"!==e?n:(t.data&&t.data.attrs&&void 0!==t.data.attrs.multiple&&n.setAttribute("multiple","multiple"),n)}function Xt(e,t){return document.createElementNS(Us[e],t)}function Zt(e){return document.createTextNode(e)}function en(e){return document.createComment(e)}function tn(e,t,n){e.insertBefore(t,n)}function nn(e,t){e.removeChild(t)}function rn(e,t){e.appendChild(t)}function on(e){return e.parentNode}function sn(e){return e.nextSibling}function an(e){return e.tagName}function ln(e,t){e.textContent=t}function cn(e,t,n){e.setAttribute(t,n)}function un(e,t){var n=e.data.ref;if(n){var i=e.context,r=e.componentInstance||e.elm,o=i.$refs;t?Array.isArray(o[n])?m(o[n],r):o[n]===r&&(o[n]=void 0):e.data.refInFor?Array.isArray(o[n])?o[n].indexOf(r)<0&&o[n].push(r):o[n]=[r]:o[n]=r}}function hn(e,t){return e.key===t.key&&(e.tag===t.tag&&e.isComment===t.isComment&&r(e.data)===r(t.data)&&dn(e,t)||o(e.isAsyncPlaceholder)&&e.asyncFactory===t.asyncFactory&&i(t.asyncFactory.error))}function dn(e,t){if("input"!==e.tag)return!0;var n,i=r(n=e.data)&&r(n=n.attrs)&&n.type,o=r(n=t.data)&&r(n=n.attrs)&&n.type;return i===o||Ys(i)&&Ys(o)}function fn(e,t,n){var i,o,s={};for(i=t;i<=n;++i)o=e[i].key,r(o)&&(s[o]=i);return s}function pn(e,t){(e.data.directives||t.data.directives)&&mn(e,t)}function mn(e,t){var n,i,r,o=e===ea,s=t===ea,a=gn(e.data.directives,e.context),l=gn(t.data.directives,t.context),c=[],u=[];for(n in l)i=a[n],r=l[n],i?(r.oldValue=i.value,yn(r,"update",t,e),r.def&&r.def.componentUpdated&&u.push(r)):(yn(r,"bind",t,e),r.def&&r.def.inserted&&c.push(r));if(c.length){var h=function(){for(var n=0;n=0&&" "===(g=e.charAt(m));m--);g&&aa.test(g)||(u=!0)}}else void 0===o?(p=r+1,o=e.slice(0,r).trim()):t();if(void 0===o?o=e.slice(0,r).trim():0!==p&&t(),s)for(r=0;r-1?{exp:e.slice(0,Bs),key:'"'+e.slice(Bs+1)+'"'}:{exp:e,key:null};for(ks=e,Bs=Ds=Ts=0;!Mn();)_s=Pn(),In(_s)?Nn(_s):91===_s&&jn(_s);return{exp:e.slice(0,Ds),key:e.slice(Ds+1,Ts)}}function Pn(){return ks.charCodeAt(++Bs)}function Mn(){return Bs>=$s}function In(e){return 34===e||39===e}function jn(e){var t=1;for(Ds=Bs;!Mn();)if(e=Pn(),In(e))Nn(e);else if(91===e&&t++,93===e&&t--,0===t){Ts=Bs;break}}function Nn(e){for(var t=e;!Mn()&&(e=Pn())!==t;);}function Hn(e,t,n){Ls=n;var i=t.value,r=t.modifiers,o=e.tag,s=e.attrsMap.type;if(e.component)return Ln(e,i,r),!1;if("select"===o)zn(e,i,r);else if("input"===o&&"checkbox"===s)Vn(e,i,r);else if("input"===o&&"radio"===s)Wn(e,i,r);else if("input"===o||"textarea"===o)Un(e,i,r);else if(!vo.isReservedTag(o))return Ln(e,i,r),!1;return!0}function Vn(e,t,n){var i=n&&n.number,r=Dn(e,"value")||"null",o=Dn(e,"true-value")||"true",s=Dn(e,"false-value")||"false";Sn(e,"checked","Array.isArray("+t+")?_i("+t+","+r+")>-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),Bn(e,"change","var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+s+");if(Array.isArray($$a)){var $$v="+(i?"_n("+r+")":r)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+t+"=$$a.concat([$$v]))}else{$$i>-1&&("+t+"=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{"+On(t,"$$c")+"}",null,!0)}function Wn(e,t,n){var i=n&&n.number,r=Dn(e,"value")||"null";r=i?"_n("+r+")":r,Sn(e,"checked","_q("+t+","+r+")"),Bn(e,"change",On(t,r),null,!0)}function zn(e,t,n){var i=n&&n.number,r='Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = "_value" in o ? o._value : o.value;return '+(i?"_n(val)":"val")+"})",o="var $$selectedVal = "+r+";";o=o+" "+On(t,"$event.target.multiple ? $$selectedVal : $$selectedVal[0]"),Bn(e,"change",o,null,!0)}function Un(e,t,n){var i=e.attrsMap.type,r=n||{},o=r.lazy,s=r.number,a=r.trim,l=!o&&"range"!==i,c=o?"change":"range"===i?la:"input",u="$event.target.value";a&&(u="$event.target.value.trim()"),s&&(u="_n("+u+")");var h=On(t,u);l&&(h="if($event.target.composing)return;"+h),Sn(e,"value","("+t+")"),Bn(e,c,h,null,!0),(a||s)&&Bn(e,"blur","$forceUpdate()")}function Kn(e){if(r(e[la])){var t=xo?"change":"input";e[t]=[].concat(e[la],e[t]||[]),delete e[la]}r(e[ca])&&(e.change=[].concat(e[ca],e.change||[]),delete e[ca])}function qn(e,t,n){var i=Os;return function r(){null!==e.apply(null,arguments)&&Jn(t,r,n,i)}}function Gn(e,t,n,i,r){t=oe(t),n&&(t=qn(t,e,i)),Os.addEventListener(e,t,Bo?{capture:i,passive:r}:i)}function Jn(e,t,n,i){(i||Os).removeEventListener(e,t._withTask||t,n)}function Qn(e,t){if(!i(e.data.on)||!i(t.data.on)){var n=t.data.on||{},r=e.data.on||{};Os=t.elm,Kn(n),ue(n,r,Gn,Jn,t.context),Os=void 0}}function Yn(e,t){if(!i(e.data.domProps)||!i(t.data.domProps)){var n,o,s=t.elm,a=e.data.domProps||{},l=t.data.domProps||{};r(l.__ob__)&&(l=t.data.domProps=w({},l));for(n in a)i(l[n])&&(s[n]="");for(n in l){if(o=l[n],"textContent"===n||"innerHTML"===n){if(t.children&&(t.children.length=0),o===a[n])continue;1===s.childNodes.length&&s.removeChild(s.childNodes[0])}if("value"===n){s._value=o;var c=i(o)?"":String(o);Xn(s,c)&&(s.value=c)}else s[n]=o}}}function Xn(e,t){return!e.composing&&("OPTION"===e.tagName||Zn(e,t)||ei(e,t))}function Zn(e,t){var n=!0;try{n=document.activeElement!==e}catch(e){}return n&&e.value!==t}function ei(e,t){var n=e.value,i=e._vModifiers;if(r(i)){if(i.lazy)return!1;if(i.number)return f(n)!==f(t);if(i.trim)return n.trim()!==t.trim()}return n!==t}function ti(e){var t=ni(e.style);return e.staticStyle?w(e.staticStyle,t):t}function ni(e){return Array.isArray(e)?C(e):"string"==typeof e?da(e):e}function ii(e,t){var n,i={};if(t)for(var r=e;r.componentInstance;)(r=r.componentInstance._vnode)&&r.data&&(n=ti(r.data))&&w(i,n);(n=ti(e.data))&&w(i,n);for(var o=e;o=o.parent;)o.data&&(n=ti(o.data))&&w(i,n);return i}function ri(e,t){var n=t.data,o=e.data;if(!(i(n.staticStyle)&&i(n.style)&&i(o.staticStyle)&&i(o.style))){var s,a,l=t.elm,c=o.staticStyle,u=o.normalizedStyle||o.style||{},h=c||u,d=ni(t.data.style)||{};t.data.normalizedStyle=r(d.__ob__)?w({},d):d;var f=ii(t,!0);for(a in h)i(f[a])&&ma(l,a,"");for(a in f)(s=f[a])!==h[a]&&ma(l,a,null==s?"":s)}}function oi(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(/\s+/).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function si(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(/\s+/).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t),e.classList.length||e.removeAttribute("class");else{for(var n=" "+(e.getAttribute("class")||"")+" ",i=" "+t+" ";n.indexOf(i)>=0;)n=n.replace(i," ");n=n.trim(),n?e.setAttribute("class",n):e.removeAttribute("class")}}function ai(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&w(t,ba(e.name||"v")),w(t,e),t}return"string"==typeof e?ba(e):void 0}}function li(e){$a(function(){$a(e)})}function ci(e,t){var n=e._transitionClasses||(e._transitionClasses=[]);n.indexOf(t)<0&&(n.push(t),oi(e,t))}function ui(e,t){e._transitionClasses&&m(e._transitionClasses,t),si(e,t)}function hi(e,t,n){var i=di(e,t),r=i.type,o=i.timeout,s=i.propCount;if(!r)return n();var a=r===Ca?xa:Sa,l=0,c=function(){e.removeEventListener(a,u),n()},u=function(t){t.target===e&&++l>=s&&c()};setTimeout(function(){l0&&(n=Ca,u=s,h=o.length):t===Aa?c>0&&(n=Aa,u=c,h=l.length):(u=Math.max(s,c),n=u>0?s>c?Ca:Aa:null,h=n?n===Ca?o.length:l.length:0),{type:n,timeout:u,propCount:h,hasTransform:n===Ca&&ka.test(i[Ea+"Property"])}}function fi(e,t){for(;e.length1}function bi(e,t){!0!==t.data.show&&mi(t)}function wi(e,t,n){Ci(e,t,n),(xo||So)&&setTimeout(function(){Ci(e,t,n)},0)}function Ci(e,t,n){var i=t.value,r=e.multiple;if(!r||Array.isArray(i)){for(var o,s,a=0,l=e.options.length;a-1,s.selected!==o&&(s.selected=o);else if(E(Ei(s),i))return void(e.selectedIndex!==a&&(e.selectedIndex=a));r||(e.selectedIndex=-1)}}function Ai(e,t){return t.every(function(t){return!E(t,e)})}function Ei(e){return"_value"in e?e._value:e.value}function xi(e){e.target.composing=!0}function Fi(e){e.target.composing&&(e.target.composing=!1,Si(e.target,"input"))}function Si(e,t){var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}function $i(e){return!e.componentInstance||e.data&&e.data.transition?e:$i(e.componentInstance._vnode)}function ki(e){var t=e&&e.componentOptions;return t&&t.Ctor.options.abstract?ki(Ae(t.children)):e}function _i(e){var t={},n=e.$options;for(var i in n.propsData)t[i]=e[i];var r=n._parentListeners;for(var o in r)t[ao(o)]=r[o];return t}function Bi(e,t){if(/\d-keep-alive$/.test(t.tag))return e("keep-alive",{props:t.componentOptions.propsData})}function Di(e){for(;e=e.parent;)if(e.data.transition)return!0}function Ti(e,t){return t.key===e.key&&t.tag===e.tag}function Li(e){e.elm._moveCb&&e.elm._moveCb(),e.elm._enterCb&&e.elm._enterCb()}function Oi(e){e.data.newPos=e.elm.getBoundingClientRect()}function Ri(e){var t=e.data.pos,n=e.data.newPos,i=t.left-n.left,r=t.top-n.top;if(i||r){e.data.moved=!0;var o=e.elm.style;o.transform=o.WebkitTransform="translate("+i+"px,"+r+"px)",o.transitionDuration="0s"}}function Pi(e,t){var n=t?za(t):Va;if(n.test(e)){for(var i,r,o,s=[],a=[],l=n.lastIndex=0;i=n.exec(e);){r=i.index,r>l&&(a.push(o=e.slice(l,r)),s.push(JSON.stringify(o)));var c=An(i[1].trim());s.push("_s("+c+")"),a.push({"@binding":c}),l=r+i[0].length}return l=0&&s[r].lowerCasedTag!==a;r--);else r=0;if(r>=0){for(var l=s.length-1;l>=r;l--)t.end&&t.end(s[l].tag,n,i);s.length=r,o=r&&s[r-1].tag}else"br"===a?t.start&&t.start(e,[],!0,n,i):"p"===a&&(t.start&&t.start(e,[],!1,n,i),t.end&&t.end(e,n,i))}for(var r,o,s=[],a=t.expectHTML,l=t.isUnaryTag||ho,c=t.canBeLeftOpenTag||ho,u=0;e;){if(r=e,o&&yl(o)){var h=0,d=o.toLowerCase(),f=bl[d]||(bl[d]=new RegExp("([\\s\\S]*?)(]*>)","i")),p=e.replace(f,function(e,n,i){return h=i.length,yl(d)||"noscript"===d||(n=n.replace(//g,"$1").replace(//g,"$1")),xl(d,n)&&(n=n.slice(1)),t.chars&&t.chars(n),""});u+=e.length-p.length,e=p,i(d,u-h,u)}else{var m=e.indexOf("<");if(0===m){if(rl.test(e)){var g=e.indexOf("--\x3e");if(g>=0){t.shouldKeepComment&&t.comment(e.substring(4,g)),n(g+3);continue}}if(ol.test(e)){var v=e.indexOf("]>");if(v>=0){n(v+2);continue}}var y=e.match(il);if(y){n(y[0].length);continue}var b=e.match(nl);if(b){var w=u;n(b[0].length),i(b[1],w,u);continue}var C=function(){var t=e.match(el);if(t){var i={tagName:t[1],attrs:[],start:u};n(t[0].length);for(var r,o;!(r=e.match(tl))&&(o=e.match(Ya));)n(o[0].length),i.attrs.push(o);if(r)return i.unarySlash=r[1],n(r[0].length),i.end=u,i}}();if(C){!function(e){var n=e.tagName,r=e.unarySlash;a&&("p"===o&&Qa(n)&&i(o),c(n)&&o===n&&i(n));for(var u=l(n)||!!r,h=e.attrs.length,d=new Array(h),f=0;f=0){for(E=e.slice(m);!(nl.test(E)||el.test(E)||rl.test(E)||ol.test(E)||(x=E.indexOf("<",1))<0);)m+=x,E=e.slice(m);A=e.substring(0,m),n(m)}m<0&&(A=e,e=""),t.chars&&A&&t.chars(A)}if(e===r){t.chars&&t.chars(e);break}}i()}function Wi(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:lr(t),parent:n,children:[]}}function zi(e,t){function n(e){e.pre&&(a=!1),dl(e.tag)&&(l=!1);for(var n=0;n':'
',vl.innerHTML.indexOf(" ")>0}function eo(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}/*! * Vue.js v2.5.13 * (c) 2014-2017 Evan You * Released under the MIT License. */ var to=Object.freeze({}),no=Object.prototype.toString,io=p("slot,component",!0),ro=p("key,ref,slot,slot-scope,is"),oo=Object.prototype.hasOwnProperty,so=/-(\w)/g,ao=v(function(e){return e.replace(so,function(e,t){return t?t.toUpperCase():""})}),lo=v(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),co=/\B([A-Z])/g,uo=v(function(e){return e.replace(co,"-$1").toLowerCase()}),ho=function(e,t,n){return!1},fo=function(e){return e},po="data-server-rendered",mo=["component","directive","filter"],go=["beforeCreate","created","beforeMount","mounted","beforeUpdate","updated","beforeDestroy","destroyed","activated","deactivated","errorCaptured"],vo={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,warnHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:ho,isReservedAttr:ho,isUnknownElement:ho,getTagNamespace:A,parsePlatformTagName:fo,mustUseProp:ho,_lifecycleHooks:go},yo=/[^\w.$]/,bo="__proto__"in{},wo="undefined"!=typeof window,Co="undefined"!=typeof WXEnvironment&&!!WXEnvironment.platform,Ao=Co&&WXEnvironment.platform.toLowerCase(),Eo=wo&&window.navigator.userAgent.toLowerCase(),xo=Eo&&/msie|trident/.test(Eo),Fo=Eo&&Eo.indexOf("msie 9.0")>0,So=Eo&&Eo.indexOf("edge/")>0,$o=Eo&&Eo.indexOf("android")>0||"android"===Ao,ko=Eo&&/iphone|ipad|ipod|ios/.test(Eo)||"ios"===Ao,_o=(Eo&&/chrome\/\d+/.test(Eo),{}.watch),Bo=!1;if(wo)try{var Do={};Object.defineProperty(Do,"passive",{get:function(){Bo=!0}}),window.addEventListener("test-passive",null,Do)}catch(e){}var To,Lo,Oo=function(){return void 0===To&&(To=!wo&&void 0!==e&&"server"===e.process.env.VUE_ENV),To},Ro=wo&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__,Po="undefined"!=typeof Symbol&&_(Symbol)&&"undefined"!=typeof Reflect&&_(Reflect.ownKeys);Lo="undefined"!=typeof Set&&_(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var Mo=A,Io=0,jo=function(){this.id=Io++,this.subs=[]};jo.prototype.addSub=function(e){this.subs.push(e)},jo.prototype.removeSub=function(e){m(this.subs,e)},jo.prototype.depend=function(){jo.target&&jo.target.addDep(this)},jo.prototype.notify=function(){for(var e=this.subs.slice(),t=0,n=e.length;t1?b(n):n;for(var i=b(arguments,1),r=0,o=n.length;rparseInt(this.max)&&Nt(l,c[0],c,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}},Ss={KeepAlive:Fs};!function(e){var t={};t.get=function(){return vo},Object.defineProperty(e,"config",t),e.util={warn:Mo,extend:w,mergeOptions:J,defineReactive:I},e.set=j,e.delete=N,e.nextTick=se,e.options=Object.create(null),mo.forEach(function(t){e.options[t+"s"]=Object.create(null)}),e.options._base=e,w(e.options.components,Ss),Dt(e),Tt(e),Lt(e),Pt(e)}(Bt),Object.defineProperty(Bt.prototype,"$isServer",{get:Oo}),Object.defineProperty(Bt.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Bt.version="2.5.13";var $s,ks,_s,Bs,Ds,Ts,Ls,Os,Rs,Ps=p("style,class"),Ms=p("input,textarea,option,select,progress"),Is=function(e,t,n){return"value"===n&&Ms(e)&&"button"!==t||"selected"===n&&"option"===e||"checked"===n&&"input"===e||"muted"===n&&"video"===e},js=p("contenteditable,draggable,spellcheck"),Ns=p("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),Hs="http://www.w3.org/1999/xlink",Vs=function(e){return":"===e.charAt(5)&&"xlink"===e.slice(0,5)},Ws=function(e){return Vs(e)?e.slice(6,e.length):""},zs=function(e){return null==e||!1===e},Us={svg:"http://www.w3.org/2000/svg",math:"http://www.w3.org/1998/Math/MathML"},Ks=p("html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,menuitem,summary,content,element,shadow,template,blockquote,iframe,tfoot"),qs=p("svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view",!0),Gs=function(e){return"pre"===e},Js=function(e){return Ks(e)||qs(e)},Qs=Object.create(null),Ys=p("text,number,password,search,email,tel,url"),Xs=Object.freeze({createElement:Yt,createElementNS:Xt,createTextNode:Zt,createComment:en,insertBefore:tn,removeChild:nn,appendChild:rn,parentNode:on,nextSibling:sn,tagName:an,setTextContent:ln,setAttribute:cn}),Zs={create:function(e,t){un(t)},update:function(e,t){e.data.ref!==t.data.ref&&(un(e,!0),un(t))},destroy:function(e){un(e,!0)}},ea=new Ho("",{},[]),ta=["create","activate","update","remove","destroy"],na={create:pn,update:pn,destroy:function(e){pn(e,ea)}},ia=Object.create(null),ra=[Zs,na],oa={create:bn,update:bn},sa={create:Cn,update:Cn},aa=/[\w).+\-_$\]]/,la="__r",ca="__c",ua={create:Qn,update:Qn},ha={create:Yn,update:Yn},da=v(function(e){var t={},n=/;(?![^(]*\))/g,i=/:(.+)/;return e.split(n).forEach(function(e){if(e){var n=e.split(i);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}),fa=/^--/,pa=/\s*!important$/,ma=function(e,t,n){if(fa.test(t))e.style.setProperty(t,n);else if(pa.test(n))e.style.setProperty(t,n.replace(pa,""),"important");else{var i=va(t);if(Array.isArray(n))for(var r=0,o=n.length;rp?(h=i(n[v+1])?null:n[v+1].elm,y(e,h,n,f,v,o)):f>v&&w(e,t,d,p)}function E(e,t,n,i){for(var o=n;o\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Xa="[a-zA-Z_][\\w\\-\\.]*",Za="((?:"+Xa+"\\:)?"+Xa+")",el=new RegExp("^<"+Za),tl=/^\s*(\/?)>/,nl=new RegExp("^<\\/"+Za+"[^>]*>"),il=/^]+>/i,rl=/^ ================================================ FILE: tac-console-web/package.json ================================================ { "name": "tac-console-web", "version": "1.0.0", "description": "A Vue.js project", "author": "jinshuan.li", "private": true, "scripts": { "dev": "node build/dev-server.js", "start": "node build/dev-server.js", "build": "node build/build.js", "lint": "eslint --ext .js,.vue src" }, "dependencies": { "bootstrap": "^4.0.0-beta.2", "bootstrap-vue": "latest", "jquery": "^3.3.1", "jsoneditor": "^5.14.0", "popper.js": "^1.12.9", "vue": "^2.5.2", "vue-resource": "^1.5.0", "vue-router": "^3.0.1", "vue-toastr": "^2.0.12" }, "devDependencies": { "autoprefixer": "^7.1.2", "babel-core": "^6.22.1", "babel-eslint": "^7.1.1", "babel-loader": "^7.1.1", "babel-plugin-transform-runtime": "^6.22.0", "babel-preset-env": "^1.3.2", "babel-preset-stage-2": "^6.22.0", "babel-register": "^6.22.0", "chalk": "^2.0.1", "connect-history-api-fallback": "^1.3.0", "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.0", "cssnano": "^3.10.0", "eslint": "^3.19.0", "eslint-friendly-formatter": "^3.0.0", "eslint-loader": "^1.7.1", "eslint-plugin-html": "^3.0.0", "eventsource-polyfill": "^0.9.6", "express": "^4.14.1", "extract-text-webpack-plugin": "^2.0.0", "file-loader": "^0.11.1", "friendly-errors-webpack-plugin": "^1.1.3", "html-webpack-plugin": "^2.28.0", "http-proxy-middleware": "^0.17.3", "webpack-bundle-analyzer": "^2.2.1", "semver": "^5.3.0", "shelljs": "^0.7.6", "opn": "^5.1.0", "optimize-css-assets-webpack-plugin": "^2.0.0", "ora": "^1.2.0", "rimraf": "^2.6.0", "url-loader": "^0.5.8", "vue-loader": "^13.0.4", "vue-style-loader": "^3.0.1", "vue-template-compiler": "^2.4.2", "webpack": "^2.6.1", "webpack-dev-middleware": "^1.10.0", "webpack-hot-middleware": "^2.18.0", "webpack-merge": "^4.1.0" }, "engines": { "node": ">= 4.0.0", "npm": ">= 3.0.0" }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] } ================================================ FILE: tac-console-web/src/App.vue ================================================ ================================================ FILE: tac-console-web/src/components/Hello.vue ================================================ ================================================ FILE: tac-console-web/src/components/Home.vue ================================================ ================================================ FILE: tac-console-web/src/components/TacConsole.vue ================================================ ================================================ FILE: tac-console-web/src/components/TacInst.vue ================================================ ================================================ FILE: tac-console-web/src/components/TacInstPublish.vue ================================================ ================================================ FILE: tac-console-web/src/components/TacInstPublishCheck.vue ================================================ ================================================ FILE: tac-console-web/src/components/TacJSONEditor.vue ================================================ ================================================ FILE: tac-console-web/src/components/TacMs.vue ================================================ ================================================ FILE: tac-console-web/src/components/TacMsEdit.vue ================================================ ================================================ FILE: tac-console-web/src/components/TacMsList.vue ================================================ ================================================ FILE: tac-console-web/src/main.js ================================================ // The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue'; import BootstrapVue from 'bootstrap-vue'; import VueResource from 'vue-resource'; import Toastr from 'vue-toastr'; import App from './App'; import router from './router'; import 'bootstrap/dist/css/bootstrap.min.css'; import 'bootstrap-vue/dist/bootstrap-vue.css'; import 'jsoneditor/dist/jsoneditor.css'; import 'vue-toastr/dist/vue-toastr.css'; import TacConsole from '@/components/TacConsole'; import TacJSONEditor from '@/components/TacJSONEditor'; import '../static/main.css'; Vue.use(Toastr); Vue.use(BootstrapVue); Vue.use(VueResource); Vue.component('TacConsole', TacConsole); Vue.component('TacJSONEditor', TacJSONEditor); Vue.config.productionTip = false; /* eslint-disable no-new */ new Vue({ el: '#app', router, template: '', components: { App } }); ================================================ FILE: tac-console-web/src/router/index.js ================================================ import Vue from 'vue'; import Router from 'vue-router'; import TacConsole from '@/components/TacConsole'; import Home from '@/components/Home'; import TacMs from '@/components/TacMs'; import TacMsList from '@/components/TacMsList'; import TacMsEdit from '@/components/TacMsEdit'; import TacInst from '@/components/TacInst'; import TacInstPublish from '@/components/TacInstPublish'; import TacInstPublishCheck from '@/components/TacInstPublishCheck'; Vue.component('Home', Home); Vue.component('TacMs', TacMs); Vue.use(Router); export default new Router({ routes: [ { path: '/', redirect: '/home' }, { path: '/home', name: 'home', component: Home }, { path: '/tacMs', redirect: '/tacMs/list', component: TacMs, children: [ { path: 'list', component: TacMsList }, { path: 'new', name: 'newMs', component: TacMsEdit }, { path: 'edit/:code', name: 'editMs', component: TacMsEdit } ] }, { path: '/tacInst', component: TacInst, redirect: '/tacMs/list', children: [ { name: 'msInstPublish', path: 'publish/:msCode', component: TacInstPublish }, { path: 'publishcheck', name: 'instTest', component: TacInstPublishCheck } ] } ] }); ================================================ FILE: tac-console-web/static/.gitkeep ================================================ ================================================ FILE: tac-console-web/static/main.css ================================================ .spanButton { margin-left: 10px; } ================================================ FILE: tac-container/pom.xml ================================================ tac com.alibaba 0.0.4 4.0.0 tac-container jar tac-container http://maven.apache.org UTF-8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-configuration-processor true com.alibaba tac-engine org.springframework.boot spring-boot-maven-plugin org.apache.maven.plugins maven-compiler-plugin ${jdk.version} ${jdk.version} UTF-8 org.apache.maven.plugins maven-resources-plugin UTF-8 ================================================ FILE: tac-container/src/main/java/com/alibaba/tac/container/ContainerApplication.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.container; import com.alibaba.tac.engine.bootlaucher.BootJarLaucherUtils; import com.alibaba.tac.engine.code.CodeLoadService; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.Banner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.loader.jar.JarFile; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.*; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * @author jinshuan.li 07/02/2018 11:18 */ @SpringBootApplication(scanBasePackages = {"com.alibaba.tac"}) @PropertySources({@PropertySource("application.properties")}) @EnableAspectJAutoProxy(proxyTargetClass = true) @Import(ContainerBeanConfig.class) @Slf4j public class ContainerApplication { public static void main(String[] args) throws Exception { // the code must execute before spring start JarFile bootJarFile = BootJarLaucherUtils.getBootJarFile(); if (bootJarFile != null) { BootJarLaucherUtils.unpackBootLibs(bootJarFile); log.debug("the temp tac lib folder:{}", BootJarLaucherUtils.getTempUnpackFolder()); } SpringApplication springApplication = new SpringApplication(ContainerApplication.class); springApplication.setWebEnvironment(true); springApplication.setBannerMode(Banner.Mode.OFF); springApplication.addListeners(new ApplicationListener() { @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { CodeLoadService.changeClassLoader(event.getEnvironment()); } }); springApplication.run(args); } @Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**"); } }; } } ================================================ FILE: tac-container/src/main/java/com/alibaba/tac/container/ContainerBeanConfig.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.container; import com.alibaba.tac.engine.service.EngineBeansConfig; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; /** * @author jinshuan.li 01/03/2018 17:21 */ @ConditionalOnProperty(name = "scan.package.name") @Configuration @ComponentScan(basePackages = "${scan.package.name}") @Import(EngineBeansConfig.class) public class ContainerBeanConfig { } ================================================ FILE: tac-container/src/main/java/com/alibaba/tac/container/web/TacApiController.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.container.web; import com.alibaba.fastjson.JSON; import com.alibaba.tac.engine.service.TacEngineService; import com.alibaba.tac.sdk.common.TacParams; import com.alibaba.tac.sdk.common.TacResult; import com.alibaba.tac.sdk.error.ErrorCode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.Map; /** * @author jinshuan.li 12/02/2018 20:27 * * the online api when run tac-container */ @RestController @RequestMapping("/api/tac") @Slf4j public class TacApiController { @Resource private TacEngineService tacEngineService; @GetMapping("/execute/{msCode}") public TacResult> execute(@PathVariable String msCode, @RequestParam(required = false) String paramMap) { try { TacParams tacParamsDO = new TacParams("tac", msCode); if (StringUtils.isEmpty(paramMap)) { } else { tacParamsDO.setParamMap(JSON.parseObject(paramMap)); } return tacEngineService.execute(msCode, tacParamsDO); } catch (Exception e) { log.error("execute error msCode:{} tacParams:{} {}", msCode, paramMap, e.getMessage(), e); return TacResult.errorResult(msCode, String.valueOf(ErrorCode.SYS_EXCEPTION)); } } @PostMapping("/execute/{msCode}") public TacResult> executePost(@PathVariable String msCode, @RequestBody Map paramMap) { try { TacParams tacParamsDO = new TacParams("tac", msCode); tacParamsDO.setParamMap(paramMap); return tacEngineService.execute(msCode, tacParamsDO); } catch (Exception e) { log.error("execute error msCode:{} tacParams:{} {}", msCode, paramMap, e.getMessage(), e); return TacResult.errorResult(msCode, String.valueOf(ErrorCode.SYS_EXCEPTION)); } } } ================================================ FILE: tac-container/src/main/resources/application.properties ================================================ project.name=tac-container # http port server.port=8001 # endpoint port management.port=8002 tac.default.store=redis scan.package.name=com.tmall.tac.test tac.extend.lib=extendlibs logging.config=classpath:tac/default-logback-spring.xml tac.container.web.api=http://localhost:8001/api/tac/execute ================================================ FILE: tac-custom-datasource-demo/pom.xml ================================================ tac com.alibaba 0.0.4 4.0.0 0.0.4-SNAPSHOT tac-custom-datasource-demo jar tac-custom-datasource-demo http://maven.apache.org UTF-8 com.alibaba tac-sdk 0.0.4 ================================================ FILE: tac-custom-datasource-demo/src/main/java/com/tmall/itemcenter/ItemDO.java ================================================ package com.tmall.itemcenter; import lombok.Data; /** * @author jinshuan.li 10/03/2018 15:43 */ @Data public class ItemDO { /** * itemID */ private Long id; /** * item name */ private String name; /** * item price */ private String price; public ItemDO(Long id, String name, String price) { this.id = id; this.name = name; this.price = price; } } ================================================ FILE: tac-custom-datasource-demo/src/main/java/com/tmall/itemcenter/TmallItemService.java ================================================ package com.tmall.itemcenter; import org.springframework.stereotype.Service; /** * @author jinshuan.li 10/03/2018 15:43 */ @Service public class TmallItemService { /** * get a item * * @param id * @return */ public ItemDO getItem(Long id) { // mock data return new ItemDO(id, "A Song of Ice and Fire", "¥222.00"); } } ================================================ FILE: tac-custom-datasource-demo/src/test/java/com/alibaba/tac/AppTest.java ================================================ package com.alibaba.tac; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: tac-dev-source-demo/pom.xml ================================================ tac com.alibaba 0.0.4 4.0.0 tac-dev-source jar tac-dev-source-demo http://maven.apache.org UTF-8 com.alibaba tac-sdk 0.0.4 com.alibaba tac-custom-datasource-demo 0.0.4-SNAPSHOT org.apache.maven.plugins maven-compiler-plugin ${jdk.version} ${jdk.version} UTF-8 org.apache.maven.plugins maven-resources-plugin UTF-8 ================================================ FILE: tac-dev-source-demo/src/main/java/com/alibaba/tac/biz/processor/HelloWorldTac.java ================================================ package com.alibaba.tac.biz.processor; import com.alibaba.tac.sdk.common.TacResult; import com.alibaba.tac.sdk.domain.Context; import com.alibaba.tac.sdk.factory.TacInfrasFactory; import com.alibaba.tac.sdk.handler.TacHandler; import com.alibaba.tac.sdk.infrastracture.TacLogger; import com.tmall.itemcenter.ItemDO; import com.tmall.itemcenter.TmallItemService; import java.util.HashMap; import java.util.Map; /** * @author jinshuan.li */ public class HelloWorldTac implements TacHandler { /** * get the logger service */ private TacLogger tacLogger = TacInfrasFactory.getLogger(); private TmallItemService tmallItemService = TacInfrasFactory.getServiceBean(TmallItemService.class); /** * implement a class which implements TacHandler interface {@link TacHandler} * * @param context * @return * @throws Exception */ @Override public TacResult execute(Context context) throws Exception { // the code tacLogger.info("Hello World22"); Map data = new HashMap<>(); data.put("name", "hellotac"); data.put("platform", "iPhone"); data.put("clientVersion", "7.0.2"); data.put("userName", "tac-userName"); ItemDO item = tmallItemService.getItem(1L); data.put("item", item); return TacResult.newResult(data); } } ================================================ FILE: tac-engine/pom.xml ================================================ tac com.alibaba 0.0.4 4.0.0 tac-engine jar tac-engine http://maven.apache.org UTF-8 com.alibaba tac-infrastructure com.alibaba tac-sdk junit junit org.springframework.boot spring-boot-loader commons-io commons-io org.apache.maven.plugins maven-compiler-plugin ${jdk.version} ${jdk.version} UTF-8 org.apache.maven.plugins maven-resources-plugin UTF-8 ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/autoconfigure/TacAutoConfiguration.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.autoconfigure; import com.alibaba.tac.engine.properties.TacDataPathProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import javax.annotation.Resource; /** * @author jinshuan.li 2018/3/1 08:17 * * the tac auto confige class. do nothing in the verion; */ @Configuration @EnableConfigurationProperties(TacDataPathProperties.class) public class TacAutoConfiguration { @Resource private TacDataPathProperties tacDataPathProperties; } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/bootlaucher/BootJarLaucherUtils.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.bootlaucher; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.loader.archive.Archive; import org.springframework.boot.loader.archive.JarFileArchive; import org.springframework.boot.loader.data.RandomAccessData; import org.springframework.boot.loader.jar.JarFile; import java.io.*; import java.net.URI; import java.security.CodeSource; import java.security.ProtectionDomain; import java.util.Enumeration; import java.util.jar.JarEntry; /** * @author jinshuan.li 08/03/2018 19:12 * * The Boot Laucher Helper class, unpack jar files in the boot jar file. the files are used to compile new java code; */ public class BootJarLaucherUtils { private static File tempUnpackFolder; private static final int BUFFER_SIZE = 32 * 1024; static final String BOOT_INF_LIB = "BOOT-INF/lib/"; /** * * unpack jar to temp folder * @param jarFile * @return */ public static Integer unpackBootLibs(JarFile jarFile) throws IOException { Enumeration entries = jarFile.entries(); int count = 0; while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); if (jarEntry.getName().startsWith(BOOT_INF_LIB) && jarEntry.getName().endsWith(".jar")) { getUnpackedNestedArchive(jarFile, jarEntry); count++; } } return count; } /** * * @param jarFile * @param jarEntry * @return * @throws IOException */ private static Archive getUnpackedNestedArchive(JarFile jarFile, JarEntry jarEntry) throws IOException { String name = jarEntry.getName(); if (name.lastIndexOf("/") != -1) { name = name.substring(name.lastIndexOf("/") + 1); } File file = new File(getTempUnpackFolder(), name); if (!file.exists() || file.length() != jarEntry.getSize()) { unpack(jarFile, jarEntry, file); } return new JarFileArchive(file, file.toURI().toURL()); } public static File getTempUnpackFolder() { if (tempUnpackFolder == null) { File tempFolder = new File(System.getProperty("java.io.tmpdir")); tempUnpackFolder = createUnpackFolder(tempFolder); } return tempUnpackFolder; } /** * create the unpack folder * @param parent * @return */ private static File createUnpackFolder(File parent) { int attempts = 0; while (attempts++ < 1000) { String fileName = "com.alibaba.tac"; File unpackFolder = new File(parent, fileName + "-spring-boot-libs"); if (unpackFolder.exists()) { return unpackFolder; } if (unpackFolder.mkdirs()) { return unpackFolder; } } throw new IllegalStateException( "Failed to create unpack folder in directory '" + parent + "'"); } /** * * @param jarFile * @param entry * @param file * @throws IOException */ private static void unpack(JarFile jarFile, JarEntry entry, File file) throws IOException { InputStream inputStream = jarFile.getInputStream(entry, RandomAccessData.ResourceAccess.ONCE); try { OutputStream outputStream = new FileOutputStream(file); try { byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead = -1; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.flush(); } finally { outputStream.close(); } } finally { inputStream.close(); } } /** * get the boot jar file * * @return the boot jar file; null is run through folder * @throws Exception */ public final static JarFile getBootJarFile() throws Exception { ProtectionDomain protectionDomain = BootJarLaucherUtils.class.getProtectionDomain(); CodeSource codeSource = protectionDomain.getCodeSource(); URI location = (codeSource == null ? null : codeSource.getLocation().toURI()); String path = (location == null ? null : location.toURL().getPath()); if (path == null) { throw new IllegalStateException("Unable to determine code source archive"); } if (path.lastIndexOf("!/BOOT-INF") <= 0) { return null; } path = path.substring(0, path.lastIndexOf("!/BOOT-INF")); path = StringUtils.replace(path, "file:", ""); File root = new File(path); if (root.isDirectory()) { return null; } if (!root.exists()) { throw new IllegalStateException( "Unable to determine code source archive from " + root); } return new JarFile(root); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/code/CodeCompileService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.code; import com.alibaba.tac.engine.compile.IJdkCompiler; import com.alibaba.tac.engine.compile.InstCodeInfo; import com.alibaba.tac.engine.compile.JavaSourceCode; import com.alibaba.tac.engine.util.TacCompressUtils; import com.alibaba.tac.sdk.error.ErrorCode; import com.alibaba.tac.sdk.error.ServiceException; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.FileCopyUtils; import javax.annotation.Resource; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.StringWriter; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author jinshuan.li 12/02/2018 09:07 */ @Service public class CodeCompileService { private Logger LOGGER = LoggerFactory.getLogger(CodeCompileService.class); @Autowired private IJdkCompiler jdkCompiler; @Resource private TacFileService tacFileService; public static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([$_a-zA-Z][$_a-zA-Z0-9\\.]*)\\s*;"); public static final Pattern CLASS_PATTERN = Pattern.compile("(?<=\\n|\\A)(?:\\s*public\\s)?\\s*" + "(final\\s+class|final\\s+public\\s+class|" + "abstract\\s+class|abstract\\s+public\\s+class|" + "class|" + "abstract\\s+interface|abstract\\s+public\\s+interface|" + "interface|" + "@interface|" + "enum)\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)"); /** * compile files * @param instId the instanceId * @param sourceFileDicPath * @return * @throws ServiceException */ public Boolean compile(Long instId, String sourceFileDicPath) throws ServiceException { File sourceFileDic = new File(sourceFileDicPath); List sourceFile = TacFileService.listAllFiles(sourceFileDic); if (CollectionUtils.isEmpty(sourceFile)) { throw new ServiceException(ErrorCode.ILLEGAL_ARGUMENT, "there is no code"); } InstCodeInfo codeInfo = getProcessCodeInfo(sourceFile); codeInfo.setInstId(instId); StringWriter compileInfo = new StringWriter(); boolean compileResult = false; try { compileResult = jdkCompiler.compile(codeInfo, compileInfo); } catch (Exception e) { throw new ServiceException(ErrorCode.ILLEGAL_ARGUMENT, "code compile fail: " + e.getMessage()); } if (!compileResult) { throw new ServiceException(ErrorCode.ILLEGAL_ARGUMENT, "code compile fail: " + compileInfo.getBuffer().toString()); } return compileResult; } /** * compile files * * @param msCode * @param sourceFileDicPath * @return * @throws ServiceException */ public Boolean compile(String msCode, String sourceFileDicPath) throws ServiceException { File sourceFileDic = new File(sourceFileDicPath); List sourceFile = TacFileService.listAllFiles(sourceFileDic); if (CollectionUtils.isEmpty(sourceFile)) { throw new ServiceException(ErrorCode.ILLEGAL_ARGUMENT, "there is no code"); } InstCodeInfo codeInfo = getProcessCodeInfo(sourceFile); codeInfo.setInstId(0L); codeInfo.setName(msCode); StringWriter compileInfo = new StringWriter(); boolean compileResult = false; try { compileResult = jdkCompiler.compileWithMsCode(codeInfo, compileInfo); } catch (Exception e) { throw new ServiceException(ErrorCode.ILLEGAL_ARGUMENT, "code compile fail: " + e.getMessage()); } if (!compileResult) { throw new ServiceException(ErrorCode.ILLEGAL_ARGUMENT, "code compile fail: " + compileInfo.getBuffer().toString()); } return compileResult; } /** * get file info * @param codeFiles * @return */ private InstCodeInfo getProcessCodeInfo(List codeFiles) { InstCodeInfo processCodeInfo = new InstCodeInfo(); if (CollectionUtils.isEmpty(codeFiles)) { return null; } for (File codeFile : codeFiles) { //filter the .java file if (codeFile == null || !StringUtils.endsWith(codeFile.getName(), ".java")) { continue; } FileInputStream in = null; try { in = new FileInputStream(codeFile); byte[] buffer = new byte[(int)codeFile.length()]; if (in.read(buffer) > 0) { String content = new String(buffer, "UTF-8"); JavaSourceCode unit = getCodeInfo(content); processCodeInfo.getJavaSourceCodes().add(unit); } } catch (Exception e) { LOGGER.error("read File Error!files = " + codeFiles, e); } finally { if (in != null) { try { in.close(); } catch (IOException e) { LOGGER.error("Close FileInputStream Failed! files=" + codeFiles); } } } } return processCodeInfo; } /** * get class info through code * @param content * @return */ private JavaSourceCode getCodeInfo(String content) { JavaSourceCode unit = new JavaSourceCode(); // the source unit.setSource(content); // class name and file name Matcher matcher = CLASS_PATTERN.matcher(content); if (matcher.find()) { unit.setClassName(matcher.group(2)); unit.setFileName(matcher.group(2) + ".java"); } // package name matcher = PACKAGE_PATTERN.matcher(content); if (matcher.find()) { unit.setPackageName(matcher.group(1)); } return unit; } public byte[] getJarFile(Long instId) throws IOException { return getJarFile(String.valueOf(instId)); } /** * get the packaged jar file data * @param msCode * @return * @throws IOException */ public byte[] getJarFile(String msCode) throws IOException { FileInputStream inputStream = null; byte[] zipFileBytes = null; try { // classse directory String zipDirectory = tacFileService.getClassFileOutputPath(msCode); String zipOutputPathName = zipDirectory + ".zip"; // the output zip File outputFile = new File(zipOutputPathName); if (outputFile.exists()) { TacFileService.deleteRecursively(outputFile); } TacCompressUtils.compress(zipDirectory + File.separator + "com", zipOutputPathName); final File zipFile = new File(zipOutputPathName); if (!zipFile.exists()) { return zipFileBytes; } inputStream = new FileInputStream(zipFile); zipFileBytes = FileCopyUtils.copyToByteArray(inputStream); } catch (Exception e) { return zipFileBytes; } finally { if (inputStream != null) { inputStream.close(); } } if (zipFileBytes == null) { return zipFileBytes; } return zipFileBytes; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/code/CodeLoadService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.code; import com.alibaba.tac.engine.compile.IJdkCompiler; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.io.File; import java.io.FileFilter; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** * @author jinshuan.li 12/02/2018 14:02 * * the code loader service class, load biz class through tac instance and jar file; */ @Slf4j @Service public class CodeLoadService { private CustomerClassLoader extendClassLoader = null; @PostConstruct public void init() { loadCustomerDirectory(); } @Value("${tac.extend.lib:extendlibs}") private String extendlibs; @Resource private IJdkCompiler jdkCompiler; @Resource private TacFileService tacFileService; static { } /** * * @param environment */ public static void changeClassLoader(Environment environment) { String extendlibs = environment.getProperty("tac.extend.lib", "extendlibs"); try { // change class loader ,this allow us load class in extend jars ClassLoader classLoader = CodeLoadService.changeClassLoader(extendlibs); } catch (Exception e) { log.error(e.getMessage(), e); } } /** * * change class loader ,this allow us load class in extend jars * @param path * @return */ public static ClassLoader changeClassLoader(String path) throws IllegalAccessException, NoSuchFieldException { try { File file = new File(path); if (!file.exists() || !file.isDirectory()) { return CodeLoadService.class.getClassLoader(); } ClassLoader classLoader = getAppClassLoader(); String[] list = file.list(); if (list == null || list.length == 0) { return classLoader; } URL[] urls = new URL[list.length]; int index = 0; String absolutePath = file.getAbsolutePath(); for (String jarFile : list) { File item = new File(absolutePath + "/" + jarFile); URL url = item.toURI().toURL(); urls[index] = url; index++; } // change classloader's parent log.info("find extendlibs . changing classloader...."); Field parent = ClassLoader.class.getDeclaredField("parent"); parent.setAccessible(true); SpringClassLoader extendClassLoader = new SpringClassLoader(urls, classLoader.getParent()); parent.set(classLoader, extendClassLoader); return classLoader; } catch (MalformedURLException e) { log.error(e.getMessage(), e); } return CodeLoadService.class.getClassLoader(); } /** * get the AppClassLoader * @return */ private static ClassLoader getAppClassLoader() { ClassLoader classLoader = CodeLoadService.class.getClassLoader(); while (classLoader != null) { if (!StringUtils.containsIgnoreCase(classLoader.getClass().getName(), "AppClassLoader")) { classLoader = classLoader.getParent(); } else { return classLoader; } } return null; } /** * change classloader * * @return * @throws NoSuchFieldException * @throws IllegalAccessException */ public static ClassLoader changeClassLoader() throws NoSuchFieldException, IllegalAccessException { String extendlibs = "extendlibs"; return changeClassLoader(extendlibs); } /** * loadCustomerDirectory this allow the console compile java file which import extend classes; */ private void loadCustomerDirectory() { try { File file = new File(extendlibs); if (!file.exists() || !file.isDirectory()) { return; } File[] files = file.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return StringUtils.endsWith(pathname.getName(), ".jar"); } }); if (files == null || files.length == 0) { return; } URL[] urls = new URL[files.length]; int index = 0; for (File jarFile : files) { URL url = jarFile.toURI().toURL(); urls[index] = url; jdkCompiler.addClassPath(jarFile); index++; } extendClassLoader = new CustomerClassLoader(urls, this.getClass().getClassLoader()); } catch (MalformedURLException e) { log.error(e.getMessage(), e); } } /** * load class in intance file * * @param instId * @return * @throws Exception */ public Class loadHandlerClass(Long instId, Class interfaceClass) throws Exception { ZipFile zipFile; String zipFileName = tacFileService.getLoadClassFilePath(instId); File file = new File(zipFileName); ClassLoader parent = null; if (this.extendClassLoader == null) { parent = this.getClass().getClassLoader(); } else { parent = this.extendClassLoader; } ClassLoader clazzLoader = new CustomerClassLoader(file, parent); zipFile = new ZipFile(file); Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String entName = entry.getName(); if (entry.isDirectory() || !entName.endsWith(".class")) { continue; } entName = entName.substring(0, entName.lastIndexOf('.')); String className = StringUtils.replaceChars(entName, '/', '.'); Class clazz = clazzLoader.loadClass(className); if (Modifier.isAbstract(clazz.getModifiers())) { continue; } if (interfaceClass.isAssignableFrom(clazz)) { return (Class)clazz; } } return null; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/code/CustomerClassLoader.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.code; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; /** * the CustomerClassLoader */ public class CustomerClassLoader extends URLClassLoader { static { extClassLoader = CustomerClassLoader.getSystemClassLoader().getParent(); } private static ClassLoader extClassLoader; public CustomerClassLoader(URL[] urls) { super(urls, CustomerClassLoader.class.getClassLoader()); } public CustomerClassLoader(File file) throws MalformedURLException { super(new URL[] {file.toURI().toURL()}, CustomerClassLoader.class.getClassLoader()); } public CustomerClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } public CustomerClassLoader(File file, ClassLoader parent) throws MalformedURLException { super(new URL[] {file.toURI().toURL()}, parent); } /** * * @param name * @param resolve * @return * @throws ClassNotFoundException * * @see ClassLoader#loadClass(java.lang.String, boolean) */ @Override protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { ClassLoader parent = this.getParent(); synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/code/SpringClassLoader.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.code; import java.net.URL; import java.net.URLClassLoader; /** * @author jinshuan.li 07/03/2018 10:18 */ public class SpringClassLoader extends URLClassLoader { public SpringClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { try { return super.loadClass(name, resolve); } catch (NoClassDefFoundError e) { // because we change the classloader , we should chaugh all exception here; return null; } } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/code/TacFileService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.code; import com.alibaba.tac.engine.properties.TacDataPathProperties; import com.google.common.collect.Lists; import com.google.common.hash.Hashing; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.DirectoryFileFilter; import org.apache.commons.io.filefilter.FileFileFilter; import org.springframework.stereotype.Service; import org.springframework.util.FileCopyUtils; import javax.annotation.Resource; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Collection; import java.util.List; @Service public class TacFileService { @Resource private TacDataPathProperties tacDataPathProperties; public String getLoadClassFilePath(Long processID) { String path; String classloadPathPrefix = tacDataPathProperties.getClassLoadPathPrefix(); if (!classloadPathPrefix.endsWith(File.separator)) { path = classloadPathPrefix + File.separator; } else { path = classloadPathPrefix; } path += processID + File.separator + processID + ".zip"; return path; } public String getClassFileOutputPath(Long processID) { return getClassFileOutputPath(String.valueOf(processID)); } public String getClassFileOutputPath(String suffix) { String path = ""; String outputPathPrefix = tacDataPathProperties.getOutputPathPrefix(); if (!outputPathPrefix.endsWith(File.separator)) { path = outputPathPrefix + File.separator; } else { path = outputPathPrefix; } path += suffix; return path; } public String getOutPutFilePath(Long processID) { return getClassFileOutputPath(processID) + ".zip"; } public String getOutPutFilePath(String suffix) { return getClassFileOutputPath(suffix) + ".zip"; } /** *listAllFiles * * @param directory * @return List */ public static List listAllFiles(File directory) { Collection files = FileUtils.listFiles(directory, FileFileFilter.FILE, DirectoryFileFilter.DIRECTORY); return Lists.newArrayList(files); } /** * * * @param file the file to delete * @throws IOException if an I/O error occurs * @see */ public static void deleteRecursively(File file) throws IOException { if (file.isDirectory()) { FileUtils.deleteDirectory(file); return ; } if (!file.delete()) { throw new IOException("Failed to delete " + file); } } /** * @param zipFile * @return * @throws IOException */ public static byte[] getFileBytes(File zipFile) throws IOException { FileInputStream inputStream = null; inputStream = new FileInputStream(zipFile); byte[] zipFileBytes = FileCopyUtils.copyToByteArray(inputStream); return zipFileBytes; } /** * @param data * @return */ public static String getMd5(byte[] data) { String md5 = Hashing.md5().hashBytes(data).toString(); return md5; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/common/DefaultTacIDGenerator.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.common; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * @author jinshuan.li 01/03/2018 13:10 */ @Service public class DefaultTacIDGenerator implements TacIDGenerator { @Resource(name = "msInstIdCounter") private SequenceCounter msInstIdCounter; @Override public Long getNextId() { return msInstIdCounter.incrementAndGet(); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/common/SequenceCounter.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.common; /** * @author jinshuan.li 2018/2/27 23:37 counter */ public interface SequenceCounter { /** * set value * @param value */ void set(long value); /** *get value * @return */ Long get(); /** * * incr and get * @return */ long incrementAndGet(); /** * * incr by a value * @param delta * @return */ long increBy(long delta); } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/common/TacIDGenerator.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.common; /** * @author jinshuan.li 01/03/2018 13:09 */ public interface TacIDGenerator { /** * get next id * * @return */ Long getNextId(); } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/common/redis/RedisSequenceCounter.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.common.redis; import com.alibaba.tac.engine.common.SequenceCounter; import com.alibaba.tac.engine.util.Bytes; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.Resource; /** * @author jinshuan.li 2018/3/1 07:35 */ @Slf4j public class RedisSequenceCounter implements SequenceCounter { /** * see counterRedisTemplate's ValueSerializer , the value will be convert to string ,then do incr * */ @Resource(name = "counterRedisTemplate") private ValueOperations valueOperations; private String counterKey; public RedisSequenceCounter(String counterKey) { this.counterKey = counterKey; } @Override public void set(long value) { valueOperations.set(counterKey, String.valueOf(value)); } @Override public Long get() { String s = valueOperations.get(counterKey); if (StringUtils.isEmpty(s)) { return null; } return Long.valueOf(s); } @Override public long incrementAndGet() { Long increment = valueOperations.increment(counterKey, 1); if (increment != null) { return increment; } return 0L; } @Override public long increBy(long delta) { Long increment = valueOperations.increment(counterKey, delta); if (increment != null) { return increment; } return 0L; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/compile/IJdkCompiler.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.compile; import java.io.File; import java.io.StringWriter; /** * @author jinshuan.li 12/02/2018 08:10 */ public interface IJdkCompiler { /** *compile code , the output name is instId * @param codeInfo * @param compileInfo * @return * @throws Exception */ boolean compile(InstCodeInfo codeInfo, StringWriter compileInfo) throws Exception; /** * compileWithMsCode, the output name is msCode * * * @param codeInfo * @param compileInfo * @return * @throws Exception */ boolean compileWithMsCode(InstCodeInfo codeInfo, StringWriter compileInfo) throws Exception; /** * add compile class path * * @param file */ void addClassPath(File file); } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/compile/InstCodeInfo.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.compile; import lombok.Data; import java.util.LinkedList; import java.util.List; /** * java code info */ @Data public class InstCodeInfo { /** * java code list */ List javaSourceCodes = new LinkedList(); /** * inst Id */ private Long instId; /** * inst Name */ private String name; /** * inst ClassName */ private String instClassName; } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/compile/JavaSourceCode.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.compile; import lombok.Data; /** * java source code info */ @Data public class JavaSourceCode { /** * class package name */ private String packageName; /** * class name without package name */ private String className; /** * the source content */ private String source; /** * the source file name */ private String fileName; public String getFullClassName() { return this.getPackageName().trim() + "." + this.getClassName(); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/compile/JdkCompilerImpl.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.compile; import com.alibaba.tac.engine.bootlaucher.BootJarLaucherUtils; import com.alibaba.tac.engine.properties.TacDataPathProperties; import com.alibaba.tac.engine.code.TacFileService; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import javax.tools.ToolProvider; import java.io.File; import java.io.FileFilter; import java.io.StringWriter; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class JdkCompilerImpl implements IJdkCompiler, InitializingBean { private static final Logger LOG = LoggerFactory.getLogger(JdkCompilerImpl.class); private static final List OPTIONS = new ArrayList(); private static final List CLASSPATH = new ArrayList(); static { OPTIONS.add("-target"); OPTIONS.add("1.8"); } @Resource private TacDataPathProperties tacDataPathProperties; @Resource private TacFileService tacFileService; public String sourcePathPrefix; public String outputPathPrefix; public String classLoadPathPrefix; public String pkgPrefix; @PostConstruct public void init() { this.sourcePathPrefix = tacDataPathProperties.getSourcePathPrefix(); this.outputPathPrefix = tacDataPathProperties.getOutputPathPrefix(); this.classLoadPathPrefix = tacDataPathProperties.getClassLoadPathPrefix(); this.pkgPrefix = tacDataPathProperties.getPkgPrefix(); } @Override public synchronized boolean compile(InstCodeInfo codeInfo, StringWriter compileInfo) throws Exception { return this.compile(codeInfo, compileInfo, String.valueOf(codeInfo.getInstId())); } @Override public synchronized boolean compileWithMsCode(InstCodeInfo codeInfo, StringWriter compileInfo) throws Exception { return this.compile(codeInfo, compileInfo, codeInfo.getName()); } @Override public void addClassPath(File file) { CLASSPATH.add(file); LOG.debug("add compile class path:{}", file.getAbsolutePath()); } private boolean compile(InstCodeInfo codeInfo, StringWriter compileInfo, String outputName) throws Exception { long start = 0; if (LOG.isInfoEnabled()) { start = System.currentTimeMillis(); } JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); File[] outputs = new File[] {new File(tacFileService.getClassFileOutputPath(outputName))}; for (File file : outputs) { if (!file.exists()) { file.mkdirs(); } else { TacFileService.deleteRecursively(file); file.mkdirs(); } } LOG.debug("compile classpath. size:{} CLASS-PATH:{}", CLASSPATH.size(), CLASSPATH); fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(outputs)); fileManager.setLocation(StandardLocation.CLASS_PATH, CLASSPATH); List fileObjects = new ArrayList(); for (JavaSourceCode unit : codeInfo.getJavaSourceCodes()) { TacJavaFileObject sourceObject = new TacJavaFileObject(unit.getFullClassName(), unit.getSource()); fileObjects.add(sourceObject); } CompilationTask task = compiler.getTask(compileInfo, fileManager, null, OPTIONS, null, fileObjects); Boolean resultSuccess = task.call(); if (resultSuccess == null || !resultSuccess) { return false; } fileManager.close(); if (LOG.isInfoEnabled()) { LOG.info("compile complete . name :{} instClassName:{} cost: {} resultSucess:{} ", codeInfo.getName(), codeInfo.getInstClassName(), (System.currentTimeMillis() - start), resultSuccess); } return true; } @Override public void afterPropertiesSet() throws Exception { initTacDict(); LOG.debug("init class path:\n"); ClassLoader classLoader = this.getClass().getClassLoader(); while (classLoader != null) { this.addClassLoaderClassPath(classLoader); classLoader = classLoader.getParent(); } addBootLibJars(); } /** * add boot lib jars to the compile classpath */ private void addBootLibJars() { File tempUnpackFolder = BootJarLaucherUtils.getTempUnpackFolder(); if (tempUnpackFolder != null && tempUnpackFolder.isDirectory() && tempUnpackFolder.exists()) { File[] files = tempUnpackFolder.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return StringUtils.endsWith(pathname.getName(), ".jar"); } }); for (File file : files) { CLASSPATH.add(file); LOG.debug("add compile class path:{} ", file.getPath()); } } } /** * @param classLoader */ private void addClassLoaderClassPath(ClassLoader classLoader) { ClassLoader loader = classLoader; if (loader instanceof URLClassLoader) { URLClassLoader urlClassLoader = (URLClassLoader)loader; URL[] urls = urlClassLoader.getURLs(); for (URL url : urls) { File file = new File(url.getFile()); if (file.exists()) { CLASSPATH.add(file); LOG.debug("add compile class path:{} ", file.getPath()); } } } else { LOG.error("need URLClassLoader!!"); } } private void initTacDict() { // class compile output directory File outputPathDic = new File(this.outputPathPrefix); if (!outputPathDic.exists()) { outputPathDic.mkdirs(); } /** * class load direcotry */ File classLoadDic = new File(this.classLoadPathPrefix); if (!classLoadDic.exists()) { classLoadDic.mkdirs(); } } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/compile/TacJavaFileObject.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.compile; import javax.tools.SimpleJavaFileObject; import java.io.IOException; import java.net.URI; public class TacJavaFileObject extends SimpleJavaFileObject { private String javaCode; protected TacJavaFileObject(String name, String content) { super(URI.create("file:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); this.javaCode = content; } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return javaCode; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/event/domain/AbstractMsEvent.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.event.domain; import lombok.Data; import org.springframework.context.ApplicationEvent; import java.io.Serializable; /** * @author jinshuan.li 26/02/2018 15:38 *

* the base event class */ @Data public abstract class AbstractMsEvent extends ApplicationEvent implements Serializable { private static final long serialVersionUID = 3861931814219403935L; /** * msCode */ private String msCode; public AbstractMsEvent() { super(System.currentTimeMillis()); } public AbstractMsEvent(String msCode) { super(System.currentTimeMillis()); this.msCode = msCode; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/event/domain/GetAllMsEvent.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.event.domain; import lombok.Data; import java.util.List; /** * @author jinshuan.li 26/02/2018 15:44 */ @Data public class GetAllMsEvent extends AbstractMsEvent { private List msCodes; public GetAllMsEvent(){ } public GetAllMsEvent(List msCodes) { this.msCodes = msCodes; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/event/domain/MsOfflineEvent.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.event.domain; import com.alibaba.tac.engine.inst.domain.TacInst; import lombok.Data; /** * @author jinshuan.li 27/02/2018 12:23 */ @Data public class MsOfflineEvent extends AbstractMsEvent { private TacInst tacInst; public MsOfflineEvent(TacInst tacInst) { super(tacInst.getMsCode()); this.tacInst = tacInst; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/event/domain/MsPublishEvent.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.event.domain; import com.alibaba.tac.engine.inst.domain.TacInst; import lombok.Data; /** * @author jinshuan.li 26/02/2018 15:45 */ @Data public class MsPublishEvent extends AbstractMsEvent { private TacInst tacInst; } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/event/domain/MsReceivePublishEvent.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.event.domain; import com.alibaba.tac.engine.inst.domain.TacInst; import lombok.Data; /** * @author jinshuan.li 26/02/2018 20:18 */ @Data public class MsReceivePublishEvent extends AbstractMsEvent { public MsReceivePublishEvent(TacInst tacInst) { this.tacInst = tacInst; } /** * the inst info */ private TacInst tacInst; } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/git/GitRepoService.java ================================================ package com.alibaba.tac.engine.git; import com.alibaba.tac.engine.properties.TacGitlabProperties; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.eclipse.jgit.api.*; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.io.File; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author jinshuan.li 06/05/2018 14:27 */ @Slf4j @Service public class GitRepoService { private final static String REMOTE_ORIGIN = "refs/remotes/origin/"; private Pattern gitPattern = Pattern.compile("git@(?[^:]+)(?:[0-9]+:)?:(?.+)"); @Resource private TacGitlabProperties tacGitlabProperties; /** * pull instance code * * @param gitURL * @param branch * @param projectName * @return */ public String pullInstanceCode(String gitURL, String projectName, String branch) { String httpURL = change2Http(gitURL); String groupName = tacGitlabProperties.getGroupName(); if (!localRepoExists(groupName, projectName, branch)) { cloneRepo(groupName, projectName, branch, httpURL); } // 拉分支数据 return pullRepo(groupName, projectName, branch); } /** * 修改为http链接 * * @param gitURL * @return */ private String change2Http(String gitURL) { String realURL = gitURL; if (StringUtils.startsWithIgnoreCase(gitURL, "git@")) { Matcher matcher = gitPattern.matcher(gitURL); if (!matcher.matches()) { throw new IllegalStateException("invalid git url" + gitURL); } String hostName = matcher.group("hostName"); String port = matcher.group("port"); String tail = matcher.group("tail"); if (StringUtils.isEmpty(port)) { realURL = String.format("http://%s/%s", hostName, tail); } else { realURL = String.format("http://%s:%s/%s", hostName, port, tail); } } return realURL; } /** * 拉取代码数据 * * @param groupName * @param projectName * @param branch */ private String pullRepo(String groupName, String projectName, String branch) { Git localGit = null; String localRepoDir = this.getLocalPath(groupName, projectName, branch); PullResult pullResult = null; try { localGit = Git.open(new File(localRepoDir)); PullCommand pullCommand = localGit.pull(); pullCommand.setRemote("origin").setCredentialsProvider( new UsernamePasswordCredentialsProvider(tacGitlabProperties.getUserName(), tacGitlabProperties.getPassword())).setTimeout(30); pullResult = pullCommand.call(); String header = localGit.getRepository().getBranch(); //如果当前分支header引用和branch分支不一致,切换分支 if (!branch.equals(header)) { //检测当前分支是否存在,不存在,返回异常 Ref remoteRef = localGit.getRepository().exactRef(REMOTE_ORIGIN + branch); if (null == remoteRef) { log.error("[Git Refresh Source] specified branch {} remote origin not exist.", branch); throw new IllegalStateException("remote origin not exist " + branch); } localGit.checkout().setForce(true).setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK) .setStartPoint( REMOTE_ORIGIN + branch).setName(branch).call(); } } catch (Exception e) { log.error("[Git Refresh Source] pull result error path:{} {}", localRepoDir, e.getMessage(), e); throw new IllegalStateException("pull source failed " + e.getMessage()); } if (null == pullResult || !pullResult.isSuccessful()) { log.error("[Git Refresh Source] pull result failed"); throw new IllegalStateException("pull source failed " + localRepoDir); } return localRepoDir; } /** * clone 代码仓库 * * @param groupName * @param projectName * @param branch */ private String cloneRepo(String groupName, String projectName, String branch, String gitURL) { String remoteURL = gitURL; String localPath = getLocalPath(groupName, projectName, branch); CloneCommand cloneCommand = Git.cloneRepository().setURI(remoteURL).setBranch(branch).setDirectory( new File(localPath)).setTimeout(30); Git git = null; cloneCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider(tacGitlabProperties.getUserName(), tacGitlabProperties.getPassword())); try { git = cloneCommand.call(); // 取分支 String existBranch = git.getRepository().getBranch(); if (!StringUtils.equals(branch, existBranch)) { throw new IllegalStateException(String.format("branch %s not exist", branch)); } } catch (Exception e) { log.error("[Git Refresh Source] clone repository error . remote:{} {}", remoteURL, e.getMessage(), e); throw new IllegalStateException("clone repository error ." + e.getMessage()); } return localPath; } /** * 本地数据是否存在 * * @param groupName * @param projectName * @param branch * @return */ public Boolean localRepoExists(String groupName, String projectName, String branch) { File file = new File(getLocalPath(groupName, projectName, branch) + ".git"); if (file.exists()) { return true; } return false; } /** * 本地代码路径 * * @param groupName * @param projectName * @param branch * @return */ private String getLocalPath(String groupName, String projectName, String branch) { StringBuilder localRepoDir = new StringBuilder(tacGitlabProperties.getBasePath()); localRepoDir.append(File.separator).append(groupName).append(File.separator).append(projectName).append( File.separator).append(branch).append(File.separator); return localRepoDir.toString(); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/inst/domain/TacInst.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.inst.domain; import lombok.Data; import java.io.Serializable; /** * The tac intance class */ @Data public class TacInst implements Serializable { private static final long serialVersionUID = -7830333085387154296L; public static final Integer STATUS_NEW = 0; public static final Integer STATUS_PRE_PUBLISH = 1; public static final Integer STATUS_PUBLISH = 2; /** * intanceId */ private long id; /** * name */ private String name; /** * the service code */ private String msCode; /** * data sign */ private String jarVersion; /** * status */ private Integer status; /** * prePublish sign */ private String prePublishJarVersion; /** * prePublish status */ private Integer prePublishStatus; /** * gitBranch */ private String gitBranch; } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/inst/domain/TacInstStatus.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.inst.domain; import lombok.Getter; /** * @author jinshuan.li 28/02/2018 13:27 */ public enum TacInstStatus { /** * invalid */ INVALID(-1), /** * normal */ NORMAL(0); private TacInstStatus(int code) { this.code = code; } @Getter private Integer code; public Integer code() { return code; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/inst/domain/TacInstanceInfo.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.inst.domain; import com.alibaba.tac.sdk.handler.TacHandler; import lombok.Data; import java.io.Serializable; /** * @author jinshuan.li 12/02/2018 17:28 */ @Data public class TacInstanceInfo implements Serializable{ private static final long serialVersionUID = 6433864847735515082L; /** * instanceId */ private long id; /** * intance data sign * */ private String jarVersion; /** * the tac handler class */ private TacHandler tacHandler; } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/inst/service/DevMsInstFileService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.inst.service; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.code.TacFileService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.io.File; import java.io.IOException; /** * @author jinshuan.li 27/02/2018 20:24 handle the inst file in dev client */ @Slf4j @Service public class DevMsInstFileService implements IMsInstFileService { @Override public byte[] getInstanceFile(long instId) { return getInstanceFile(String.valueOf(instId)); } @Resource private TacFileService tacFileService; public byte[] getInstanceFile(String msCode){ String zipFileName = tacFileService.getOutPutFilePath(msCode); final File zipFile = new File(zipFileName); byte[] zipFileBytes = null; if (!zipFile.exists()) { return zipFileBytes; } try { zipFileBytes = TacFileService.getFileBytes(zipFile); } catch (IOException e) { log.error(e.getMessage(), e); return null; } return zipFileBytes; } @Override public Boolean saveInstanceFile(TacInst tacInst, byte[] data) { throw new UnsupportedOperationException(); } /** * get file data in filePath * * @param filePath * @return */ public byte[] getInstanceFileData(String filePath) { byte[] zipFileBytes = null; try { final File zipFile = new File(filePath); zipFileBytes = TacFileService.getFileBytes(zipFile); } catch (IOException e) { log.error(e.getMessage(), e); return null; } return zipFileBytes; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/inst/service/IMsInstFileService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.inst.service; import com.alibaba.tac.engine.inst.domain.TacInst; /** * @author jinshuan.li 12/02/2018 17:46 */ public interface IMsInstFileService { /** * get inst file data * * @param instId * @return */ byte[] getInstanceFile(long instId); /** * save inst file data * * @param tacInst * @return */ Boolean saveInstanceFile(TacInst tacInst, byte[] data); } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/inst/service/IMsInstService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.inst.service; import com.alibaba.tac.engine.inst.domain.TacInst; import java.util.List; /** * @author jinshuan.li 12/02/2018 17:14 */ public interface IMsInstService { /** * get all tac instance * * @return */ List getAllTacMsInsts(); /** * get single instance * * @param msInstId * @return */ TacInst getTacMsInst(Long msInstId); /** * create instance * * @param tacInst * @return */ TacInst createTacMsInst(TacInst tacInst); /** * update instance * * @param instId * @param tacInst * @return */ Boolean updateTacMsInst(Long instId, TacInst tacInst); /** * remove instance * * @param instId * @return */ Boolean removeMsInst(Long instId); /** * getMsInsts * @param code * @return */ List getMsInsts(String code); /** * create default instance * * @param msCode * @param name * @param jarVersion * @return */ default TacInst createTacMsInst(String msCode, String name, String jarVersion) { TacInst tacInst = new TacInst(); tacInst.setJarVersion(jarVersion); tacInst.setMsCode(msCode); tacInst.setName(msCode); return this.createTacMsInst(tacInst); } /** * create git instance * * @param msCode * @param name * @param branch * @return */ default TacInst createGitTacMsInst(String msCode, String name, String branch) { TacInst tacInst = new TacInst(); tacInst.setGitBranch(branch); tacInst.setMsCode(msCode); tacInst.setName(name); return this.createTacMsInst(tacInst); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/inst/service/LocalMsInstFileService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.inst.service; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.code.TacFileService; import com.google.common.io.Files; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.util.FileCopyUtils; import javax.annotation.Resource; import java.io.File; import java.io.IOException; /** * @author jinshuan.li 12/02/2018 18:14 handle the inst file in container */ @Slf4j @Service public class LocalMsInstFileService implements IMsInstFileService { private static Logger LOGGER = LoggerFactory.getLogger(LocalMsInstFileService.class); @Resource private TacFileService tacFileService; @Override public byte[] getInstanceFile(long instId) { String zipFileName = tacFileService.getLoadClassFilePath(instId); final File zipFile = new File(zipFileName); byte[] zipFileBytes = null; if (!zipFile.exists()) { return zipFileBytes; } try { zipFileBytes = TacFileService.getFileBytes(zipFile); } catch (IOException e) { log.error(e.getMessage(), e); return null; } return zipFileBytes; } @Override public Boolean saveInstanceFile(TacInst tacInst, byte[] data) { // 文件存在删除 try { Long instId = tacInst.getId(); File zipOutFile = new File(tacFileService.getLoadClassFilePath(instId)); if (zipOutFile.exists()) { boolean deleteResult = zipOutFile.delete(); LOGGER.debug("TacInstanceLoader.loadTacHandler,instId:{} exists,delete result {}", instId, deleteResult); } else { final String saveFileName = tacFileService.getLoadClassFilePath(instId); LOGGER.debug("TacInstanceLoader.loadTacHandler,saveFileName:{} ", saveFileName); Files.createParentDirs(new File(saveFileName)); LOGGER.debug("TacInstanceLoader.loadTacHandler,createParentDirs success"); } zipOutFile.createNewFile(); LOGGER.debug("TacInstanceLoader.loadTacHandler,createNewFile success " + zipOutFile.getAbsolutePath()); FileCopyUtils.copy(data, zipOutFile); LOGGER.debug("TacInstanceLoader.loadTacHandler,createNewFile copy success"); return true; } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/inst/service/redis/RedisMsInstFileService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.inst.service.redis; import com.alibaba.fastjson.JSONObject; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.service.IMsInstFileService; import com.alibaba.tac.engine.properties.TacRedisConfigProperties; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.serializer.RedisSerializer; import javax.annotation.PostConstruct; import javax.annotation.Resource; /** * @author jinshuan.li 2018/3/1 07:57 */ @Slf4j public class RedisMsInstFileService implements IMsInstFileService { @Resource(name = "redisTemplate") private RedisTemplate redisTemplate; @Resource(name = "redisTemplate") private ValueOperations fileDataMetaOperations; @Resource private TacRedisConfigProperties redisConfig; private RedisSerializer keySerializer; private Boolean prePublish = false; @PostConstruct public void init() { keySerializer = redisTemplate.getKeySerializer(); } public RedisMsInstFileService(Boolean prePublish) { this.prePublish = prePublish; } @Override public byte[] getInstanceFile(long instId) { String dataPath = getDataPath(instId, null); JSONObject jsonObject = fileDataMetaOperations.get(dataPath); if (jsonObject == null) { return null; } String jarVersion = jsonObject.getString("jarVersion"); dataPath = getDataPath(instId, jarVersion); String finalDataPath = dataPath; // the bytes type should get the raw data, don't serializ it byte[] bytes = redisTemplate.execute(new RedisCallback() { @Override public byte[] doInRedis(RedisConnection connection) throws DataAccessException { return connection.get(keySerializer.serialize(finalDataPath)); } }); return bytes; } @Override public Boolean saveInstanceFile(TacInst tacInst, byte[] data) { String jarVersion; if (this.prePublish) { jarVersion = tacInst.getPrePublishJarVersion(); } else { jarVersion = tacInst.getJarVersion(); } if (StringUtils.isEmpty(jarVersion)) { throw new IllegalArgumentException("jarVersion"); } long instId = tacInst.getId(); if (instId <= 0) { throw new IllegalArgumentException("instId"); } if (data == null) { throw new IllegalArgumentException("data is null"); } String dataPath = getDataPath(instId, null); JSONObject metaData = new JSONObject(); metaData.put("jarVersion", jarVersion); fileDataMetaOperations.set(dataPath, metaData); dataPath = getDataPath(instId, jarVersion); String finalDataPath = dataPath; redisTemplate.execute(new RedisCallback() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { connection.set(keySerializer.serialize(finalDataPath), data); return null; } }); return true; } /** * get data path * * @param instId * @param jarVersion * @return */ private String getDataPath(Long instId, String jarVersion) { String dataPathPrefix = redisConfig.getDataPathPrefix(); if (this.prePublish) { dataPathPrefix += ".prepublish."; } if (StringUtils.isEmpty(jarVersion)) { return String.format("%s.%d", dataPathPrefix, instId); } return String.format("%s.%d.%s", dataPathPrefix, instId, jarVersion); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/inst/service/redis/RedisMsInstService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.inst.service.redis; import com.alibaba.fastjson.JSONObject; import com.alibaba.tac.engine.common.redis.RedisSequenceCounter; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.service.IMsInstService; import com.alibaba.tac.engine.properties.TacRedisConfigProperties; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import javax.annotation.Resource; import java.util.List; /** * @author jinshuan.li 2018/3/1 07:58 */ @Slf4j public class RedisMsInstService implements IMsInstService { /** * 计数器 */ @Resource(name = "msInstIdCounter") private RedisSequenceCounter sequenceCounter; @Resource private TacRedisConfigProperties tacRedisConfigProperties; @Resource(name = "redisTemplate") private RedisTemplate redisTemplate; @Resource(name = "redisTemplate") HashOperations hashOperations; @Override public List getAllTacMsInsts() { return hashOperations.values(getMainKey()); } @Override public TacInst getTacMsInst(Long msInstId) { TacInst data = hashOperations.get(getMainKey(), String.valueOf(msInstId)); if (data != null) { return data; } return null; } @Override public TacInst createTacMsInst(TacInst tacInst) { // 校验参数 checkInst(tacInst); long msInstId = sequenceCounter.incrementAndGet(); if (msInstId <= 0) { throw new IllegalStateException("error on get id "); } tacInst.setId(msInstId); hashOperations.putIfAbsent(getMainKey(), String.valueOf(tacInst.getId()), tacInst); hashOperations.put(getMainKey() + tacInst.getMsCode(), String.valueOf(tacInst.getId()), tacInst); return tacInst; } @Override public Boolean updateTacMsInst(Long instId, TacInst tacInst) { tacInst.setId(instId); hashOperations.put(getMainKey(), String.valueOf(instId), tacInst); hashOperations.put(getMainKey() + tacInst.getMsCode(), String.valueOf(tacInst.getId()), tacInst); return true; } @Override public Boolean removeMsInst(Long instId) { String key = String.valueOf(instId); TacInst tacInst = hashOperations.get(getMainKey(), key); if (tacInst != null) { hashOperations.delete(getMainKey() + tacInst.getMsCode(), key); } hashOperations.delete(getMainKey(), key); return true; } @Override public List getMsInsts(String code) { return hashOperations.values(getMainKey() + code); } private void checkInst(TacInst tacInst) { if (StringUtils.isEmpty(tacInst.getJarVersion()) && StringUtils.isEmpty(tacInst.getGitBranch())) { throw new IllegalArgumentException("jar version or branch is empty"); } String msCode = tacInst.getMsCode(); if (StringUtils.isEmpty(msCode)) { throw new IllegalArgumentException("msCode"); } } private String getMainKey() { return tacRedisConfigProperties.getMsInstMetaDataPath(); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/domain/TacMs.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.domain; import com.alibaba.tac.engine.inst.domain.TacInst; import lombok.Data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * The tac msService calss */ @Data public class TacMs implements Serializable { private static final long serialVersionUID = 6396890479297204348L; /** * ms id */ private long id; /** * mscode */ private String code; /** * name */ private String name; /** * inst list */ private List instList = new ArrayList(); } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/domain/TacMsDO.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.domain; import lombok.Data; import java.io.Serializable; /** * @author jinshuan.li 28/02/2018 13:25 */ @Data public class TacMsDO implements Serializable { private static final long serialVersionUID = 6723105954964294367L; /** * id */ private Long id; /** * code */ private String code; /** * name */ private String name; /** * status */ private Integer status = TacMsStatus.NORMAL.code(); /** * published inst id */ private Long publishedInstId; /** * */ private Boolean gitSupport; /** * */ private String gitRepo; } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/domain/TacMsPublishMeta.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.domain; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.properties.TacMsConstants; import lombok.Data; import java.io.Serializable; /** * @author jinshuan.li 27/02/2018 13:28 * * the publish meta data */ @Data public class TacMsPublishMeta implements Serializable { private static final long serialVersionUID = 7774540294768819287L; private TacInst tacInst; private Integer status = TacMsConstants.INST_STATUS_ONLINE; public TacMsPublishMeta() { } public TacMsPublishMeta(TacInst tacInst, Integer status) { this.tacInst = tacInst; this.status = status; } public TacMsPublishMeta(TacInst tacInst) { this.tacInst = tacInst; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/domain/TacMsStatus.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.domain; import lombok.Getter; /** * @author jinshuan.li 28/02/2018 13:27 */ public enum TacMsStatus { /** * invalid */ INVALID(-1), /** * normal */ NORMAL(0); private TacMsStatus(int code) { this.code = code; } @Getter private Integer code; public Integer code() { return code; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/service/AbstractDefaultMsPublisher.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.service; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.service.DevMsInstFileService; import javax.annotation.Resource; import java.text.MessageFormat; /** * @author jinshuan.li 01/03/2018 15:22 */ public abstract class AbstractDefaultMsPublisher implements IMsPublisher { @Resource private DevMsInstFileService devMsInstFileService; @Override public Boolean publish(TacInst tacInst) { long instId = tacInst.getId(); //1.1 get data byte[] instanceFile = devMsInstFileService.getInstanceFile(instId); if (instanceFile == null) { throw new IllegalArgumentException( MessageFormat.format("can't find local instance file . instId {0}", instId)); } //1.2 check sign checkSign(tacInst, instanceFile); return this.publish(tacInst, instanceFile); } @Override public Boolean prePublish(TacInst tacInst, byte[] data) { return null; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/service/DefaultMsEventHandlers.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.service; import com.alibaba.tac.engine.event.domain.GetAllMsEvent; import com.alibaba.tac.engine.event.domain.MsOfflineEvent; import com.alibaba.tac.engine.event.domain.MsReceivePublishEvent; import com.alibaba.tac.engine.service.TacInstanceContainerService; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import javax.annotation.PostConstruct; import javax.annotation.Resource; /** * @author jinshuan.li 26/02/2018 20:13 *

* 此处设置最高优先级 */ @Slf4j @Configuration @Order(Ordered.HIGHEST_PRECEDENCE) public class DefaultMsEventHandlers implements ApplicationEventPublisherAware { @Resource private TacInstanceContainerService tacInstanceContainerService; private ApplicationEventPublisher applicationEventPublisher; @PostConstruct public void init() { } @Bean public ApplicationListener getAllMsEventApplicationListener() { return new ApplicationListener() { @Override public void onApplicationEvent(GetAllMsEvent event) { } }; } @Bean public ApplicationListener msReceivePublishEventApplicationListener() { return new ApplicationListener() { @Override public void onApplicationEvent(MsReceivePublishEvent event) { try { log.info("handle MsReceivePublishEvent {}", event); tacInstanceContainerService.loadTacInstance(event.getTacInst()); } catch (Exception e) { log.error("load instance error tacInst:{} {}", event.getTacInst(), e.getMessage(), e); throw new IllegalStateException("load instance error " + e.getMessage()); } } }; } @Bean public ApplicationListener msOfflineEventApplicationListener() { return new ApplicationListener() { @Override public void onApplicationEvent(MsOfflineEvent event) { String msCode = event.getMsCode(); tacInstanceContainerService.offlineMs(msCode); } }; } public ApplicationEventPublisher getPublisher() { return applicationEventPublisher; } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/service/IMsPublisher.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.service; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.ms.domain.TacMs; import com.alibaba.tac.engine.ms.domain.TacMsDO; import com.google.common.hash.Hashing; import org.apache.commons.lang3.StringUtils; import java.text.MessageFormat; /** * @author jinshuan.li 26/02/2018 10:04 */ public interface IMsPublisher { /** * publish the instance * * @param tacInst * @return */ Boolean publish(TacInst tacInst); /** * publish the instance with data * * @param tacInst * @param data * @return */ Boolean publish(TacInst tacInst, byte[] data); /** * pre publish with data * * @param tacInst * @param data * @return */ Boolean prePublish(TacInst tacInst, byte[] data); /** * git 预发 * @param tacMsDO * @param tacInst * @return */ TacInst gitPrePublish(TacMsDO tacMsDO,TacInst tacInst); /** * offline instance * * @param tacInst * @return */ Boolean offline(TacInst tacInst); /** * check the instance data sign * * @param tacInst * @param instanceFile */ default void checkSign(TacInst tacInst, byte[] instanceFile) { String md5 = Hashing.md5().hashBytes(instanceFile).toString(); if (!StringUtils.equalsIgnoreCase(tacInst.getJarVersion(), md5)) { throw new IllegalStateException("instance jar version check error " + tacInst.getId()); } } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/service/IMsService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.service; import com.alibaba.tac.engine.ms.domain.TacMsDO; import org.apache.commons.lang3.StringUtils; import java.util.List; /** * @author jinshuan.li 12/02/2018 17:18 */ public interface IMsService { /** * create ms * * @param tacMsDO * @return */ TacMsDO createMs(TacMsDO tacMsDO); /** * remove ms * * @param msCode * @return */ Boolean removeMs(String msCode); /** * invalid ms * * @param msCode * @return */ Boolean invalidMs(String msCode); /** * update ms * * @param msCode * @param tacMsDO * @return */ Boolean updateMs(String msCode, TacMsDO tacMsDO); /** * get ms * * @param msCode * @return */ TacMsDO getMs(String msCode); /** * get all ms * * @return */ List getAllMs(); default void checkMsDO(TacMsDO tacMsDO) { if (tacMsDO == null) { throw new IllegalArgumentException("tacMsDO is null"); } if (StringUtils.isEmpty(tacMsDO.getCode()) || StringUtils.isEmpty(tacMsDO.getName())) { throw new IllegalArgumentException("code or name is empty"); } } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/service/IMsSubscriber.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.service; /** * @author jinshuan.li 26/02/2018 15:24 */ public interface IMsSubscriber { /** * begin subscribe event */ void subscribe(); } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/service/redis/RedisMsPublisher.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.service.redis; import com.alibaba.fastjson.JSONObject; import com.alibaba.tac.engine.code.CodeCompileService; import com.alibaba.tac.engine.git.GitRepoService; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.service.DevMsInstFileService; import com.alibaba.tac.engine.inst.service.IMsInstService; import com.alibaba.tac.engine.inst.service.redis.RedisMsInstFileService; import com.alibaba.tac.engine.ms.domain.TacMsDO; import com.alibaba.tac.engine.ms.domain.TacMsPublishMeta; import com.alibaba.tac.engine.ms.service.AbstractDefaultMsPublisher; import com.alibaba.tac.engine.ms.service.IMsService; import com.alibaba.tac.engine.properties.TacMsConstants; import com.alibaba.tac.engine.properties.TacRedisConfigProperties; import com.alibaba.tac.engine.code.TacFileService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import javax.annotation.Resource; /** * @author jinshuan.li 2018/3/1 07:58 the redis ms publisher */ @Slf4j public class RedisMsPublisher extends AbstractDefaultMsPublisher { @Resource private DevMsInstFileService devMsInstFileService; @Resource(name = "remoteMsInstFileService") private RedisMsInstFileService redisMsInstFileService; @Resource(name = "prePublishMsInstFileService") private RedisMsInstFileService prePublishMsInstFileService; @Resource private TacRedisConfigProperties tacRedisConfigProperties; @Resource(name = "redisTemplate") HashOperations hashOperations; @Resource private GitRepoService gitRepoService; @Resource private IMsInstService msInstService; @Resource private CodeCompileService codeCompileService; @Resource private IMsService msService; /** * the subsciber is serializ with string ,then the publisher should serialize with string too; */ @Resource(name = "counterRedisTemplate") private RedisTemplate redisTopicTemplate; @Override public Boolean publish(TacInst tacInst, byte[] data) { byte[] instanceFile = data; tacInst.setJarVersion(TacFileService.getMd5(data)); tacInst.setStatus(TacInst.STATUS_PUBLISH); // 1 save data redisMsInstFileService.saveInstanceFile(tacInst, instanceFile); // 2. save meta data String msCode = tacInst.getMsCode(); TacMsPublishMeta publishMeta = new TacMsPublishMeta(tacInst); hashOperations.put(getMainKey(), msCode, publishMeta); // 3. send message redisTopicTemplate.convertAndSend(getPublishChannel(), JSONObject.toJSONString(publishMeta)); TacMsDO ms = msService.getMs(tacInst.getMsCode()); // 4 update ms Long oldPublishId = ms.getPublishedInstId(); ms.setPublishedInstId(tacInst.getId()); msService.updateMs(tacInst.getMsCode(), ms); // 5 update instance msInstService.updateTacMsInst(tacInst.getId(), tacInst); if (oldPublishId != null && !oldPublishId.equals(tacInst.getId())) { TacInst tacMsInst = msInstService.getTacMsInst(oldPublishId); if (tacMsInst != null) { tacMsInst.setStatus(TacInst.STATUS_PRE_PUBLISH); msInstService.updateTacMsInst(oldPublishId, tacMsInst); } } return true; } @Override public Boolean offline(TacInst tacInst) { String msCode = tacInst.getMsCode(); // 1. update data TacMsPublishMeta publishMeta = new TacMsPublishMeta(tacInst, TacMsConstants.INST_STATUS_OFFLINE); hashOperations.put(getMainKey(), msCode, publishMeta); // 2. send message redisTopicTemplate.convertAndSend(getPublishChannel(), JSONObject.toJSONString(publishMeta)); return true; } private String getMainKey() { return tacRedisConfigProperties.getMsListPath(); } private String getPublishChannel() { return tacRedisConfigProperties.getPublishEventChannel(); } @Override public Boolean prePublish(TacInst tacInst, byte[] data) { byte[] instanceFile = data; tacInst.setStatus(TacInst.STATUS_PRE_PUBLISH); tacInst.setPrePublishJarVersion(TacFileService.getMd5(data)); // 1 save data prePublishMsInstFileService.saveInstanceFile(tacInst, instanceFile); // 2 update meta data msInstService.updateTacMsInst(tacInst.getId(), tacInst); return true; } @Override public TacInst gitPrePublish(TacMsDO tacMsDO, TacInst tacInst) { if (StringUtils.isEmpty(tacMsDO.getGitRepo())) { throw new IllegalArgumentException("no git repo address"); } String gitBranch = tacInst.getGitBranch(); if (StringUtils.isEmpty(gitBranch)) { throw new IllegalArgumentException("git branch is emplty"); } TacMsDO ms = tacMsDO; String sourcePath = gitRepoService.pullInstanceCode(ms.getGitRepo(), ms.getCode(), tacInst.getGitBranch()); try { codeCompileService.compile(tacInst.getId(), sourcePath); byte[] jarFile = codeCompileService.getJarFile(tacInst.getId()); this.prePublish(tacInst, jarFile); } catch (Exception e) { throw new IllegalStateException(e); } return msInstService.getTacMsInst(tacInst.getId()); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/service/redis/RedisMsService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.service.redis; import com.alibaba.fastjson.JSONObject; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.ms.domain.TacMsDO; import com.alibaba.tac.engine.ms.domain.TacMsStatus; import com.alibaba.tac.engine.ms.service.IMsService; import com.alibaba.tac.engine.properties.TacRedisConfigProperties; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; /** * @author jinshuan.li 2018/3/1 07:59 */ @Slf4j public class RedisMsService implements IMsService { @Resource private TacRedisConfigProperties tacRedisConfigProperties; @Resource(name = "redisTemplate") private RedisTemplate redisTemplate; @Resource(name = "redisTemplate") HashOperations hashOperations; @Override public TacMsDO createMs(TacMsDO tacMsDO) { checkMsDO(tacMsDO); hashOperations.put(getMainKey(), tacMsDO.getCode(), tacMsDO); return tacMsDO; } @Override public Boolean removeMs(String msCode) { hashOperations.delete(getMainKey(), msCode); return true; } @Override public Boolean invalidMs(String msCode) { TacMsDO ms = this.getMs(msCode); if (ms == null) { return true; } ms.setStatus(TacMsStatus.INVALID.code()); return this.updateMs(msCode, ms); } @Override public Boolean updateMs(String msCode, TacMsDO tacMsDO) { hashOperations.put(getMainKey(), msCode, tacMsDO); return true; } @Override public TacMsDO getMs(String msCode) { return hashOperations.get(getMainKey(), msCode); } @Override public List getAllMs() { return hashOperations.values(getMainKey()); } private String getMainKey() { return tacRedisConfigProperties.getMsMetaDataPath(); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/ms/service/redis/RedisMsSubscriber.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.ms.service.redis; import com.alibaba.fastjson.JSON; import com.alibaba.tac.engine.event.domain.GetAllMsEvent; import com.alibaba.tac.engine.event.domain.MsOfflineEvent; import com.alibaba.tac.engine.event.domain.MsReceivePublishEvent; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.ms.domain.TacMsPublishMeta; import com.alibaba.tac.engine.ms.service.DefaultMsEventHandlers; import com.alibaba.tac.engine.ms.service.IMsSubscriber; import com.alibaba.tac.engine.properties.TacMsConstants; import com.alibaba.tac.engine.properties.TacRedisConfigProperties; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import javax.annotation.Resource; import java.util.Collection; import java.util.Map; import java.util.Set; import static com.alibaba.tac.engine.properties.TacMsConstants.INST_STATUS_ONLINE; /** * @author jinshuan.li 2018/3/1 07:59 */ @Slf4j public class RedisMsSubscriber implements IMsSubscriber { @Resource private RedisMessageListenerContainer container; @Resource(name = "redisSubscribMessageAdapter") private MessageListenerAdapter messageListenerAdapter; @Resource private TacRedisConfigProperties tacRedisConfigProperties; @Resource(name = "redisTemplate") HashOperations hashOperations; @Resource private DefaultMsEventHandlers defaultMsEventHandlers; private Set loadedMsCodes = Sets.newHashSet(); /** * load all msCode */ public void loadAllMsCode() { Map entries = hashOperations.entries(getMainKey()); if (MapUtils.isEmpty(entries)) { return; } Collection values = entries.values(); if (CollectionUtils.isEmpty(values)) { return; } log.debug("on GetAllMsEvent : ", values); defaultMsEventHandlers.getPublisher().publishEvent(new GetAllMsEvent(Lists.newArrayList(entries.keySet()))); entries.forEach((msCode, tacMsPublishMeta) -> { if (loadedMsCodes.contains(msCode)) { return; } handleOnePublish(tacMsPublishMeta, true); loadedMsCodes.add(msCode); }); } private void handleOnePublish(TacMsPublishMeta publishMeta, Boolean isLoad) { log.debug("on tacInstData :{}", publishMeta); TacInst tacInst = publishMeta.getTacInst(); if (publishMeta.getStatus().equals(INST_STATUS_ONLINE)) { log.debug("publish inst tacInst:{} isload:{}", tacInst, isLoad); defaultMsEventHandlers.getPublisher().publishEvent(new MsReceivePublishEvent(tacInst)); } else if (publishMeta.getStatus().equals(TacMsConstants.INST_STATUS_OFFLINE)) { if (!isLoad) { log.debug("removePublished tacInst:{}", tacInst); defaultMsEventHandlers.getPublisher().publishEvent(new MsOfflineEvent(tacInst)); } } } @Override public void subscribe() { container.addMessageListener(messageListenerAdapter, new ChannelTopic(tacRedisConfigProperties.getPublishEventChannel())); this.loadAllMsCode(); } /** * receiveMessage, don't change the name and params * * @param message * @param channel */ public void receiveMessage(String message, String channel) { if (!StringUtils.equalsIgnoreCase(channel, getPublishChannel())) { return; } log.debug("receiveMessage. channel:{} message:{}", channel, message); TacMsPublishMeta publishMeta = JSON.parseObject(message, TacMsPublishMeta.class); handleOnePublish(publishMeta, false); } private String getMainKey() { return tacRedisConfigProperties.getMsListPath(); } private String getPublishChannel() { return tacRedisConfigProperties.getPublishEventChannel(); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/properties/TacDataPathProperties.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.properties; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * @author jinshuan.li 12/02/2018 16:45 */ @Component @ConfigurationProperties(prefix = "tac.data.path") public class TacDataPathProperties { private String sourcePathPrefix; @Value("${user.home}/tac/data/classes") private String outputPathPrefix; @Value("${user.home}/tac/data/ms") private String classLoadPathPrefix; private String pkgPrefix="com.alibaba.tac.biz"; public void setSourcePathPrefix(String sourcePathPrefix) { this.sourcePathPrefix = sourcePathPrefix; } public String getSourcePathPrefix() { return sourcePathPrefix; } public String getOutputPathPrefix() { return outputPathPrefix; } public void setOutputPathPrefix(String outputPathPrefix) { this.outputPathPrefix = outputPathPrefix; } public String getClassLoadPathPrefix() { return classLoadPathPrefix; } public void setClassLoadPathPrefix(String classLoadPathPrefix) { this.classLoadPathPrefix = classLoadPathPrefix; } public String getPkgPrefix() { return pkgPrefix; } public void setPkgPrefix(String pkgPrefix) { this.pkgPrefix = pkgPrefix; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/properties/TacGitlabProperties.java ================================================ package com.alibaba.tac.engine.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * @author jinshuan.li 06/05/2018 14:30 */ @Data @Component @ConfigurationProperties(prefix = "tac.gitlab.config") public class TacGitlabProperties { private String hostURL = "http://127.0.0.1/"; private String token = "t_bj6gJywKH2fCkbWY7k"; private String groupName = "tac-admin"; private String userName = "tac-admin"; private String password = "tac-admin"; private String basePath = "/home/admin/tac/git_codes"; } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/properties/TacMsConstants.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.properties; /** * @author jinshuan.li 26/02/2018 12:00 */ public class TacMsConstants { public static final String DEFAULT_STORE="redis"; /** * zookeeper max data size KB */ public static Integer ZooKeeperMaxDataSizeKB = 1000; /** * inst status online */ public static Integer INST_STATUS_ONLINE = 1; /** * inst status offline */ public static Integer INST_STATUS_OFFLINE = -1; } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/properties/TacRedisConfigProperties.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * @author jinshuan.li 2018/3/1 07:37 */ @Data @Component @ConfigurationProperties(prefix = "tac.redis.config") public class TacRedisConfigProperties { /** * msInst meta data path */ private String msInstMetaDataPath = "com.alibaba.tac.msInstMetaData"; /** * ms meta data path */ private String msMetaDataPath = "com.alibaba.tac.msMetaData"; /** * data path prefix */ private String dataPathPrefix = "msInstFile"; /** * ms list path */ private String msListPath = "msPublishedList"; /** * the event publish channel */ private String publishEventChannel = "tac.inst.publish.channel"; } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/service/DefaultTacEngineService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.service; import com.alibaba.tac.engine.inst.domain.TacInstanceInfo; import com.alibaba.tac.engine.util.TacLogUtils; import com.alibaba.tac.sdk.common.TacContants; import com.alibaba.tac.sdk.common.TacParams; import com.alibaba.tac.sdk.common.TacResult; import com.alibaba.tac.sdk.common.TacThreadLocals; import com.alibaba.tac.sdk.domain.TacRequestContext; import com.alibaba.tac.sdk.handler.TacHandler; import com.alibaba.tac.sdk.infrastracture.TacLogger; import com.alibaba.tac.sdk.utils.TacIPUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * @author jinshuan.li 12/02/2018 18:49 */ @Service public class DefaultTacEngineService implements TacEngineService, PriorityOrdered { private Logger LOGGER = LoggerFactory.getLogger(DefaultTacEngineService.class); @Resource private TacInstanceContainerService instanceContainerService; @Resource private TacInstRunService tacInstRunService; @Resource TacLogger TACLOG; @Override public TacResult> execute(String msCode, TacParams params) { /** 参数校验 **/ TacResult> result = new TacResult<>(Collections.EMPTY_MAP); if (params == null || StringUtils.isEmpty(params.getAppName())) { TacLogUtils.warnRate(LOGGER, "{}^{}^{}^{}^{}", params.getAppName(), params.getMsCodes(), params.isBatch(), 0, params.getParamMap()); return TacResult.errorResult("PARAM_ERROR", "app name should not be empty..."); } if (params == null || StringUtils.isEmpty(params.getMsCodes())) { TacLogUtils.warnRate(LOGGER, "{}^{}^{}^{}^{}", params.getAppName(), params.getMsCodes(), params.isBatch(), 0, params.getParamMap()); return TacResult.errorResult("PARAM_ERROR", "msCodes should not be empty..."); } /** set context param **/ TacRequestContext context = new TacRequestContext(); this.setTacContext(context, params); // debug param String debug = String.valueOf(params.getParamValue(TacContants.DEBUG)); Map resultDataMap = new HashMap<>(); Map singleDateMap = new HashMap<>(); long singleStartTime = System.currentTimeMillis(); try { TacThreadLocals.clear(); Map tacParams = new HashMap<>(5); if (params.getParamValue(TacContants.IP) != null) { tacParams.put(TacContants.IP, params.getParamValue(TacContants.IP)); } tacParams.put(TacContants.MS_CODE, msCode); if (params.getParamValue(TacContants.DEBUG) != null) { tacParams.put(TacContants.DEBUG, String.valueOf(params.getParamValue(TacContants.DEBUG))); } TacThreadLocals.TAC_PARAMS.set(tacParams); TacInstanceInfo tacInstanceInfo = instanceContainerService.getInstanceFromCache(msCode); if (tacInstanceInfo == null) { this.setCommonFields(singleDateMap, msCode, "NOT_EXIST", msCode + ": the inst is not exist...", false, debug); resultDataMap.put(msCode, singleDateMap); result.setData(resultDataMap); return result; } context.setMsCode(msCode); context.setInstId(tacInstanceInfo.getId()); /** execute user's ms code **/ TacResult singleResult = null; TacHandler tacHandler = tacInstanceInfo.getTacHandler(); singleResult = tacHandler.execute(context); /** the result **/ if (singleResult != null && singleResult.isSuccess()) { this.setCommonFields(singleDateMap, msCode, singleResult.getMsgCode(), singleResult.getMsgInfo(), singleResult.isSuccess(), debug); if (singleResult.getHasMore() != null) { singleDateMap.put("hasMore", singleResult.getHasMore()); } singleDateMap.put("data", singleResult.getData()); resultDataMap.put(msCode, singleDateMap); } else { this.setCommonFields(singleDateMap, msCode, singleResult.getMsgCode(), singleResult.getMsgInfo(), singleResult.isSuccess(), debug); singleDateMap.put("data", singleResult.getData()); resultDataMap.put(msCode, singleDateMap); } } catch (Exception e) { singleDateMap.put("msCode", msCode); singleDateMap.put("errorCode", "MicroService_EXCEPTION"); TACLOG.error( "-------------------------------------------------------------------------------------YourCode " + "Exception--------------------------------------------------------------------------------", e); singleDateMap.put("errorMsg", msCode + " error , please check your code"); singleDateMap.put("ip", TacIPUtils.getLocalIp()); singleDateMap.put("success", false); resultDataMap.put(msCode, singleDateMap); } finally { if ("true".equalsIgnoreCase(debug)) { result.setMsgInfo(TACLOG.getContent()); } LOGGER.info("Single^{}^{}^{}^{}", params.getAppName(), context.getMsCode(), false, System.currentTimeMillis() - singleStartTime); TacThreadLocals.clear(); } result.setData(resultDataMap); return result; } private void setCommonFields(Map singleDateMap, String msCode, String errorCode, String errorMsg, boolean success, String debug) { if (singleDateMap == null) { return; } singleDateMap.put("msCode", msCode); if (!success) { singleDateMap.put("errorCode", errorCode); singleDateMap.put("errorMsg", errorMsg); } if ("true".equalsIgnoreCase(debug)) { singleDateMap.put("ip", TacIPUtils.getLocalIp()); } singleDateMap.put("success", success); } private void setTacContext(TacRequestContext context, TacParams params) { /** app name **/ context.setAppName(params.getAppName()); /** custom params **/ context.putAll(params.getParamMap()); } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/service/EngineBeansConfig.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.service; import com.alibaba.tac.engine.compile.IJdkCompiler; import com.alibaba.tac.engine.compile.JdkCompilerImpl; import com.alibaba.tac.engine.properties.TacDataPathProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.annotation.Resource; /** * @author jinshuan.li 12/02/2018 16:42 *

* the engine beans config class */ @Configuration public class EngineBeansConfig { @Resource private TacDataPathProperties tacDataPathProperties; @Bean public IJdkCompiler jdkCompiler() { IJdkCompiler iJdkCompiler = new JdkCompilerImpl(); return iJdkCompiler; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/service/RedisBeansConfig.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.service; import com.alibaba.tac.engine.common.SequenceCounter; import com.alibaba.tac.engine.common.redis.RedisSequenceCounter; import com.alibaba.tac.engine.inst.service.redis.RedisMsInstFileService; import com.alibaba.tac.engine.inst.service.redis.RedisMsInstService; import com.alibaba.tac.engine.ms.service.IMsSubscriber; import com.alibaba.tac.engine.ms.service.redis.RedisMsPublisher; import com.alibaba.tac.engine.ms.service.redis.RedisMsService; import com.alibaba.tac.engine.ms.service.redis.RedisMsSubscriber; import com.alibaba.tac.engine.util.ThreadPoolUtils; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * @author jinshuan.li 01/03/2018 10:12 * * the beans while use redis store */ @ConditionalOnProperty(name = "tac.default.store",havingValue = "redis") @Configuration public class RedisBeansConfig { @Bean public RedisTemplate redisTemplate( JedisConnectionFactory jedisConnectionFactory) { return getRedisTemplate(jedisConnectionFactory); } @Bean(name = "counterRedisTemplate") public RedisTemplate counterRedisTemplate(JedisConnectionFactory jedisConnectionFactory) { return getCounterRedisTemplate(jedisConnectionFactory); } public static RedisTemplate getRedisTemplate(JedisConnectionFactory jedisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(jedisConnectionFactory); redisTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } /** * @param jedisConnectionFactory * @return */ public static RedisTemplate getCounterRedisTemplate(JedisConnectionFactory jedisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(jedisConnectionFactory); // the conter should use StringRedisSerializer redisTemplate.setValueSerializer(new StringRedisSerializer()); return redisTemplate; } @Bean public RedisMessageListenerContainer redisMessageListenerContainer(JedisConnectionFactory jedisConnectionFactory) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(jedisConnectionFactory); // set thread pool container.setTaskExecutor(ThreadPoolUtils.createThreadPool(10, "tac-redis-subscribe-pool")); return container; } @Bean(name = "redisSubscribMessageAdapter") public MessageListenerAdapter listenerAdapter(IMsSubscriber messageListener) { MessageListenerAdapter adapter = new MessageListenerAdapter(messageListener, "receiveMessage"); return adapter; } /** * the redis implements beans */ @Bean(name = "msInstIdCounter") public SequenceCounter msInstIdCounter() { return new RedisSequenceCounter("msInstIdCounter"); } @Bean(name = "remoteMsInstFileService") public RedisMsInstFileService redisMsInstFileService() { return new RedisMsInstFileService(false); } @Bean(name = "prePublishMsInstFileService") public RedisMsInstFileService redisPrePublishMsInstFileService() { return new RedisMsInstFileService(true); } @Bean public RedisMsInstService redisMsInstService() { return new RedisMsInstService(); } @Bean public RedisMsPublisher redisMsPublisher() { return new RedisMsPublisher(); } @Bean public RedisMsService redisMsService() { return new RedisMsService(); } @Bean public RedisMsSubscriber redisMsSubscriber() { return new RedisMsSubscriber(); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/service/TacEngineService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.service; import com.alibaba.tac.sdk.common.TacParams; import com.alibaba.tac.sdk.common.TacResult; import java.util.Map; /** * @author jinshuan.li 12/02/2018 18:44 *

* the engine entrance */ public interface TacEngineService { /** * execute msCode * * @param params * @return */ TacResult> execute(String msCode, TacParams params); } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/service/TacInstRunService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.service; import com.alibaba.tac.engine.code.CodeLoadService; import com.alibaba.tac.sdk.common.TacContants; import com.alibaba.tac.sdk.common.TacResult; import com.alibaba.tac.sdk.common.TacThreadLocals; import com.alibaba.tac.sdk.domain.TacRequestContext; import com.alibaba.tac.sdk.handler.DisposableHandler; import com.alibaba.tac.sdk.handler.InitializingHandler; import com.alibaba.tac.sdk.handler.TacHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.HashMap; import java.util.Map; /** * @author jinshuan.li 12/02/2018 14:11 */ @Service public class TacInstRunService { private Logger LOGGER = LoggerFactory.getLogger(TacInstRunService.class); @Resource private CodeLoadService codeLoadService; /** * @param instId * @return */ public TacResult runWithLoad(String msCode, Long instId, Map params) throws Exception { Class clazz = codeLoadService.loadHandlerClass(instId, TacHandler.class); if (clazz == null) { throw new RuntimeException("can't find target class"); } TacHandler handlerInstance = null; // get an instance handlerInstance = clazz.newInstance(); // init resource when the class implements InitializingHandler if (InitializingHandler.class.isAssignableFrom(handlerInstance.getClass())) { InitializingHandler initializingHandler = (InitializingHandler)handlerInstance; LOGGER.info("InitializingHandler instId:{} init : {}", instId, initializingHandler.getClass()); initializingHandler.afterPropertiesSet(); } try { TacRequestContext context = new TacRequestContext(); context.setMsCode(msCode); context.setInstId(instId); context.putAll(params); // clear threadlocals before invoke TacThreadLocals.clear(); Map tacParams = new HashMap<>(5); tacParams.put(TacContants.MS_CODE, msCode); tacParams.put(TacContants.DEBUG, true); TacThreadLocals.TAC_PARAMS.set(tacParams); // run TacResult execute = handlerInstance.execute(context); // get user log String runLog = getRunLog(); execute.setMsgInfo(runLog); // dispose resource try { if (DisposableHandler.class.isAssignableFrom(handlerInstance.getClass())) { DisposableHandler disposableHandler = (DisposableHandler)handlerInstance; LOGGER.info("DisposableHandler distory . instId:{} {}", instId, disposableHandler.getClass()); disposableHandler.destroy(); } } catch (Exception ex) { LOGGER.error("DisposableHandler error", ex); } return execute; } catch (Throwable e) { throw new RuntimeException(e); } finally { TacThreadLocals.clear(); } } public String getRunLog() { StringBuilder stringBuilder = TacThreadLocals.TAC_LOG_CONTENT.get(); if (stringBuilder != null) { return stringBuilder.toString(); } return ""; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/service/TacInstanceContainerService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.service; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.domain.TacInstanceInfo; import com.alibaba.tac.engine.inst.service.IMsInstService; import com.alibaba.tac.engine.ms.service.IMsSubscriber; import com.alibaba.tac.sdk.handler.DisposableHandler; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.collect.Maps; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; /** * @author jinshuan.li 12/02/2018 17:27 */ @Service public class TacInstanceContainerService implements ApplicationListener { private static Logger LOGGER = LoggerFactory.getLogger(TacInstanceLoadService.class); /** * cache */ private Cache tacInstCache = CacheBuilder.newBuilder().build(); @Resource private IMsInstService iMsInstService; @Resource private TacInstanceLoadService tacInstanceLoadService; @Resource private IMsSubscriber msSubscriber; private AtomicBoolean initFlag = new AtomicBoolean(false); /** * the locks prevent repeat load */ private Map instanceLoadLocks = Maps.newConcurrentMap(); public void init() { if (initFlag.compareAndSet(false, true)) { msSubscriber.subscribe(); } } /** * get from cache * * @param msCode * @return */ public TacInstanceInfo getInstanceFromCache(String msCode) { return tacInstCache.getIfPresent(msCode); } /** * load instance * * @param tacInst */ public TacInstanceInfo loadTacInstance(TacInst tacInst) throws Exception { String msCode = tacInst.getMsCode(); ReentrantLock reentrantLock = instanceLoadLocks.get(msCode); if (reentrantLock == null) { reentrantLock = new ReentrantLock(); instanceLoadLocks.put(msCode, reentrantLock); } reentrantLock.lock(); try { TacInstanceInfo existInstance = tacInstCache.getIfPresent(tacInst.getMsCode()); if (existInstance == null) { TacInstanceInfo tacInstanceInfo = tacInstanceLoadService.loadTacHandler(tacInst); assert tacInstanceInfo != null; LOGGER.info("TacInstanceContainer loadTacInstance , result : {}", tacInstanceInfo); tacInstCache.put(msCode, tacInstanceInfo); return tacInstanceInfo; } if (StringUtils.equals(tacInst.getJarVersion(), existInstance.getJarVersion())) { LOGGER.debug("the exist instance has the same jarVersion ,skip. {}", tacInst.getJarVersion()); LOGGER.info("TacInstanceContainer loadTacInstance has been load!"); return existInstance; } TacInstanceInfo tacInstanceInfo = tacInstanceLoadService.loadTacHandler(tacInst); assert tacInstanceInfo != null; LOGGER.info("TacInstanceContainer loadTacInstance , result : {}", tacInstanceInfo); tacInstCache.put(msCode, tacInstanceInfo); LOGGER.info("TacInstanceContainer loadTacInstance,msCode:{},instId:{},oldVersilon:{},newVersion:{}", msCode, tacInst.getId(), existInstance.getJarVersion(), tacInstanceInfo.getJarVersion()); if (existInstance != null) { this.disposeInstance(existInstance); } return tacInstanceInfo; } finally { reentrantLock.unlock(); } } /** * destroy instance * * @param oldTacInstanceInfo */ private void disposeInstance(TacInstanceInfo oldTacInstanceInfo) { try { if (oldTacInstanceInfo != null && DisposableHandler.class.isAssignableFrom( oldTacInstanceInfo.getTacHandler().getClass())) { DisposableHandler disposableHandler = (DisposableHandler)oldTacInstanceInfo.getTacHandler(); LOGGER.info("TacInstanceContainer oldTacInstanceInfo distory : " + oldTacInstanceInfo.getTacHandler() .getClass()); disposableHandler.destroy(); } } catch (Exception ex) { LOGGER.error("TacInstanceContainer DisposableHandler error", ex); } } @Override public void onApplicationEvent(ContextRefreshedEvent event) { try { this.init(); } catch (Throwable e) { LOGGER.error(e.getMessage(), e); // throw exception immediatly when has error while start up throw new IllegalStateException("init tacInstanceInfo error"); } } /** * offline Ms * * @param msCode */ public void offlineMs(String msCode) { TacInstanceInfo tacInstanceInfo = tacInstCache.getIfPresent(msCode); if (tacInstanceInfo != null) { LOGGER.info("offlineMs msCode:{}", msCode); tacInstCache.invalidate(msCode); this.disposeInstance(tacInstanceInfo); } } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/service/TacInstanceLoadService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.service; import com.alibaba.tac.engine.code.CodeLoadService; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.domain.TacInstanceInfo; import com.alibaba.tac.engine.inst.service.IMsInstFileService; import com.alibaba.tac.engine.inst.service.LocalMsInstFileService; import com.alibaba.tac.sdk.handler.InitializingHandler; import com.alibaba.tac.sdk.handler.TacHandler; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * @author jinshuan.li 12/02/2018 17:27 */ @Service public class TacInstanceLoadService { private static Logger LOGGER = LoggerFactory.getLogger(TacInstanceLoadService.class); @Resource(name = "remoteMsInstFileService") private IMsInstFileService remoteMsInstFileService; @Resource private LocalMsInstFileService localMsInstFileService; @Resource private CodeLoadService codeLoadService; public TacInstanceInfo loadTacHandler(TacInst tacInst) throws Exception { LOGGER.info("TacInstanceLoader.loadTacHandler start,tacMs:{}", tacInst); if (tacInst == null) { return null; } //Step 1: download zip data TacInstanceInfo tacInstanceInfo = new TacInstanceInfo(); long instId = tacInst.getId(); byte[] bytes = remoteMsInstFileService.getInstanceFile(tacInst.getId()); tacInstanceInfo.setJarVersion(tacInst.getJarVersion()); tacInstanceInfo.setId(instId); LOGGER.info("TacInstanceLoader.loadTacHandler,msCode:{},instId:{},jarVersion:{}", tacInst.getMsCode(), instId, tacInst.getJarVersion()); if (bytes == null || StringUtils.isEmpty(tacInstanceInfo.getJarVersion())) { throw new IllegalStateException("can't get jar file . instId:" + tacInst.getId()); } localMsInstFileService.saveInstanceFile(tacInst, bytes); //Step 2: create CustomerClassLoader load jar file Class clazz = codeLoadService.loadHandlerClass(instId, TacHandler.class); if (clazz == null) { LOGGER.error("can't find the calss {} from source. instId:{}", TacHandler.class.getCanonicalName(), instId); throw new IllegalStateException("can't find the TacHandler.calss from source. instId:" + tacInst.getId()); } TacHandler tacHandler = clazz.newInstance(); LOGGER.info( "InitializingHandler init class : " + tacHandler.getClass() + " , isInit :" + InitializingHandler.class .isAssignableFrom(tacHandler.getClass())); // init resource if (InitializingHandler.class.isAssignableFrom(tacHandler.getClass())) { InitializingHandler initializingHandler = (InitializingHandler)tacHandler; LOGGER.info("InitializingHandler init : " + initializingHandler.getClass()); initializingHandler.afterPropertiesSet(); } tacInstanceInfo.setTacHandler(tacHandler); LOGGER.info("TacInstanceLoader.loadTacHandler,instId {} end ..", instId); return tacInstanceInfo; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/service/TacPublishTestService.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.service; import com.alibaba.fastjson.JSONObject; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.service.IMsInstFileService; import com.alibaba.tac.engine.inst.service.IMsInstService; import com.alibaba.tac.engine.inst.service.LocalMsInstFileService; import com.alibaba.tac.sdk.common.TacContants; import com.alibaba.tac.sdk.common.TacParams; import com.alibaba.tac.sdk.common.TacResult; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.ListenableFuture; import org.asynchttpclient.Response; import org.asynchttpclient.util.HttpConstants; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static org.asynchttpclient.Dsl.asyncHttpClient; /** * @author jinshuan.li 06/03/2018 14:34 */ @Slf4j @Service public class TacPublishTestService { @Resource private IMsInstService msInstService; @Resource(name = "prePublishMsInstFileService") private IMsInstFileService prePublishMsInstFileService; @Resource private LocalMsInstFileService localMsInstFileService; @Resource private TacInstRunService tacInstRunService; @Resource private TacEngineService tacEngineService; @Value("${tac.container.web.api:http://localhost:8001/api/tac/execute}") private String containerWebApi; /** * prePublishTest * * @param instId * @param msCode * @param params * @return */ public TacResult prePublishTest(Long instId, String msCode, Map params) throws Exception { TacInst tacMsInst = msInstService.getTacMsInst(instId); if (tacMsInst == null) { throw new IllegalArgumentException("the instance is not exist"); } if (!StringUtils.equalsIgnoreCase(tacMsInst.getMsCode(), msCode)) { throw new IllegalArgumentException("the code is not match"); } byte[] instanceFile = prePublishMsInstFileService.getInstanceFile(instId); if (instanceFile == null) { throw new IllegalStateException("can't get instance file"); } // save data to local localMsInstFileService.saveInstanceFile(tacMsInst, instanceFile); // load and run return tacInstRunService.runWithLoad(msCode, instId, params); } /** * online publish test * * @param instId * @param msCode * @param params * @return */ public TacResult onlinePublishTest(Long instId, String msCode, Map params) { // 走http请求调用 if (params == null) { params = Maps.newHashMap(); } params.put(TacContants.DEBUG, true); return onlinePublishTestHttp(instId, msCode, params); } /** * test with http . * * @param instId * @param msCode * @param params * @return */ private TacResult onlinePublishTestHttp(Long instId, String msCode, Map params) { AsyncHttpClient asyncHttpClient = asyncHttpClient(); ListenableFuture execute = asyncHttpClient.preparePost(containerWebApi + "/" + msCode) .addHeader("Content-Type", "application/json;charset=UTF-8").setBody(JSONObject.toJSONString(params)) .execute(); Response response; try { response = execute.get(10, TimeUnit.SECONDS); if (response.getStatusCode() == HttpConstants.ResponseStatusCodes.OK_200) { TacResult tacResult = JSONObject.parseObject(response.getResponseBody(), TacResult.class); return tacResult; } log.error("onlinePublishTestHttp msCode:{} params:{} {}", msCode, params, response); throw new IllegalStateException("request engine error " + msCode); } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/util/Bytes.java ================================================ /** * Copyright 2010 The Apache Software Foundation *

* 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.alibaba.tac.engine.util; import com.alibaba.tac.engine.util.Bytes.LexicographicalComparerHolder.UnsafeComparer; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import sun.misc.Unsafe; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; /** * copy from hbase bytes */ /** * Utility class that handles byte arrays, conversions to/from other types, comparisons, hash code generation, * manufacturing keys for HashMaps or HashSets, etc. */ public class Bytes { private static final Log LOG = LogFactory.getLog(Bytes.class); /** * Size of boolean in bytes */ public static final int SIZEOF_BOOLEAN = Byte.SIZE / Byte.SIZE; /** * Size of byte in bytes */ public static final int SIZEOF_BYTE = SIZEOF_BOOLEAN; /** * Size of char in bytes */ public static final int SIZEOF_CHAR = Character.SIZE / Byte.SIZE; /** * Size of double in bytes */ public static final int SIZEOF_DOUBLE = Double.SIZE / Byte.SIZE; /** * Size of float in bytes */ public static final int SIZEOF_FLOAT = Float.SIZE / Byte.SIZE; /** * Size of int in bytes */ public static final int SIZEOF_INT = Integer.SIZE / Byte.SIZE; /** * Size of long in bytes */ public static final int SIZEOF_LONG = Long.SIZE / Byte.SIZE; /** * Size of short in bytes */ public static final int SIZEOF_SHORT = Short.SIZE / Byte.SIZE; /** * Estimate of size cost to pay beyond payload in jvm for instance of byte []. Estimate based on study of jhat and * jprofiler numbers. */ // JHat says BU is 56 bytes. // SizeOf which uses java.lang.instrument says 24 bytes. (3 longs?) public static final int ESTIMATED_HEAP_TAX = 16; /** * Put bytes at the specified byte array position. * * @param tgtBytes the byte array * @param tgtOffset position in the array * @param srcBytes array to write out * @param srcOffset source offset * @param srcLength source length * @return incremented offset */ public static int putBytes(byte[] tgtBytes, int tgtOffset, byte[] srcBytes, int srcOffset, int srcLength) { System.arraycopy(srcBytes, srcOffset, tgtBytes, tgtOffset, srcLength); return tgtOffset + srcLength; } /** * Write a single byte out to the specified byte array position. * * @param bytes the byte array * @param offset position in the array * @param b byte to write out * @return incremented offset */ public static int putByte(byte[] bytes, int offset, byte b) { bytes[offset] = b; return offset + 1; } /** * Returns a new byte array, copied from the passed ByteBuffer. * * @param bb A ByteBuffer * @return the byte array */ public static byte[] toBytes(ByteBuffer bb) { int length = bb.limit(); byte[] result = new byte[length]; System.arraycopy(bb.array(), bb.arrayOffset(), result, 0, length); return result; } /** * @param b Presumed UTF-8 encoded byte array. * @return String made from b */ public static String toString(final byte[] b) { if (b == null) { return null; } return toString(b, 0, b.length); } /** * Joins two byte arrays together using a separator. * * @param b1 The first byte array. * @param sep The separator to use. * @param b2 The second byte array. */ public static String toString(final byte[] b1, String sep, final byte[] b2) { return toString(b1, 0, b1.length) + sep + toString(b2, 0, b2.length); } /** * Converts the given byte buffer, from its array offset to its limit, to a string. The position and the mark are * ignored. * * @param buf a byte buffer * @return a string representation of the buffer's binary contents */ public static String toString(ByteBuffer buf) { if (buf == null) { return null; } return toString(buf.array(), buf.arrayOffset(), buf.limit()); } /** * This method will convert utf8 encoded bytes into a string. If an UnsupportedEncodingException occurs, this method * will eat it and return null instead. * * @param b Presumed UTF-8 encoded byte array. * @param off offset into array * @param len length of utf-8 sequence * @return String made from b or null */ public static String toString(final byte[] b, int off, int len) { if (b == null) { return null; } if (len == 0) { return ""; } try { return new String(b, off, len, HConstants.UTF8_ENCODING); } catch (UnsupportedEncodingException e) { LOG.error("UTF-8 not supported?", e); return null; } } /** * Write a printable representation of a byte array. * * @param b byte array * @return string * @see #toStringBinary(byte[], int, int) */ public static String toStringBinary(final byte[] b) { if (b == null) { return "null"; } return toStringBinary(b, 0, b.length); } /** * Converts the given byte buffer, from its array offset to its limit, to a string. The position and the mark are * ignored. * * @param buf a byte buffer * @return a string representation of the buffer's binary contents */ public static String toStringBinary(ByteBuffer buf) { if (buf == null) { return "null"; } return toStringBinary(buf.array(), buf.arrayOffset(), buf.limit()); } /** * Write a printable representation of a byte array. Non-printable characters are hex escaped in the format \\x%02X, * eg: \x00 \x05 etc * * @param b array to write out * @param off offset to start at * @param len length to write * @return string output */ public static String toStringBinary(final byte[] b, int off, int len) { StringBuilder result = new StringBuilder(); try { String first = new String(b, off, len, "ISO-8859-1"); for (int i = 0; i < first.length(); ++i) { int ch = first.charAt(i) & 0xFF; if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || " `~!@#$%^&*()-_=+[]{}\\|;:'\",.<>/?".indexOf(ch) >= 0) { result.append(first.charAt(i)); } else { result.append(String.format("\\x%02X", ch)); } } } catch (UnsupportedEncodingException e) { LOG.error("ISO-8859-1 not supported?", e); } return result.toString(); } private static boolean isHexDigit(char c) { return (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'); } /** * Takes a ASCII digit in the range A-F0-9 and returns the corresponding integer/ordinal value. * * @param ch The hex digit. * @return The converted hex value as a byte. */ public static byte toBinaryFromHex(byte ch) { if (ch >= 'A' && ch <= 'F') { return (byte)((byte)10 + (byte)(ch - 'A')); } // else return (byte)(ch - '0'); } public static byte[] toBytesBinary(String in) { // this may be bigger than we need, but lets be safe. byte[] b = new byte[in.length()]; int size = 0; for (int i = 0; i < in.length(); ++i) { char ch = in.charAt(i); // If current char is '\', we check if next char is 'x', and if it is we must make // sure there're two more chars after 'x', since the String might ends with "\x" // For example, check Bytes.toStringBinary(Bytes.toBytes(1409531337848L)) if (ch == '\\' && in.length() > i + 3 && in.charAt(i + 1) == 'x') { // ok, take next 2 hex digits. char hd1 = in.charAt(i + 2); char hd2 = in.charAt(i + 3); // they need to be A-F0-9: if (!isHexDigit(hd1) || !isHexDigit(hd2)) { // this indicates the "\x" here is not escape code but two real bytes '\' and 'x' b[size++] = (byte)ch; continue; } // turn hex ASCII digit -> number byte d = (byte)((toBinaryFromHex((byte)hd1) << 4) + toBinaryFromHex((byte)hd2)); b[size++] = d; i += 3; // skip 3 } else { b[size++] = (byte)ch; } } // resize: byte[] b2 = new byte[size]; System.arraycopy(b, 0, b2, 0, size); return b2; } /** * Converts a string to a UTF-8 byte array. * * @param s string * @return the byte array */ public static byte[] toBytes(String s) { try { return s.getBytes(HConstants.UTF8_ENCODING); } catch (UnsupportedEncodingException e) { LOG.error("UTF-8 not supported?", e); return null; } } /** * Convert a boolean to a byte array. True becomes -1 and false becomes 0. * * @param b value * @return b encoded in a byte array. */ public static byte[] toBytes(final boolean b) { return new byte[] {b ? (byte)-1 : (byte)0}; } /** * Reverses {@link #toBytes(boolean)} * * @param b array * @return True or false. */ public static boolean toBoolean(final byte[] b) { if (b.length != 1) { throw new IllegalArgumentException("Array has wrong size: " + b.length); } return b[0] != (byte)0; } /** * Convert a long value to a byte array using big-endian. * * @param val value to convert * @return the byte array */ public static byte[] toBytes(long val) { byte[] b = new byte[8]; for (int i = 7; i > 0; i--) { b[i] = (byte)val; val >>>= 8; } b[0] = (byte)val; return b; } /** * Converts a byte array to a long value. Reverses {@link #toBytes(long)} * * @param bytes array * @return the long value */ public static long toLong(byte[] bytes) { return toLong(bytes, 0, SIZEOF_LONG); } /** * Converts a byte array to a long value. Assumes there will be {@link #SIZEOF_LONG} bytes available. * * @param bytes bytes * @param offset offset * @return the long value */ public static long toLong(byte[] bytes, int offset) { return toLong(bytes, offset, SIZEOF_LONG); } /** * Converts a byte array to a long value. * * @param bytes array of bytes * @param offset offset into array * @param length length of data (must be {@link #SIZEOF_LONG}) * @return the long value * @throws IllegalArgumentException if length is not {@link #SIZEOF_LONG} or if there's not enough room in the array * at the offset indicated. */ public static long toLong(byte[] bytes, int offset, final int length) { if (length != SIZEOF_LONG || offset + length > bytes.length) { throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_LONG); } if (UnsafeComparer.isAvailable()) { return toLongUnsafe(bytes, offset); } else { long l = 0; for (int i = offset; i < offset + length; i++) { l <<= 8; l ^= bytes[i] & 0xFF; } return l; } } private static IllegalArgumentException explainWrongLengthOrOffset(final byte[] bytes, final int offset, final int length, final int expectedLength) { String reason; if (length != expectedLength) { reason = "Wrong length: " + length + ", expected " + expectedLength; } else { reason = "offset (" + offset + ") + length (" + length + ") exceed the" + " capacity of the array: " + bytes.length; } return new IllegalArgumentException(reason); } /** * Put a long value out to the specified byte array position. * * @param bytes the byte array * @param offset position in the array * @param val long to write out * @return incremented offset * @throws IllegalArgumentException if the byte array given doesn't have enough room at the offset specified. */ public static int putLong(byte[] bytes, int offset, long val) { if (bytes.length - offset < SIZEOF_LONG) { throw new IllegalArgumentException("Not enough room to put a long at" + " offset " + offset + " in a " + bytes.length + " byte array"); } if (UnsafeComparer.isAvailable()) { return putLongUnsafe(bytes, offset, val); } else { for (int i = offset + 7; i > offset; i--) { bytes[i] = (byte)val; val >>>= 8; } bytes[offset] = (byte)val; return offset + SIZEOF_LONG; } } /** * Put a long value out to the specified byte array position (Unsafe). * * @param bytes the byte array * @param offset position in the array * @param val long to write out * @return incremented offset */ public static int putLongUnsafe(byte[] bytes, int offset, long val) { if (UnsafeComparer.littleEndian) { val = Long.reverseBytes(val); } UnsafeComparer.theUnsafe.putLong(bytes, (long)offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET, val); return offset + SIZEOF_LONG; } /** * Presumes float encoded as IEEE 754 floating-point "single format" * * @param bytes byte array * @return Float made from passed byte array. */ public static float toFloat(byte[] bytes) { return toFloat(bytes, 0); } /** * Presumes float encoded as IEEE 754 floating-point "single format" * * @param bytes array to convert * @param offset offset into array * @return Float made from passed byte array. */ public static float toFloat(byte[] bytes, int offset) { return Float.intBitsToFloat(toInt(bytes, offset, SIZEOF_INT)); } /** * @param bytes byte array * @param offset offset to write to * @param f float value * @return New offset in bytes */ public static int putFloat(byte[] bytes, int offset, float f) { return putInt(bytes, offset, Float.floatToRawIntBits(f)); } /** * @param f float value * @return the float represented as byte [] */ public static byte[] toBytes(final float f) { // Encode it as int return Bytes.toBytes(Float.floatToRawIntBits(f)); } /** * @param bytes byte array * @return Return double made from passed bytes. */ public static double toDouble(final byte[] bytes) { return toDouble(bytes, 0); } /** * @param bytes byte array * @param offset offset where double is * @return Return double made from passed bytes. */ public static double toDouble(final byte[] bytes, final int offset) { return Double.longBitsToDouble(toLong(bytes, offset, SIZEOF_LONG)); } /** * @param bytes byte array * @param offset offset to write to * @param d value * @return New offset into array bytes */ public static int putDouble(byte[] bytes, int offset, double d) { return putLong(bytes, offset, Double.doubleToLongBits(d)); } /** * Serialize a double as the IEEE 754 double format output. The resultant array will be 8 bytes long. * * @param d value * @return the double represented as byte [] */ public static byte[] toBytes(final double d) { // Encode it as a long return Bytes.toBytes(Double.doubleToRawLongBits(d)); } /** * Convert an int value to a byte array * * @param val value * @return the byte array */ public static byte[] toBytes(int val) { byte[] b = new byte[4]; for (int i = 3; i > 0; i--) { b[i] = (byte)val; val >>>= 8; } b[0] = (byte)val; return b; } /** * Converts a byte array to an int value * * @param bytes byte array * @return the int value */ public static int toInt(byte[] bytes) { return toInt(bytes, 0, SIZEOF_INT); } /** * Converts a byte array to an int value * * @param bytes byte array * @param offset offset into array * @return the int value */ public static int toInt(byte[] bytes, int offset) { return toInt(bytes, offset, SIZEOF_INT); } /** * Converts a byte array to an int value * * @param bytes byte array * @param offset offset into array * @param length length of int (has to be {@link #SIZEOF_INT}) * @return the int value * @throws IllegalArgumentException if length is not {@link #SIZEOF_INT} or if there's not enough room in the array * at the offset indicated. */ public static int toInt(byte[] bytes, int offset, final int length) { if (length != SIZEOF_INT || offset + length > bytes.length) { throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_INT); } if (UnsafeComparer.isAvailable()) { return toIntUnsafe(bytes, offset); } else { int n = 0; for (int i = offset; i < (offset + length); i++) { n <<= 8; n ^= bytes[i] & 0xFF; } return n; } } /** * Converts a byte array to an int value (Unsafe version) * * @param bytes byte array * @param offset offset into array * @return the int value */ public static int toIntUnsafe(byte[] bytes, int offset) { if (UnsafeComparer.littleEndian) { return Integer.reverseBytes(UnsafeComparer.theUnsafe.getInt(bytes, (long)offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET)); } else { return UnsafeComparer.theUnsafe.getInt(bytes, (long)offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET); } } /** * Converts a byte array to an short value (Unsafe version) * * @param bytes byte array * @param offset offset into array * @return the short value */ public static short toShortUnsafe(byte[] bytes, int offset) { if (UnsafeComparer.littleEndian) { return Short.reverseBytes(UnsafeComparer.theUnsafe.getShort(bytes, (long)offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET)); } else { return UnsafeComparer.theUnsafe.getShort(bytes, (long)offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET); } } /** * Converts a byte array to an long value (Unsafe version) * * @param bytes byte array * @param offset offset into array * @return the long value */ public static long toLongUnsafe(byte[] bytes, int offset) { if (UnsafeComparer.littleEndian) { return Long.reverseBytes(UnsafeComparer.theUnsafe.getLong(bytes, (long)offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET)); } else { return UnsafeComparer.theUnsafe.getLong(bytes, (long)offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET); } } /** * Put an int value out to the specified byte array position. * * @param bytes the byte array * @param offset position in the array * @param val int to write out * @return incremented offset * @throws IllegalArgumentException if the byte array given doesn't have enough room at the offset specified. */ public static int putInt(byte[] bytes, int offset, int val) { if (bytes.length - offset < SIZEOF_INT) { throw new IllegalArgumentException("Not enough room to put an int at" + " offset " + offset + " in a " + bytes.length + " byte array"); } if (UnsafeComparer.isAvailable()) { return putIntUnsafe(bytes, offset, val); } else { for (int i = offset + 3; i > offset; i--) { bytes[i] = (byte)val; val >>>= 8; } bytes[offset] = (byte)val; return offset + SIZEOF_INT; } } /** * Put an int value out to the specified byte array position (Unsafe). * * @param bytes the byte array * @param offset position in the array * @param val int to write out * @return incremented offset */ public static int putIntUnsafe(byte[] bytes, int offset, int val) { if (UnsafeComparer.littleEndian) { val = Integer.reverseBytes(val); } UnsafeComparer.theUnsafe.putInt(bytes, (long)offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET, val); return offset + SIZEOF_INT; } /** * Convert a short value to a byte array of {@link #SIZEOF_SHORT} bytes long. * * @param val value * @return the byte array */ public static byte[] toBytes(short val) { byte[] b = new byte[SIZEOF_SHORT]; b[1] = (byte)val; val >>= 8; b[0] = (byte)val; return b; } /** * Converts a byte array to a short value * * @param bytes byte array * @return the short value */ public static short toShort(byte[] bytes) { return toShort(bytes, 0, SIZEOF_SHORT); } /** * Converts a byte array to a short value * * @param bytes byte array * @param offset offset into array * @return the short value */ public static short toShort(byte[] bytes, int offset) { return toShort(bytes, offset, SIZEOF_SHORT); } /** * Converts a byte array to a short value * * @param bytes byte array * @param offset offset into array * @param length length, has to be {@link #SIZEOF_SHORT} * @return the short value * @throws IllegalArgumentException if length is not {@link #SIZEOF_SHORT} or if there's not enough room in the * array at the offset indicated. */ public static short toShort(byte[] bytes, int offset, final int length) { if (length != SIZEOF_SHORT || offset + length > bytes.length) { throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_SHORT); } if (UnsafeComparer.isAvailable()) { return toShortUnsafe(bytes, offset); } else { short n = 0; n ^= bytes[offset] & 0xFF; n <<= 8; n ^= bytes[offset + 1] & 0xFF; return n; } } /** * This method will get a sequence of bytes from pos -> limit, but will restore pos after. * * @param buf * @return byte array */ public static byte[] getBytes(ByteBuffer buf) { int savedPos = buf.position(); byte[] newBytes = new byte[buf.remaining()]; buf.get(newBytes); buf.position(savedPos); return newBytes; } /** * Put a short value out to the specified byte array position. * * @param bytes the byte array * @param offset position in the array * @param val short to write out * @return incremented offset * @throws IllegalArgumentException if the byte array given doesn't have enough room at the offset specified. */ public static int putShort(byte[] bytes, int offset, short val) { if (bytes.length - offset < SIZEOF_SHORT) { throw new IllegalArgumentException("Not enough room to put a short at" + " offset " + offset + " in a " + bytes.length + " byte array"); } if (UnsafeComparer.isAvailable()) { return putShortUnsafe(bytes, offset, val); } else { bytes[offset + 1] = (byte)val; val >>= 8; bytes[offset] = (byte)val; return offset + SIZEOF_SHORT; } } /** * Put a short value out to the specified byte array position (Unsafe). * * @param bytes the byte array * @param offset position in the array * @param val short to write out * @return incremented offset */ public static int putShortUnsafe(byte[] bytes, int offset, short val) { if (UnsafeComparer.littleEndian) { val = Short.reverseBytes(val); } UnsafeComparer.theUnsafe.putShort(bytes, (long)offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET, val); return offset + SIZEOF_SHORT; } /** * Convert a BigDecimal value to a byte array * * @param val * @return the byte array */ public static byte[] toBytes(BigDecimal val) { byte[] valueBytes = val.unscaledValue().toByteArray(); byte[] result = new byte[valueBytes.length + SIZEOF_INT]; int offset = putInt(result, 0, val.scale()); putBytes(result, offset, valueBytes, 0, valueBytes.length); return result; } /** * Converts a byte array to a BigDecimal * * @param bytes * @return the char value */ public static BigDecimal toBigDecimal(byte[] bytes) { return toBigDecimal(bytes, 0, bytes.length); } /** * Converts a byte array to a BigDecimal value * * @param bytes * @param offset * @param length * @return the char value */ public static BigDecimal toBigDecimal(byte[] bytes, int offset, final int length) { if (bytes == null || length < SIZEOF_INT + 1 || (offset + length > bytes.length)) { return null; } int scale = toInt(bytes, offset); byte[] tcBytes = new byte[length - SIZEOF_INT]; System.arraycopy(bytes, offset + SIZEOF_INT, tcBytes, 0, length - SIZEOF_INT); return new BigDecimal(new BigInteger(tcBytes), scale); } /** * Put a BigDecimal value out to the specified byte array position. * * @param bytes the byte array * @param offset position in the array * @param val BigDecimal to write out * @return incremented offset */ public static int putBigDecimal(byte[] bytes, int offset, BigDecimal val) { if (bytes == null) { return offset; } byte[] valueBytes = val.unscaledValue().toByteArray(); byte[] result = new byte[valueBytes.length + SIZEOF_INT]; offset = putInt(result, offset, val.scale()); return putBytes(result, offset, valueBytes, 0, valueBytes.length); } /** * @param left left operand * @param right right operand * @return 0 if equal, < 0 if left is less than right, etc. */ public static int compareTo(final byte[] left, final byte[] right) { if (left == null) { return right == null ? 0 : -1; } else if (right == null) { return 1; } return LexicographicalComparerHolder.BEST_COMPARER. compareTo(left, 0, left.length, right, 0, right.length); } /** * Lexicographically compare two arrays. * * @param buffer1 left operand * @param buffer2 right operand * @param offset1 Where to start comparing in the left buffer * @param offset2 Where to start comparing in the right buffer * @param length1 How much to compare from the left buffer * @param length2 How much to compare from the right buffer * @return 0 if equal, < 0 if left is less than right, etc. */ public static int compareTo(byte[] buffer1, int offset1, int length1, byte[] buffer2, int offset2, int length2) { return LexicographicalComparerHolder.BEST_COMPARER. compareTo(buffer1, offset1, length1, buffer2, offset2, length2); } interface Comparer { abstract public int compareTo(T buffer1, int offset1, int length1, T buffer2, int offset2, int length2); } @VisibleForTesting static Comparer lexicographicalComparerJavaImpl() { return LexicographicalComparerHolder.PureJavaComparer.INSTANCE; } /** * Provides a lexicographical comparer implementation; either a Java implementation or a faster implementation based * on {@link Unsafe}. *

*

Uses reflection to gracefully fall back to the Java implementation if {@code Unsafe} isn't available. */ static class LexicographicalComparerHolder { static final String UNSAFE_COMPARER_NAME = LexicographicalComparerHolder.class.getName() + "$UnsafeComparer"; static final Comparer BEST_COMPARER = getBestComparer(); /** * Returns the Unsafe-using Comparer, or falls back to the pure-Java implementation if unable to do so. */ static Comparer getBestComparer() { try { Class theClass = Class.forName(UNSAFE_COMPARER_NAME); // yes, UnsafeComparer does implement Comparer @SuppressWarnings("unchecked") Comparer comparer = (Comparer)theClass.getEnumConstants()[0]; return comparer; } catch (Throwable t) { // ensure we really catch *everything* return lexicographicalComparerJavaImpl(); } } enum PureJavaComparer implements Comparer { /** * */ INSTANCE; @Override public int compareTo(byte[] buffer1, int offset1, int length1, byte[] buffer2, int offset2, int length2) { // Short circuit equal case if (buffer1 == buffer2 && offset1 == offset2 && length1 == length2) { return 0; } // Bring WritableComparator code local int end1 = offset1 + length1; int end2 = offset2 + length2; for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) { int a = (buffer1[i] & 0xff); int b = (buffer2[j] & 0xff); if (a != b) { return a - b; } } return length1 - length2; } } @VisibleForTesting enum UnsafeComparer implements Comparer { /** * */ INSTANCE; static final Unsafe theUnsafe; /** * The offset to the first element in a byte array. */ static final int BYTE_ARRAY_BASE_OFFSET; static { theUnsafe = (Unsafe)AccessController.doPrivileged( new PrivilegedAction() { @Override public Object run() { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return f.get(null); } catch (NoSuchFieldException e) { // It doesn't matter what we throw; // it's swallowed in getBestComparer(). throw new Error(); } catch (IllegalAccessException e) { throw new Error(); } } }); BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); // sanity check - this should never fail if (theUnsafe.arrayIndexScale(byte[].class) != 1) { throw new AssertionError(); } } static final boolean littleEndian = ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); /** * Returns true if x1 is less than x2, when both values are treated as unsigned long. */ static boolean lessThanUnsignedLong(long x1, long x2) { return (x1 + Long.MIN_VALUE) < (x2 + Long.MIN_VALUE); } /** * Returns true if x1 is less than x2, when both values are treated as unsigned int. */ static boolean lessThanUnsignedInt(int x1, int x2) { return (x1 & 0xffffffffL) < (x2 & 0xffffffffL); } /** * Returns true if x1 is less than x2, when both values are treated as unsigned short. */ static boolean lessThanUnsignedShort(short x1, short x2) { return (x1 & 0xffff) < (x2 & 0xffff); } /** * Checks if Unsafe is available * * @return true, if available, false - otherwise */ public static boolean isAvailable() { return theUnsafe != null; } /** * Lexicographically compare two arrays. * * @param buffer1 left operand * @param buffer2 right operand * @param offset1 Where to start comparing in the left buffer * @param offset2 Where to start comparing in the right buffer * @param length1 How much to compare from the left buffer * @param length2 How much to compare from the right buffer * @return 0 if equal, < 0 if left is less than right, etc. */ @Override public int compareTo(byte[] buffer1, int offset1, int length1, byte[] buffer2, int offset2, int length2) { // Short circuit equal case if (buffer1 == buffer2 && offset1 == offset2 && length1 == length2) { return 0; } final int minLength = Math.min(length1, length2); final int minWords = minLength / SIZEOF_LONG; final long offset1Adj = offset1 + BYTE_ARRAY_BASE_OFFSET; final long offset2Adj = offset2 + BYTE_ARRAY_BASE_OFFSET; /* * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a * time is no slower than comparing 4 bytes at a time even on 32-bit. * On the other hand, it is substantially faster on 64-bit. */ for (int i = 0; i < minWords * SIZEOF_LONG; i += SIZEOF_LONG) { long lw = theUnsafe.getLong(buffer1, offset1Adj + (long)i); long rw = theUnsafe.getLong(buffer2, offset2Adj + (long)i); long diff = lw ^ rw; if (littleEndian) { lw = Long.reverseBytes(lw); rw = Long.reverseBytes(rw); } if (diff != 0) { return lessThanUnsignedLong(lw, rw) ? -1 : 1; } } int offset = minWords * SIZEOF_LONG; if (minLength - offset >= SIZEOF_INT) { int il = theUnsafe.getInt(buffer1, offset1Adj + offset); int ir = theUnsafe.getInt(buffer2, offset2Adj + offset); if (littleEndian) { il = Integer.reverseBytes(il); ir = Integer.reverseBytes(ir); } if (il != ir) { return lessThanUnsignedInt(il, ir) ? -1 : 1; } offset += SIZEOF_INT; } if (minLength - offset >= SIZEOF_SHORT) { short sl = theUnsafe.getShort(buffer1, offset1Adj + offset); short sr = theUnsafe.getShort(buffer2, offset2Adj + offset); if (littleEndian) { sl = Short.reverseBytes(sl); sr = Short.reverseBytes(sr); } if (sl != sr) { return lessThanUnsignedShort(sl, sr) ? -1 : 1; } offset += SIZEOF_SHORT; } if (minLength - offset == 1) { int a = (buffer1[(int)(offset1 + offset)] & 0xff); int b = (buffer2[(int)(offset2 + offset)] & 0xff); if (a != b) { return a - b; } } return length1 - length2; } } } /** * @param left left operand * @param right right operand * @return True if equal */ public static boolean equals(final byte[] left, final byte[] right) { // Could use Arrays.equals? //noinspection SimplifiableConditionalExpression if (left == right) { return true; } if (left == null || right == null) { return false; } if (left.length != right.length) { return false; } if (left.length == 0) { return true; } // Since we're often comparing adjacent sorted data, // it's usual to have equal arrays except for the very last byte // so check that first if (left[left.length - 1] != right[right.length - 1]) { return false; } return compareTo(left, right) == 0; } public static boolean equals(final byte[] left, int leftOffset, int leftLen, final byte[] right, int rightOffset, int rightLen) { // short circuit case if (left == right && leftOffset == rightOffset && leftLen == rightLen) { return true; } // different lengths fast check if (leftLen != rightLen) { return false; } if (leftLen == 0) { return true; } // Since we're often comparing adjacent sorted data, // it's usual to have equal arrays except for the very last byte // so check that first if (left[leftOffset + leftLen - 1] != right[rightOffset + rightLen - 1]) { return false; } return LexicographicalComparerHolder.BEST_COMPARER. compareTo(left, leftOffset, leftLen, right, rightOffset, rightLen) == 0; } /** * Return true if the byte array on the right is a prefix of the byte array on the left. */ public static boolean startsWith(byte[] bytes, byte[] prefix) { return bytes != null && prefix != null && bytes.length >= prefix.length && LexicographicalComparerHolder.BEST_COMPARER. compareTo(bytes, 0, prefix.length, prefix, 0, prefix.length) == 0; } /** * @param a lower half * @param b upper half * @return New array that has a in lower half and b in upper half. */ public static byte[] add(final byte[] a, final byte[] b) { return add(a, b, HConstants.EMPTY_BYTE_ARRAY); } /** * @param a first third * @param b second third * @param c third third * @return New array made from a, b and c */ public static byte[] add(final byte[] a, final byte[] b, final byte[] c) { byte[] result = new byte[a.length + b.length + c.length]; System.arraycopy(a, 0, result, 0, a.length); System.arraycopy(b, 0, result, a.length, b.length); System.arraycopy(c, 0, result, a.length + b.length, c.length); return result; } /** * @param a first array * @param b second array * @return New array merge from a, b */ public static byte[][] merge(final byte[][] a, final byte[][] b) { int length = 0; if (a != null) { length += a.length; } if (b != null) { length += b.length; } if (length == 0) { return null; } byte[][] c = new byte[length][]; if (a != null) { for (int i = 0; i < a.length; i++) { c[i] = copy(a[i]); } } if (b != null) { for (int i = 0; i < b.length; i++) { c[i + (a == null ? 0 : a.length)] = copy(b[i]); } } return c; } /** * @param a array * @param length amount of bytes to grab * @return First length bytes from a */ public static byte[] head(final byte[] a, final int length) { if (a.length < length) { return null; } byte[] result = new byte[length]; System.arraycopy(a, 0, result, 0, length); return result; } /** * @param a array * @param length amount of bytes to snarf * @return Last length bytes from a */ public static byte[] tail(final byte[] a, final int length) { if (a.length < length) { return null; } byte[] result = new byte[length]; System.arraycopy(a, a.length - length, result, 0, length); return result; } /** * @param a array * @param length new array size * @return Value in a plus length prepended 0 bytes */ public static byte[] padHead(final byte[] a, final int length) { byte[] padding = new byte[length]; for (int i = 0; i < length; i++) { padding[i] = 0; } return add(padding, a); } /** * @param a array * @param length new array size * @return Value in a plus length appended 0 bytes */ public static byte[] padTail(final byte[] a, final int length) { byte[] padding = new byte[length]; for (int i = 0; i < length; i++) { padding[i] = 0; } return add(a, padding); } /** * Split passed range. Expensive operation relatively. Uses BigInteger math. Useful splitting ranges for MapReduce * jobs. * * @param a Beginning of range * @param b End of range * @param num Number of times to split range. Pass 1 if you want to split the range in two; i.e. one split. * @return Array of dividing values */ public static byte[][] split(final byte[] a, final byte[] b, final int num) { return split(a, b, false, num); } /** * Split passed range. Expensive operation relatively. Uses BigInteger math. Useful splitting ranges for MapReduce * jobs. * * @param a Beginning of range * @param b End of range * @param inclusive Whether the end of range is prefix-inclusive or is considered an exclusive boundary. Automatic * splits are generally exclusive and manual splits with an explicit range utilize an inclusive end * of range. * @param num Number of times to split range. Pass 1 if you want to split the range in two; i.e. one split. * @return Array of dividing values */ public static byte[][] split(final byte[] a, final byte[] b, boolean inclusive, final int num) { byte[][] ret = new byte[num + 2][]; int i = 0; Iterable iter = iterateOnSplits(a, b, inclusive, num); if (iter == null) { return null; } for (byte[] elem : iter) { ret[i++] = elem; } return ret; } /** * Iterate over keys within the passed range, splitting at an [a,b) boundary. */ public static Iterable iterateOnSplits(final byte[] a, final byte[] b, final int num) { return iterateOnSplits(a, b, false, num); } /** * Iterate over keys within the passed range. */ public static Iterable iterateOnSplits( final byte[] a, final byte[] b, boolean inclusive, final int num) { byte[] aPadded; byte[] bPadded; if (a.length < b.length) { aPadded = padTail(a, b.length - a.length); bPadded = b; } else if (b.length < a.length) { aPadded = a; bPadded = padTail(b, a.length - b.length); } else { aPadded = a; bPadded = b; } if (compareTo(aPadded, bPadded) >= 0) { throw new IllegalArgumentException("b <= a"); } if (num <= 0) { throw new IllegalArgumentException("num cannot be < 0"); } byte[] prependHeader = {1, 0}; final BigInteger startBI = new BigInteger(add(prependHeader, aPadded)); final BigInteger stopBI = new BigInteger(add(prependHeader, bPadded)); BigInteger diffBI = stopBI.subtract(startBI); if (inclusive) { diffBI = diffBI.add(BigInteger.ONE); } final BigInteger splitsBI = BigInteger.valueOf(num + 1); if (diffBI.compareTo(splitsBI) < 0) { return null; } final BigInteger intervalBI; try { intervalBI = diffBI.divide(splitsBI); } catch (Exception e) { LOG.error("Exception caught during division", e); return null; } final Iterator iterator = new Iterator() { private int i = -1; @Override public boolean hasNext() { return i < num + 1; } @Override public byte[] next() { i++; if (i == 0) { return a; } if (i == num + 1) { return b; } BigInteger curBI = startBI.add(intervalBI.multiply(BigInteger.valueOf(i))); byte[] padded = curBI.toByteArray(); if (padded[1] == 0) { padded = tail(padded, padded.length - 2); } else { padded = tail(padded, padded.length - 1); } return padded; } @Override public void remove() { throw new UnsupportedOperationException(); } }; return new Iterable() { @Override public Iterator iterator() { return iterator; } }; } /** * @param bytes array to hash * @param offset offset to start from * @param length length to hash */ public static int hashCode(byte[] bytes, int offset, int length) { int hash = 1; for (int i = offset; i < offset + length; i++) { hash = (31 * hash) + (int)bytes[i]; } return hash; } /** * @param t operands * @return Array of byte arrays made from passed array of Text */ public static byte[][] toByteArrays(final String[] t) { byte[][] result = new byte[t.length][]; for (int i = 0; i < t.length; i++) { result[i] = Bytes.toBytes(t[i]); } return result; } /** * @param column operand * @return A byte array of a byte array where first and only entry is column */ public static byte[][] toByteArrays(final String column) { return toByteArrays(toBytes(column)); } /** * @param column operand * @return A byte array of a byte array where first and only entry is column */ public static byte[][] toByteArrays(final byte[] column) { byte[][] result = new byte[1][]; result[0] = column; return result; } /** * Bytewise binary increment/deincrement of long contained in byte array on given amount. * * @param value - array of bytes containing long (length <= SIZEOF_LONG) * @param amount value will be incremented on (deincremented if negative) * @return array of bytes containing incremented long (length == SIZEOF_LONG) */ public static byte[] incrementBytes(byte[] value, long amount) { byte[] val = value; if (val.length < SIZEOF_LONG) { // Hopefully this doesn't happen too often. byte[] newvalue; if (val[0] < 0) { newvalue = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1}; } else { newvalue = new byte[SIZEOF_LONG]; } System.arraycopy(val, 0, newvalue, newvalue.length - val.length, val.length); val = newvalue; } else if (val.length > SIZEOF_LONG) { throw new IllegalArgumentException("Increment Bytes - value too big: " + val.length); } if (amount == 0) { return val; } if (val[0] < 0) { return binaryIncrementNeg(val, amount); } return binaryIncrementPos(val, amount); } /* increment/deincrement for positive value */ private static byte[] binaryIncrementPos(byte[] value, long amount) { long amo = amount; int sign = 1; if (amount < 0) { amo = -amount; sign = -1; } for (int i = 0; i < value.length; i++) { int cur = ((int)amo % 256) * sign; amo = (amo >> 8); int val = value[value.length - i - 1] & 0x0ff; int total = val + cur; if (total > 255) { amo += sign; total %= 256; } else if (total < 0) { amo -= sign; } value[value.length - i - 1] = (byte)total; if (amo == 0) { return value; } } return value; } /* increment/deincrement for negative value */ private static byte[] binaryIncrementNeg(byte[] value, long amount) { long amo = amount; int sign = 1; if (amount < 0) { amo = -amount; sign = -1; } for (int i = 0; i < value.length; i++) { int cur = ((int)amo % 256) * sign; amo = (amo >> 8); int val = ((~value[value.length - i - 1]) & 0x0ff) + 1; int total = cur - val; if (total >= 0) { amo += sign; } else if (total < -256) { amo -= sign; total %= 256; } value[value.length - i - 1] = (byte)total; if (amo == 0) { return value; } } return value; } /** * Writes a string as a fixed-size field, padded with zeros. */ public static void writeStringFixedSize(final DataOutput out, String s, int size) throws IOException { byte[] b = toBytes(s); if (b.length > size) { throw new IOException("Trying to write " + b.length + " bytes (" + toStringBinary(b) + ") into a field of length " + size); } out.writeBytes(s); for (int i = 0; i < size - s.length(); ++i) { out.writeByte(0); } } /** * Reads a fixed-size field and interprets it as a string padded with zeros. */ public static String readStringFixedSize(final DataInput in, int size) throws IOException { byte[] b = new byte[size]; in.readFully(b); int n = b.length; while (n > 0 && b[n - 1] == 0) { --n; } return toString(b, 0, n); } /** * Search sorted array "a" for byte "key". I can't remember if I wrote this or copied it from somewhere. (mcorgan) * * @param a Array to search. Entries must be sorted and unique. * @param fromIndex First index inclusive of "a" to include in the search. * @param toIndex Last index exclusive of "a" to include in the search. * @param key The byte to search for. * @return The index of key if found. If not found, return -(index + 1), where negative indicates "not found" and * the "index + 1" handles the "-0" case. */ public static int unsignedBinarySearch(byte[] a, int fromIndex, int toIndex, byte key) { int unsignedKey = key & 0xff; int low = fromIndex; int high = toIndex - 1; while (low <= high) { int mid = (low + high) >>> 1; int midVal = a[mid] & 0xff; if (midVal < unsignedKey) { low = mid + 1; } else if (midVal > unsignedKey) { high = mid - 1; } else { return mid; // key found } } return -(low + 1); // key not found. } /** * Treat the byte[] as an unsigned series of bytes, most significant bits first. Start by adding 1 to the rightmost * bit/byte and carry over all overflows to the more significant bits/bytes. * * @param input The byte[] to increment. * @return The incremented copy of "in". May be same length or 1 byte longer. */ public static byte[] unsignedCopyAndIncrement(final byte[] input) { byte[] copy = copy(input); if (copy == null) { throw new IllegalArgumentException("cannot increment null array"); } for (int i = copy.length - 1; i >= 0; --i) { if (copy[i] == -1) {// -1 is all 1-bits, which is the unsigned maximum copy[i] = 0; } else { ++copy[i]; return copy; } } // we maxed out the array byte[] out = new byte[copy.length + 1]; out[0] = 1; System.arraycopy(copy, 0, out, 1, copy.length); return out; } /** * Copy the byte array given in parameter and return an instance of a new byte array with the same length and the * same content. * * @param bytes the byte array to duplicate * @return a copy of the given byte array */ public static byte[] copy(byte[] bytes) { if (bytes == null) { return null; } byte[] result = new byte[bytes.length]; System.arraycopy(bytes, 0, result, 0, bytes.length); return result; } public static List getUtf8ByteArrays(List strings) { List byteArrays = Lists.newArrayListWithCapacity(CollectionUtils.nullSafeSize(strings)); for (String s : IterableUtils.nullSafe(strings)) { byteArrays.add(Bytes.toBytes(s)); } return byteArrays; } public static boolean isSorted(Collection arrays) { byte[] previous = new byte[0]; for (byte[] array : IterableUtils.nullSafe(arrays)) { if (Bytes.compareTo(previous, array) > 0) { return false; } previous = array; } return true; } public static boolean equals(List a, List b) { if (a == null) { if (b == null) { return true; } return false; } if (b == null) { return false; } if (a.size() != b.size()) { return false; } for (int i = 0; i < a.size(); ++i) { if (!Bytes.equals(a.get(i), b.get(i))) { return false; } } return true; } /** * @param b Array of presumed UTF-8 encoded byte array. * @return a collection of String made from b */ public static Collection toStringCollection(final byte[][] b) { if (b == null) { return null; } return Collections2.transform(Arrays.asList(b), new Function() { @Override public String apply(byte[] a) { return Bytes.toString(a); } }); } /** * Create a max byte array with the specified max byte count * * @param maxByteCount the length of returned byte array * @return the created max byte array */ public static byte[] createMaxByteArray(int maxByteCount) { byte[] maxByteArray = new byte[maxByteCount]; for (int i = 0; i < maxByteArray.length; i++) { maxByteArray[i] = (byte)0xff; } return maxByteArray; } /** * Create a byte array which is multiple given bytes * * @param srcBytes * @param multiNum * @return byte array */ public static byte[] multiple(byte[] srcBytes, int multiNum) { if (multiNum <= 0) { return new byte[0]; } byte[] result = new byte[srcBytes.length * multiNum]; for (int i = 0; i < multiNum; i++) { System.arraycopy(srcBytes, 0, result, i * srcBytes.length, srcBytes.length); } return result; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/util/CollectionUtils.java ================================================ /** * Copyright The Apache Software Foundation * * 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.alibaba.tac.engine.util; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; /** * Utility methods for dealing with Collections, including treating null * collections as empty. */ public class CollectionUtils { private static final List EMPTY_LIST = Collections .unmodifiableList(new ArrayList(0)); @SuppressWarnings("unchecked") public static Collection nullSafe(Collection in) { if (in == null) { return (Collection) EMPTY_LIST; } return in; } /************************ size ************************************/ public static int nullSafeSize(Collection collection) { if (collection == null) { return 0; } return collection.size(); } public static boolean nullSafeSameSize(Collection a, Collection b) { return nullSafeSize(a) == nullSafeSize(b); } /*************************** empty ****************************************/ public static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } public static boolean notEmpty(Collection collection) { return !isEmpty(collection); } /************************ first/last **************************/ public static T getFirst(Collection collection) { if (CollectionUtils.isEmpty(collection)) { return null; } for (T t : collection) { return t; } return null; } /** * @param list any list * @return -1 if list is empty, otherwise the max index */ public static int getLastIndex(List list) { if (isEmpty(list)) { return -1; } return list.size() - 1; } /** * @param list * @param index the index in question * @return true if it is the last index or if list is empty and -1 is passed * for the index param */ public static boolean isLastIndex(List list, int index) { return index == getLastIndex(list); } public static T getLast(List list) { if (isEmpty(list)) { return null; } return list.get(list.size() - 1); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/util/HConstants.java ================================================ /** * Copyright 2010 The Apache Software Foundation * * 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.alibaba.tac.engine.util; /** * HConstants holds a bunch of HBase-related constants */ public final class HConstants { /** * An empty instance. */ public static final byte [] EMPTY_BYTE_ARRAY = new byte [0]; /** When we encode strings, we always specify UTF8 encoding */ public static final String UTF8_ENCODING = "UTF-8"; private HConstants() { // Can't be instantiated with this ctor. } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/util/IterableUtils.java ================================================ /** * Copyright The Apache Software Foundation * * 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.alibaba.tac.engine.util; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Utility methods for Iterable including null-safe handlers. */ public class IterableUtils { private static final List EMPTY_LIST = Collections .unmodifiableList(new ArrayList(0)); @SuppressWarnings("unchecked") public static Iterable nullSafe(Iterable in) { if (in == null) { return (List) EMPTY_LIST; } return in; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/util/TacCompressUtils.java ================================================ package com.alibaba.tac.engine.util; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.function.Consumer; import java.util.stream.Stream; import java.util.zip.CRC32; import java.util.zip.CheckedOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class TacCompressUtils { public static final String EXT = ".zip"; private static final String BASE_DIR = ""; // 符号"/"用来作为目录标识判断符 private static final String PATH = "/"; private static final int BUFFER = 1024; /** * compress * * @param srcFile * @param destFile * @throws Exception */ public static void compress(File srcFile, File destFile) throws Exception { // CRC32 check CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream( destFile), new CRC32()); ZipOutputStream zos = new ZipOutputStream(cos); zos.setComment(new String("comment")); compress(srcFile, zos, BASE_DIR); zos.flush(); zos.close(); } /** * compress * * @param srcFile * @param destPath * @throws Exception */ public static void compress(File srcFile, String destPath) throws Exception { compress(srcFile, new File(destPath)); } /** * compress * * @param srcFile * @param zos ZipOutputStream * @param basePath * @throws Exception */ private static void compress(File srcFile, ZipOutputStream zos, String basePath) throws Exception { if (srcFile.isDirectory()) { compressDir(srcFile, zos, basePath); } else { compressFile(srcFile, zos, basePath); } } /** * compress * * @param srcPath * @param destPath */ public static void compress(String srcPath, String destPath) throws Exception { File srcFile = new File(srcPath); compress(srcFile, destPath); } /** * compressDir * * @param dir * @param zos outPutStream * @param basePath * @throws Exception */ private static void compressDir(File dir, ZipOutputStream zos, String basePath) throws Exception { File[] files = dir.listFiles(); // create empty directory if (files.length < 1) { ZipEntry entry = new ZipEntry(basePath + dir.getName() + PATH); zos.putNextEntry(entry); zos.closeEntry(); } for (File file : files) { compress(file, zos, basePath + dir.getName() + PATH); } } /** * compress file * * @param file * @param zos ZipOutputStream * @param dir * @throws Exception */ private static void compressFile(File file, ZipOutputStream zos, String dir) throws Exception { ZipEntry entry = new ZipEntry(dir + file.getName()); zos.putNextEntry(entry); BufferedInputStream bis = new BufferedInputStream(new FileInputStream( file)); int count; byte data[] = new byte[BUFFER]; while ((count = bis.read(data, 0, BUFFER)) != -1) { zos.write(data, 0, count); } bis.close(); zos.closeEntry(); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/util/TacLogUtils.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** * */ package com.alibaba.tac.engine.util; import org.slf4j.Logger; /** * */ public class TacLogUtils { /** * * * @param logger * @param format * @param arguments */ public static void infoRate(Logger logger, String format, Object... arguments) { logger.info(format, arguments); } /** * * * @param logger * @param format * @param arguments */ public static void warnRate(Logger logger, String format, Object... arguments) { logger.warn(format, arguments); } /** * * * @param logger * @param format * @param arguments */ public static void traceRate(Logger logger, String format, Object... arguments) { logger.trace(format, arguments); } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/util/ThreadPoolUtils.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.util; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * @author jinshuan.li on 2018/8/19 15:47. */ public class ThreadPoolUtils { /** * create thrad pool * @param nThreads * @param threadName * @return */ public static ExecutorService createThreadPool(int nThreads, String threadName) { ExecutorService executorService = new ThreadPoolExecutor(nThreads, 300, 5, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new ThreadFactory() { private AtomicInteger count = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { return new Thread(r, threadName + "_" + count.incrementAndGet()); } }); return executorService; } } ================================================ FILE: tac-engine/src/main/java/com/alibaba/tac/engine/util/ThreadUtils.java ================================================ /* * MIT License * * Copyright (c) 2016 Alibaba Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.alibaba.tac.engine.util; import org.apache.commons.collections.CollectionUtils; import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; /** * @author jinshuan.li */ public class ThreadUtils { public static ExecutorService executorService = ThreadPoolUtils.createThreadPool(30, "tac_batch_process"); /** * execute tasks and wait all complete * * @param tasks * @throws InterruptedException * @throws ExecutionException */ public static List runWaitCompleteTask(List> tasks, Long timeout, TimeUnit timeUnit) throws Exception { if (CollectionUtils.isEmpty(tasks)) { return new ArrayList(); } CompletionService completionService = new ExecutorCompletionService(executorService); for (Callable runnable : tasks) { completionService.submit(runnable); } Future resultFuture = null; List result = new ArrayList(); for (int i = 0; i < tasks.size(); i++) { resultFuture = completionService.take(); result.add(resultFuture.get(timeout, timeUnit)); } return result; } public static Future runAsync(Callable task) { return executorService.submit(task); } public static Future runAsync(ExecutorService executor, Callable task) { return executor.submit(task); } public static void sleep(long time) { try { Thread.sleep(time); } catch (InterruptedException e) { } } } ================================================ FILE: tac-engine/src/main/resources/META-INF/spring.factories ================================================ # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.alibaba.tac.engine.autoconfigure.TacAutoConfiguration ================================================ FILE: tac-engine/src/main/resources/tac/default-logback-spring.xml ================================================ ${LOG_FILE} ${FILE_LOG_PATTERN} ${LOG_FILE}.%d{yyyy-MM-dd} 60 ${TAC_USER_LOG_FILE} %m%n ${TAC_USER_LOG_FILE}.%d{yyyy-MM-dd} 60 ${CONSOLE_LOG_PATTERN} utf8 ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/common/redis/RedisSequenceCounterTest.java ================================================ package com.alibaba.tac.engine.common.redis; import com.alibaba.tac.engine.test.TacEnginTest; import org.junit.Before; import org.junit.Test; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.annotation.Resource; import static org.junit.Assert.*; /** * @author jinshuan.li 01/03/2018 11:01 */ public class RedisSequenceCounterTest extends TacEnginTest { @Resource RedisSequenceCounter redisSequenceCounter; @Before public void setUp() throws Exception { } @Test public void set() { redisSequenceCounter.set(10); Long data = redisSequenceCounter.get(); assertTrue(data.equals(10L)); } @Test public void get() { } @Test public void incrementAndGet() { long data = redisSequenceCounter.incrementAndGet(); Long data2 = redisSequenceCounter.get(); //assertTrue(data==11); } @Test public void increBy() { } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/inst/service/redis/RedisMsInstFileServiceTest.java ================================================ package com.alibaba.tac.engine.inst.service.redis; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.service.DevMsInstFileService; import com.alibaba.tac.engine.test.TacEnginTest; import org.junit.Before; import org.junit.Test; import javax.annotation.Resource; import static org.junit.Assert.*; /** * @author jinshuan.li 01/03/2018 13:38 */ public class RedisMsInstFileServiceTest extends TacEnginTest { @Resource private RedisMsInstFileService redisMsInstFileService; @Resource private DevMsInstFileService devMsInstFileService; private TacInst tacInst; @Before public void setUp() throws Exception { tacInst = new TacInst(); tacInst.setJarVersion("abcccc"); tacInst.setId(111); tacInst.setMsCode("abc"); tacInst.setName("abc-test"); } @Test public void getInstanceFile() { byte[] instanceFile = redisMsInstFileService.getInstanceFile(tacInst.getId()); assertNotNull(instanceFile); } @Test public void saveInstanceFile() { Boolean aBoolean = redisMsInstFileService.saveInstanceFile(tacInst, devMsInstFileService.getInstanceFile(tacInst.getMsCode())); assertTrue(aBoolean); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/inst/service/redis/RedisMsInstServiceTest.java ================================================ package com.alibaba.tac.engine.inst.service.redis; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.test.TacEnginTest; import org.junit.Before; import org.junit.Test; import javax.annotation.Resource; import java.util.List; import static org.junit.Assert.*; /** * @author jinshuan.li 01/03/2018 14:42 */ public class RedisMsInstServiceTest extends TacEnginTest { @Resource private RedisMsInstService redisMsInstService; TacInst tacInst = new TacInst(); @Before public void before() { tacInst.setJarVersion("xxxxx"); tacInst.setMsCode("abc"); tacInst.setName("test"); } @Test public void getAllTacMsInsts() { List allTacMsInsts = redisMsInstService.getAllTacMsInsts(); assertNotNull(allTacMsInsts); } @Test public void getTacMsInst() { TacInst tacMsInst = redisMsInstService.getTacMsInst(1L); assertNotNull(tacMsInst); } @Test public void createTacMsInst() { TacInst tacMsInst = redisMsInstService.createTacMsInst(tacInst); assertTrue(tacMsInst.getId() > 0); } @Test public void removeMsInst() { Boolean success = redisMsInstService.removeMsInst(1L); assertTrue(success); TacInst tacMsInst = redisMsInstService.getTacMsInst(1L); assertNull(tacMsInst); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/ms/service/redis/RedisMsPublisherTest.java ================================================ package com.alibaba.tac.engine.ms.service.redis; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.service.DevMsInstFileService; import com.alibaba.tac.engine.inst.service.IMsInstService; import com.alibaba.tac.engine.test.TacEnginTest; import org.junit.Test; import javax.annotation.Resource; import java.util.concurrent.CountDownLatch; import static org.junit.Assert.*; /** * @author jinshuan.li 01/03/2018 16:22 */ public class RedisMsPublisherTest extends TacEnginTest { @Resource RedisMsPublisher redisMsPublisher; @Resource IMsInstService iMsInstService; @Resource private DevMsInstFileService devMsInstFileService; CountDownLatch countDownLatch = new CountDownLatch(1); @Test public void publish() throws InterruptedException { TacInst abc = iMsInstService.getTacMsInst(1L); byte[] abcs = devMsInstFileService.getInstanceFile("abc"); Boolean publish = redisMsPublisher.publish(abc, abcs); assertTrue(publish); countDownLatch.await(); } @Test public void offline() throws InterruptedException { TacInst abc = iMsInstService.getTacMsInst(1L); Boolean offline = redisMsPublisher.offline(abc); assertTrue(offline); countDownLatch.await(); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/ms/service/redis/RedisMsServiceTest.java ================================================ package com.alibaba.tac.engine.ms.service.redis; import com.alibaba.tac.engine.ms.domain.TacMsDO; import com.alibaba.tac.engine.ms.domain.TacMsStatus; import com.alibaba.tac.engine.test.TacEnginTest; import org.apache.commons.collections4.CollectionUtils; import org.junit.Before; import org.junit.Test; import javax.annotation.Resource; import java.util.List; import static org.junit.Assert.*; /** * @author jinshuan.li 01/03/2018 14:53 */ public class RedisMsServiceTest extends TacEnginTest { @Resource private RedisMsService redisMsService; TacMsDO tacMsDO = new TacMsDO(); @Before public void setUp() throws Exception { tacMsDO.setCode("test"); tacMsDO.setName("ljinshuan"); } @Test public void createMs() { redisMsService.createMs(tacMsDO); TacMsDO ms = redisMsService.getMs(tacMsDO.getCode()); assertNotNull(ms); } @Test public void removeMs() { redisMsService.removeMs(tacMsDO.getCode()); TacMsDO ms = redisMsService.getMs(tacMsDO.getCode()); assertNull(ms); } @Test public void invalidMs() { redisMsService.invalidMs(tacMsDO.getCode()); TacMsDO ms = redisMsService.getMs(tacMsDO.getCode()); assertTrue(ms == null || ms.getStatus().equals(TacMsStatus.INVALID.code())); } @Test public void updateMs() { tacMsDO.setName("updated"); redisMsService.updateMs(tacMsDO.getCode(), tacMsDO); TacMsDO ms = redisMsService.getMs(tacMsDO.getCode()); assertEquals(ms.getName(), "updated"); } @Test public void getMs() { TacMsDO ms = redisMsService.getMs(tacMsDO.getCode()); assertNull(ms); } @Test public void getAllMs() { List allMs = redisMsService.getAllMs(); assertTrue(CollectionUtils.isNotEmpty(allMs)); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/service/GitLabFeatureTest.java ================================================ package com.alibaba.tac.engine.service; import com.alibaba.tac.engine.code.CodeCompileService; import com.alibaba.tac.engine.git.GitRepoService; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.service.IMsInstService; import com.alibaba.tac.engine.ms.domain.TacMsDO; import com.alibaba.tac.engine.ms.service.IMsPublisher; import com.alibaba.tac.engine.ms.service.IMsService; import com.alibaba.tac.engine.test.TacEnginTest; import com.alibaba.tac.sdk.error.ServiceException; import org.junit.Assert; import org.junit.Test; import javax.annotation.Resource; import java.io.IOException; import java.util.List; import static org.junit.Assert.*; /** * @author jinshuan.li 08/05/2018 10:32 */ public class GitLabFeatureTest extends TacEnginTest { @Resource private IMsService msService; @Resource private IMsInstService msInstService; @Resource private GitRepoService gitRepoService; @Resource private CodeCompileService codeCompileService; @Resource private IMsPublisher msPublisher; private final String code = "gitlab-test"; private final String defaultBranch = "master"; @Test public void createMs() { TacMsDO tacMsDO = new TacMsDO(); tacMsDO.setCode("gitlab-test"); tacMsDO.setName("gitlab测试"); tacMsDO.setGitSupport(true); tacMsDO.setGitRepo("git@127.0.0.1:tac-admin/tac-test01.git"); TacMsDO ms = msService.createMs(tacMsDO); assertNotNull(ms); } @Test public void getMs() { TacMsDO ms = msService.getMs(code); assertNotNull(ms); } @Test public void createMsInst() { TacInst gitTacMsInst = msInstService.createGitTacMsInst(code, code, defaultBranch); assertNotNull(gitTacMsInst); List msInsts = msInstService.getMsInsts(code); assertNotNull(msInsts); } @Test public void prePublishTest() throws ServiceException, IOException { TacMsDO ms = msService.getMs(code); List allTacMsInsts = msInstService.getMsInsts(code); TacInst tacInst = allTacMsInsts.get(0); String sourcePath = gitRepoService.pullInstanceCode(ms.getGitRepo(), ms.getCode(), tacInst.getGitBranch()); codeCompileService.compile(tacInst.getId(), sourcePath); byte[] jarFile = codeCompileService.getJarFile(tacInst.getId()); msPublisher.prePublish(tacInst, jarFile); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/service/JdkCompilerTest.java ================================================ package com.alibaba.tac.engine.service; import com.alibaba.tac.engine.code.CodeCompileService; import com.alibaba.tac.engine.test.TacEnginTest; import com.alibaba.tac.sdk.common.TacResult; import com.google.common.collect.Maps; import org.junit.Before; import org.junit.Test; import javax.annotation.Resource; import java.io.File; import java.io.IOException; import static org.junit.Assert.assertNotNull; /** * @author jinshuan.li 12/02/2018 08:49 */ public class JdkCompilerTest extends TacEnginTest { @Resource private CodeCompileService codeCompileService; @Resource private TacInstRunService tacInstService; private String src = "/Users/jinshuan.li/Source/open-tac/tac-dev-source"; private String msCode = "abcd"; @Before public void beforeTest() throws Exception { } @Test public void testCompiler() throws Exception { codeCompileService.compile(msCode, src); } @Test public void testPackage() throws IOException { byte[] jarFile = codeCompileService.getJarFile(msCode); assertNotNull(jarFile); } @Test public void runCodeTest() throws Exception { TacResult tacResult = tacInstService.runWithLoad("msCode", 1024L, Maps.newHashMap()); System.out.println(tacResult); } String getCurrentPath() { File file = new File(""); return file.getAbsolutePath(); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/service/TacInstanceLoadServiceTest.java ================================================ package com.alibaba.tac.engine.service; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.inst.domain.TacInstanceInfo; import com.alibaba.tac.engine.inst.service.IMsInstService; import com.alibaba.tac.engine.test.TestApplication; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; import static org.junit.Assert.*; /** * @author jinshuan.li 12/02/2018 18:24 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = TestApplication.class) public class TacInstanceLoadServiceTest { @Resource private TacInstanceLoadService tacInstanceLoadService; @Resource private IMsInstService iMsInstService; @Test public void loadTacHandler() throws Exception { TacInst tacMsInst = iMsInstService.getTacMsInst(111L); TacInstanceInfo tacInstanceInfo = tacInstanceLoadService.loadTacHandler(tacMsInst); assertNotNull(tacInstanceInfo); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/test/TacEnginTest.java ================================================ package com.alibaba.tac.engine.test; import com.alibaba.tac.engine.code.CodeLoadService; import lombok.extern.slf4j.Slf4j; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * @author jinshuan.li 26/02/2018 11:36 */ @Slf4j @RunWith(SpringRunner.class) @SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) public class TacEnginTest { static { try { ClassLoader classLoader = CodeLoadService.changeClassLoader(); } catch (Exception e) { log.error(e.getMessage(), e); } } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/test/TestApplication.java ================================================ package com.alibaba.tac.engine.test; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySources; /** * @author jinshuan.li 12/02/2018 09:26 */ @SpringBootApplication( scanBasePackages = {"com.alibaba.tac.engine", "com.alibaba.tac.infrastracture", "com.alibaba.tac.sdk","com.tmall.tac.test"}) @PropertySources({@PropertySource("classpath:test.properties")}) public class TestApplication { } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/engine/utils/TacFileUtilTest.java ================================================ package com.alibaba.tac.engine.utils; import com.alibaba.tac.engine.test.TacEnginTest; import com.alibaba.tac.engine.code.TacFileService; import org.junit.Test; import javax.annotation.Resource; /** * @author jinshuan.li 27/02/2018 20:29 */ public class TacFileUtilTest extends TacEnginTest { @Resource private TacFileService tacFileService; @Test public void getClassFileOutputPath() { String classFileOutputPath = tacFileService.getClassFileOutputPath(1L); System.out.println(classFileOutputPath); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/test/http/HttpClientTest.java ================================================ package com.alibaba.tac.test.http; import com.alibaba.fastjson.JSONObject; import com.alibaba.tac.sdk.common.TacResult; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.ListenableFuture; import org.asynchttpclient.Response; import org.asynchttpclient.util.HttpConstants; import org.junit.Test; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static org.asynchttpclient.Dsl.asyncHttpClient; /** * @author jinshuan.li 07/03/2018 15:41 */ public class HttpClientTest { @Test public void test() throws InterruptedException, ExecutionException, TimeoutException { JSONObject data = new JSONObject(); data.put("name", "ljinshuan"); AsyncHttpClient asyncHttpClient = asyncHttpClient(); ListenableFuture execute = asyncHttpClient.preparePost("http://localhost:8001/api/tac/execute/shuan") .addHeader("Content-Type", "application/json;charset=UTF-8").setBody(data.toJSONString()).execute(); Response response = execute.get(10, TimeUnit.SECONDS); if (response.getStatusCode() == HttpConstants.ResponseStatusCodes.OK_200) { TacResult tacResult = JSONObject.parseObject(response.getResponseBody(), TacResult.class); System.out.println(tacResult); } System.out.println(response); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/test/redis/RedisConfig.java ================================================ package com.alibaba.tac.test.redis; import com.alibaba.tac.engine.common.redis.RedisSequenceCounter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import static com.alibaba.tac.engine.service.RedisBeansConfig.getCounterRedisTemplate; /** * @author jinshuan.li 28/02/2018 19:52 */ @Configuration("testRedisConfig") public class RedisConfig { @Bean public RedisMessageListenerContainer redisMessageListenerContainer(JedisConnectionFactory jedisConnectionFactory, MessageListenerAdapter listenerAdapter) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(jedisConnectionFactory); container.addMessageListener(listenerAdapter, new ChannelTopic("topicA")); // 设置线程池 //container.setTaskExecutor(null); return container; } @Bean public MessageListenerAdapter listenerAdapter(TacRedisMessageListener messageListener) { MessageListenerAdapter adapter = new MessageListenerAdapter(messageListener, "receiveMessage"); return adapter; } @Bean(name = "counterRedisTemplate") public RedisTemplate counterRedisTemplate(JedisConnectionFactory jedisConnectionFactory) { return getCounterRedisTemplate(jedisConnectionFactory); } @Bean public RedisSequenceCounter redisSequenceCounter() { return new RedisSequenceCounter("redis.counter"); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/test/redis/StringDataRedisTest.java ================================================ package com.alibaba.tac.test.redis; import com.alibaba.tac.engine.inst.domain.TacInst; import com.alibaba.tac.engine.util.Bytes; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; import java.io.Serializable; import java.util.concurrent.CountDownLatch; /** * @author jinshuan.li 28/02/2018 19:44 */ @SpringBootApplication(scanBasePackages = {"com.alibaba.tac.engine.redis"}) @RunWith(SpringRunner.class) @SpringBootTest(classes = StringDataRedisTest.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) public class StringDataRedisTest { @Autowired private RedisTemplate template; @Resource(name = "redisTemplate") private ValueOperations valueOperations; @Resource(name = "redisTemplate") private ValueOperations valueOperations2; @Resource(name = "counterRedisTemplate") private RedisTemplate counterRedisTemplate; private CountDownLatch countDownLatch = new CountDownLatch(1); @Test public void test() { TacInst tacInst = new TacInst(); tacInst.setJarVersion("xxx"); TacInst tacInst1 = saveTest("name", tacInst); System.out.println(tacInst1); } @Test public void sendMessage() { template.convertAndSend("topicA", "hello world"); System.out.println("xxx"); } @Test public void sendMessage2() throws InterruptedException { while (true) { template.convertAndSend("topicA", "hello world" + System.currentTimeMillis()); Thread.sleep(1000L); } } @Test public void testSub1() throws InterruptedException { System.out.println("testSub1"); countDownLatch.await(); } @Test public void testSub2() throws InterruptedException { System.out.println("testSub2"); countDownLatch.await(); } public T saveTest(String key, T value) { valueOperations.set(key, value); T data = (T)valueOperations.get(key); return data; } @Test public void testIncre() { RedisConnection connection = template.getConnectionFactory().getConnection(); byte[] key = Bytes.toBytes("ljinshuan123"); Long incr = connection.incr(key); byte[] bytes = connection.get(key); connection.set(key, bytes); connection.incr(key); bytes = connection.get(key); } @Test public void testIncre2() { byte[] bytes = Bytes.toBytes("2"); long a = 52; byte b = (byte)a; } @Test public void testIncre3() { ValueOperations valueOperations = counterRedisTemplate.opsForValue(); String key = "ahaha"; String s = valueOperations.get(key); valueOperations.set(key, "1"); Long increment = valueOperations.increment(key, 2L); valueOperations.get(key); System.out.println(s); } } ================================================ FILE: tac-engine/src/test/java/com/alibaba/tac/test/redis/TacRedisMessageListener.java ================================================ package com.alibaba.tac.test.redis; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * @author jinshuan.li 28/02/2018 20:24 */ @Slf4j @Service public class TacRedisMessageListener { /** * 接收消息 * * @param message * @param channel */ public void receiveMessage(String message, String channel) { log.info(message); } } ================================================ FILE: tac-engine/src/test/resources/test.properties ================================================ tac.default.store=redis ================================================ FILE: tac-engine/src/test/source/test1/com/alibaba/tac/biz/processor/HelloTac.java ================================================ package com.alibaba.tac.biz.processor; import com.alibaba.tac.sdk.common.TacResult; import com.alibaba.tac.sdk.domain.Context; import com.alibaba.tac.sdk.handler.TacHandler; /** * @author jinshuan.li 12/02/2018 08:47 */ public class HelloTac implements TacHandler { @Override public TacResult execute(Context context) throws Exception { System.out.println(context); System.out.println("hello tac"); return TacResult.newResult(1); } } ================================================ FILE: tac-infrastructure/pom.xml ================================================ tac com.alibaba 0.0.4 4.0.0 tac-infrastructure jar tac-infrastructure http://maven.apache.org UTF-8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-cache org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-logging ch.qos.logback logback-classic org.aspectj aspectjweaver org.apache.zookeeper zookeeper com.alibaba tac-sdk org.asynchttpclient async-http-client org.gitlab4j gitlab4j-api org.eclipse.jgit org.eclipse.jgit org.apache.maven.plugins maven-compiler-plugin ${jdk.version} ${jdk.version} UTF-8 org.apache.maven.plugins maven-resources-plugin UTF-8 ================================================ FILE: tac-infrastructure/src/main/java/com/alibaba/tac/infrastracture/logger/TacLogConsts.java ================================================ package com.alibaba.tac.infrastracture.logger; /** * Created by huchangkun on 2018/1/29. */ public final class TacLogConsts { /** * the user log tag */ public static String TAC_USER_LOG = "TAC-USER-LOG"; /** * TT_LOG */ public static String TAC_BIZ_TT_LOG = "TAC-BIZ-TT-LOG"; /** * executor log */ public static String TAC_EXECUTOR_LOG = "TAC-EXECUTOR-LOG"; } ================================================ FILE: tac-infrastructure/src/main/java/com/alibaba/tac/infrastracture/logger/TacLoggerImpl.java ================================================ package com.alibaba.tac.infrastracture.logger; import com.alibaba.tac.sdk.common.TacContants; import com.alibaba.tac.sdk.common.TacThreadLocals; import com.alibaba.tac.sdk.infrastracture.TacLogger; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.io.PrintWriter; import java.io.StringWriter; import java.net.Inet4Address; import java.net.UnknownHostException; import java.util.Map; import java.util.Random; import java.util.concurrent.locks.ReentrantLock; /** * the logger service */ @Service public class TacLoggerImpl implements TacLogger { protected static final Logger SYS_LOGGER = LoggerFactory.getLogger(TacLoggerImpl.class); protected static final Logger LOGGER = LoggerFactory.getLogger(TacLogConsts.TAC_USER_LOG); protected static final Logger TT_LOGGER = LoggerFactory.getLogger(TacLogConsts.TAC_BIZ_TT_LOG); private final String lineSeparator = System.getProperty("line.separator"); private final String replacement = "##"; private static String hostIp; private static String hostHost; /** * the console log lock */ private ReentrantLock consoleLogLock = new ReentrantLock(); /** * default log rate */ private volatile float logOutputRate = 1; @Override public void debug(String log) { appendToStringBuilder(log); log = StringUtils.replace(log, lineSeparator, replacement); if (LOGGER.isDebugEnabled()) { LOGGER.debug("^" + hostIp + "^" + log); } } @Override public void info(String log) { appendToStringBuilder(log); log = StringUtils.replace(log, lineSeparator, replacement); if (LOGGER.isInfoEnabled()) { LOGGER.info("^" + hostIp + "^" + log); } } @Override public void rateInfo(String log) { if (new Random().nextInt(100) < (logOutputRate * 100)) { appendToStringBuilder(log); log = StringUtils.replace(log, lineSeparator, replacement); if (LOGGER.isInfoEnabled()) { LOGGER.info("^" + hostIp + "^" + log); } } } @Override public void warn(String log) { appendToStringBuilder(log); log = StringUtils.replace(log, lineSeparator, replacement); if (LOGGER.isWarnEnabled()) { LOGGER.warn("^" + hostIp + "^" + log); } } @Override public void rateWarn(String log) { if (new Random().nextInt(100) < (logOutputRate * 100)) { appendToStringBuilder(log); log = StringUtils.replace(log, lineSeparator, replacement); if (LOGGER.isWarnEnabled()) { LOGGER.warn("^" + hostIp + "^" + log); } } } @Override public void error(String log, Throwable t) { log = StringUtils.replace(log, lineSeparator, replacement); StringWriter sw = new StringWriter(); if (t != null) { t.printStackTrace(new PrintWriter(sw)); } appendToStringBuilder(log, t, sw); String errorMsg = new StringBuilder(log).append(replacement) .append(StringUtils.replace(sw.toString(), lineSeparator, replacement)).toString(); if (LOGGER.isErrorEnabled()) { LOGGER.error("^" + hostIp + "^" + errorMsg); } } @Override public void ttLog(String log) { if (TT_LOGGER.isInfoEnabled()) { TT_LOGGER.info("^" + hostIp + "^" + log); } } @Override public String getContent() { StringBuilder content = TacThreadLocals.TAC_LOG_CONTENT.get(); if (content != null) { return content.toString(); } return StringUtils.EMPTY; } @Override public boolean isDebugEnabled() { return LOGGER.isDebugEnabled(); } @Override public boolean isInfoEnabled() { return LOGGER.isInfoEnabled(); } @Override public boolean isWarnEnabled() { return LOGGER.isWarnEnabled(); } @Override public boolean isErrorEnabled() { return LOGGER.isErrorEnabled(); } private void appendToStringBuilder(String log) { if (!isDebugInvoke()) { return; } try { consoleLogLock.lock(); StringBuilder content = TacThreadLocals.TAC_LOG_CONTENT.get(); if (content == null) { content = new StringBuilder(); TacThreadLocals.TAC_LOG_CONTENT.set(content); } content.append(log).append("\n"); } finally { consoleLogLock.unlock(); } } private Boolean isDebugInvoke() { Map params = TacThreadLocals.TAC_PARAMS.get(); if (params == null) { return false; } String flag = String.valueOf(params.get(TacContants.DEBUG)); if (StringUtils.equalsIgnoreCase("true", flag)) { return true; } return false; } private void appendToStringBuilder(String log, Throwable t, StringWriter sw) { if (!isDebugInvoke()) { return; } try { consoleLogLock.lock(); StringBuilder content = TacThreadLocals.TAC_LOG_CONTENT.get(); if (content == null) { content = new StringBuilder(); TacThreadLocals.TAC_LOG_CONTENT.set(content); } content.append(log).append("\n"); content.append(sw.toString()).append("\n"); } finally { consoleLogLock.unlock(); } } static { try { if (hostIp == null) { hostIp = Inet4Address.getLocalHost().getHostAddress(); } if (hostHost == null) { hostHost = Inet4Address.getLocalHost().getHostName(); } } catch (UnknownHostException var1) { var1.printStackTrace(); } } } ================================================ FILE: tac-infrastructure/src/test/java/com/alibaba/tac/AppTest.java ================================================ package com.alibaba.tac; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class AppTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public AppTest( String testName ) { super( testName ); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite( AppTest.class ); } /** * Rigourous Test :-) */ public void testApp() { assertTrue( true ); } } ================================================ FILE: tac-sdk/pom.xml ================================================ tac com.alibaba 0.0.4 ../pom.xml 4.0.0 tac-sdk jar tac-sdk http://maven.apache.org UTF-8 com.google.guava guava org.apache.commons commons-collections4 commons-beanutils commons-beanutils org.apache.commons commons-lang3 junit junit org.springframework.boot spring-boot-starter-test org.springframework.boot spring-boot-configuration-processor org.projectlombok lombok com.alibaba fastjson org.springframework.boot spring-boot-starter-web org.apache.maven.plugins maven-compiler-plugin ${jdk.version} ${jdk.version} UTF-8 org.apache.maven.plugins maven-resources-plugin UTF-8 ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/common/TacContants.java ================================================ package com.alibaba.tac.sdk.common; /** * Created by changkun.hck on 2016/12/20. */ public final class TacContants { public static final String USER_ID = "userId"; public static final String USER_NICK = "userNick"; public static final String PLATFORM = "platform"; public static final String VERSION = "version"; public static final String CHANNEL = "channel"; public static final String USER_AGENT = "userAgent"; public static final String IP = "ip"; public static final String TTID = "ttid"; public static final String UTDID = "utdid"; public static final String APP_NAME = "appName"; public static final String MS_CODE = "msCode"; public static final String DEBUG = "debug"; public static final String IMEI = "imei"; public static final String IMSI = "imsi"; public static final String DEVICE_ID = "deviceId"; public static final String DEVICE_MODE = "deviceMode"; public static final String DEVICE_OS_VERSION = "deviceOsVersion"; public static final String NETWORK_SCHEMA = "networkSchema"; public static final String FROM_CONSOLE="fromConsole"; public static final String HOTPOINT_KEY = "hotPointKey"; } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/common/TacParams.java ================================================ package com.alibaba.tac.sdk.common; import lombok.Data; import org.apache.commons.lang3.StringUtils; import java.io.Serializable; import java.util.HashMap; import java.util.Map; @Data public class TacParams implements Serializable { private static final long serialVersionUID = 6681979519642226666L; private String appName; /** * the service codes , required, split with ',' * */ private String msCodes; /** * is batch execute * */ private boolean isBatch = false; /** * the service params, {@link TacContants} * */ private Map paramMap = new HashMap(); public TacParams(String appName, String msCodes) { this.appName = appName; this.msCodes = msCodes; } public String getParamValue(String paramKey) { if (StringUtils.isEmpty(paramKey)) { return StringUtils.EMPTY; } if (paramMap.get(paramKey) != null) { return String.valueOf(paramMap.get(paramKey)); } else { return StringUtils.EMPTY; } } public void addPara(String key, Object value) { this.paramMap.put(key, value); } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/common/TacResult.java ================================================ package com.alibaba.tac.sdk.common; import lombok.Data; import java.io.Serializable; import java.net.Inet4Address; import java.net.UnknownHostException; /** * @author jinshuan.li 12/02/2018 08:10 */ @Data public class TacResult implements Serializable { private static final long serialVersionUID = 2743787028659568164L; private boolean success = true; /** * the msgCode */ private String msgCode; /** * the msgInfo */ private String msgInfo; /** * the execute data */ public T data; /** * hasMore data */ public Boolean hasMore; private static String staticIp; /** * the tac engine host ip */ private String ip; /** * * * @param data */ public TacResult(T data) { this.data = data; this.ip = staticIp; } /** * * * @param msgCode * @param msgInfo * @param data */ public TacResult(String msgCode, String msgInfo, T data) { this.msgCode = msgCode; this.msgInfo = msgInfo; this.data = data; this.ip = staticIp; } public static final TacResult newResult(T data) { TacResult instance = new TacResult(data); return instance; } public static final TacResult newResult(T data, boolean hasMore) { TacResult instance = new TacResult(data); instance.setHasMore(hasMore); return instance; } /** * * * @param msgCode * @param msgInfo * @return */ public static final TacResult errorResult(String msgCode, String msgInfo) { TacResult instance = new TacResult(null); instance.success = false; instance.msgCode = msgCode; instance.msgInfo = msgInfo; return instance; } /** * * * @param msgCode * @return */ public static final TacResult errorResult(String msgCode) { TacResult instance = new TacResult(null); instance.success = false; instance.msgCode = msgCode; instance.msgInfo = msgCode; return instance; } static { try { if (staticIp == null) { staticIp = Inet4Address.getLocalHost().getHostAddress(); } } catch (UnknownHostException var1) { var1.printStackTrace(); } } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/common/TacThreadLocals.java ================================================ package com.alibaba.tac.sdk.common; import java.util.Map; public class TacThreadLocals { /** * the log data */ public static final ThreadLocal TAC_LOG_CONTENT = new ThreadLocal(); /** * the params data */ public static final ThreadLocal> TAC_PARAMS = new ThreadLocal>(); /** * clear data in threadlocal */ public static void clear() { TAC_LOG_CONTENT.remove(); TAC_PARAMS.remove(); } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/domain/Context.java ================================================ package com.alibaba.tac.sdk.domain; import java.io.Serializable; import java.util.Map; /** * @author jinshuan.li 12/02/2018 */ public interface Context extends Serializable { /** * get the msCode * * @return */ String getMsCode(); /** * get the instanceId * * @return */ long getInstId(); /** * get appName * @return */ String getAppName(); /** * get the value in the params map */ Object get(String key); /** * * @param key * @return */ boolean containsKey(String key); /** * * @return */ Map getParams(); } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/domain/TacRequestContext.java ================================================ package com.alibaba.tac.sdk.domain; import java.util.HashMap; import java.util.Map; public class TacRequestContext implements Context { private String appName; private String msCode; private long instId; @Override public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public void setMsCode(String msCode) { this.msCode = msCode; } public void setInstId(long instId) { this.instId = instId; } public void setParamMap(Map paramMap) { this.paramMap = paramMap; } private Map paramMap = new HashMap(); @Override public String getMsCode() { return msCode; } @Override public long getInstId() { return instId; } @Override public Object get(String key) { return paramMap.get(key); } @Override public boolean containsKey(String key) { return paramMap.containsKey(key); } @Override public Map getParams() { return paramMap; } public Map getParamMap() { return paramMap; } public void putAll(Map paramMap) { if (paramMap != null || !paramMap.isEmpty()) { this.paramMap.putAll(paramMap); } } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/error/ErrorCode.java ================================================ package com.alibaba.tac.sdk.error; public class ErrorCode { /** * ALREADY_EXIST */ public static final int ALREADY_EXIST = -102; /** * NOT_EXIST */ public static final int NOT_EXIST = -103; /** * NO_PERMISSION */ public static final int NO_PERMISSION = -104; /** * NOT_SUPPORTED */ public static final int NOT_SUPPORTED = -105; /** * */ public static final int INCREMENTAL_CATALOGS_CLIENT_DATA_VERSION_ERR = -106; /** * */ public static final int ILLEGAL_ARGUMENT = -110; /** * IO Exception */ public static final int IO_EXCEPTION = -111; /** * 系统异常 */ public static final int SYS_EXCEPTION = -500; } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/error/IError.java ================================================ package com.alibaba.tac.sdk.error; /** * @author jinshuan.li 2017/12/28 下午3:03 */ public interface IError { /** * get error code * @return */ int getCode(); /** * get error message * @return */ String getMessage(); } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/error/ServiceException.java ================================================ package com.alibaba.tac.sdk.error; /** * the service exception class */ public class ServiceException extends Exception { private static final long serialVersionUID = 1701785539191674857L; /** * errorCode */ private int errorCode; /** * errorMessage */ private String message; public ServiceException(int errorCode, String message) { super(message); this.errorCode = errorCode; this.message = message; } public int getErrorCode() { return errorCode; } public void setErrorCode(int errorCode) { this.errorCode = errorCode; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } /** * the helper method which throw an exception * * @param error * @return * @throws ServiceException */ public static ServiceException throwException(IError error) throws ServiceException { throw new ServiceException(error.getCode(), error.getMessage()); } /** * * * @param errorCode * @param errorMessage * @return * @throws ServiceException */ public static ServiceException throwException(Integer errorCode, String errorMessage) throws ServiceException { throw new ServiceException(errorCode, errorMessage); } /** * * * @param error * @param errorMessage * @return * @throws ServiceException */ public static ServiceException throwException(IError error, String errorMessage) throws ServiceException { throw new ServiceException(error.getCode(), errorMessage); } /** * * * @param errorCode * @param errorMessage * @return */ public static ServiceException newException(Integer errorCode, String errorMessage) { return new ServiceException(errorCode, errorMessage); } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/factory/AbstractServiceFactory.java ================================================ package com.alibaba.tac.sdk.factory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * * the factory class which provide tac engine data source */ public abstract class AbstractServiceFactory implements ApplicationContextAware { /** * the Spring contenxt */ public static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext ac) throws BeansException { if (applicationContext != null) { return; } // set the spring context applicationContext = ac; } /** * get spring bean with beanId; * * @param beanId spring bean id * @param * @return */ protected static T getServiceBean(String beanId) { if (applicationContext.getBean(beanId) == null) { throw new IllegalStateException("bean[" + beanId + "] Incorrectly configured!"); } return (T)applicationContext.getBean(beanId); } public static T getServiceBean(Class clazz) { T bean = applicationContext.getBean(clazz); checkNull(bean, clazz); return bean; } private static void checkNull(Object bean, Class clazz) { if (bean == null) { String format = String.format("bean [ %s ] Incorrectly configured!", clazz.getName()); throw new IllegalStateException(format); } } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/factory/TacInfrasFactory.java ================================================ package com.alibaba.tac.sdk.factory; import com.alibaba.tac.sdk.infrastracture.TacLogger; import org.springframework.stereotype.Service; /** * @author jinshuan.li 12/02/2018 19:04 */ @Service public class TacInfrasFactory extends AbstractServiceFactory { /** * The logger Service * * @return */ public static TacLogger getLogger() { return getServiceBean(TacLogger.class); } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/handler/DisposableHandler.java ================================================ package com.alibaba.tac.sdk.handler; /** * TacHandler destroy * Created by huchangkun on 2017/5/23. */ public interface DisposableHandler { void destroy() throws Exception; } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/handler/InitializingHandler.java ================================================ package com.alibaba.tac.sdk.handler; /** * TacHandler init * Created by huchangkun on 2017/5/23. */ public interface InitializingHandler { void afterPropertiesSet() throws Exception; } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/handler/TacHandler.java ================================================ package com.alibaba.tac.sdk.handler; import com.alibaba.tac.sdk.common.TacResult; import com.alibaba.tac.sdk.domain.Context; /** * the core interface in tac ,all the biz code should implements it * * @param */ public interface TacHandler { /** * execute the biz code * @param context * @return * @throws Exception */ TacResult execute(Context context) throws Exception; } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/infrastracture/TacLogger.java ================================================ package com.alibaba.tac.sdk.infrastracture; /** * tac log * */ public interface TacLogger { public void debug(String log); public void info(String log); public void rateInfo(String log); public void warn(String log); public void rateWarn(String log); public void error(String log, Throwable t); public void ttLog(String log); public String getContent(); boolean isDebugEnabled(); boolean isInfoEnabled(); boolean isWarnEnabled(); boolean isErrorEnabled(); } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/Cell.java ================================================ package com.alibaba.tac.sdk.tangram4tac; import com.alibaba.tac.sdk.tangram4tac.render.DefaultRender; import com.alibaba.tac.sdk.tangram4tac.render.IRender; /** * Created by longerian on 2017/11/5. */ public abstract class Cell { protected String id; protected final String type; protected T style; protected String reuseId; @FieldExcluder protected IRender mRender; public Cell() { this.type = getType(); this.mRender = new DefaultRender(); } public void setId(String id) { this.id = id; } public void setReuseId(String reuseId) { this.reuseId = reuseId; } public String getId() { return id; } public void setStyle(T style) { this.style = style; } public T getStyle() { return style; } public String getReuseId() { return reuseId; } public void setRender(IRender mRender) { this.mRender = mRender; } public abstract String getType(); public Object render() { if (mRender != null) { return mRender.renderTo(this); } return null; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/Container.java ================================================ package com.alibaba.tac.sdk.tangram4tac; import java.util.ArrayList; import java.util.List; /** * Created by longerian on 2017/11/5. */ public abstract class Container extends Cell { protected Cell header; protected Cell footer; protected List items; protected String load; /** * businessType 是提供给H5的兼容参数,对Native无意义 */ protected String businessType; protected int loadType; public Container() { super(); items = new ArrayList(); } public String getBusinessType() { return businessType; } public void setBusinessType(String businessType) { this.businessType = businessType; } public Cell getHeader() { return header; } public void setHeader(Cell header) { this.header = header; } public Cell getFooter() { return footer; } public void setFooter(Cell footer) { this.footer = footer; } public void setLoad(String load) { this.load = load; } public void setLoadType(int loadType) { this.loadType = loadType; } public String getLoad() { return load; } public int getLoadType() { return loadType; } public void addChild(Cell child) { addChild(child, -1); } public void addChild(Cell child, int index) { if (child != null) { if (index < 0) { items.add(child); } else if (index == 0) { items.add(0, child); } else if (index > 0 && index < items.size()) { items.add(index, child); } } } public void addChildren(List children) { addChildren(children, -1); } public void addChildren(List children, int index) { if (children != null && !children.isEmpty()) { if (index < 0) { items.addAll(children); } else if (index == 0) { items.addAll(0, children); } else if (index > 0 && index < items.size()) { items.addAll(index, children); } } } public Cell getChildAt(int index) { if (index >= 0 && index < items.size()) { return items.get(index); } else { return null; } } public int getChildIndex(Cell child) { if (child != null) { return items.indexOf(child); } else { return -1; } } public void removeChild(Cell child) { if (child != null) { items.remove(child); } } public void removeAllChildren() { items.clear(); } public void removeChildAt(int index) { if (index >= 0 && index < items.size()) { items.remove(index); } } public List getItems() { return items; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/FieldExcluder.java ================================================ package com.alibaba.tac.sdk.tangram4tac; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Created by longerian on 2017/11/18. * * 序列化的时候将排除该注解标记的字段 * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface FieldExcluder { } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/FieldNameMapper.java ================================================ package com.alibaba.tac.sdk.tangram4tac; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Created by longerian on 2017/11/18. * * 字段序列化后,默认输出到json里的字段名与成员变量名相同,通过该注解可提供一个别名 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface FieldNameMapper { String key(); } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/Style.java ================================================ package com.alibaba.tac.sdk.tangram4tac; /** * Created by longerian on 2017/11/5. */ public class Style { @FieldNameMapper(key = "background-color") protected String backgroundColor; @FieldNameMapper(key = "background-image") protected String backgroundImage; protected float width; protected float height; protected int zIndex; protected String display; private float[] margin; private float[] padding; protected float[] cols; protected float aspectRatio; protected float ratio; protected boolean slidable; protected String forLabel; protected boolean disableReuse; public Style() { } public void setMargin(float top, float right, float bottom, float left) { if (this.margin == null) { this.margin = new float[4]; } this.margin[0] = top; this.margin[1] = right; this.margin[2] = bottom; this.margin[3] = left; } public void setPadding(float top, float right, float bottom, float left) { if (this.padding == null) { this.padding = new float[4]; } this.padding[0] = top; this.padding[1] = right; this.padding[2] = bottom; this.padding[3] = left; } public void setBackgroundColor(String backgroundColor) { this.backgroundColor = backgroundColor; } public void setBackgroundImage(String backgroundImage) { this.backgroundImage = backgroundImage; } public void setWidth(int width) { this.width = width; } public void setHeight(int height) { this.height = height; } public void setzIndex(int zIndex) { this.zIndex = zIndex; } public void setDisplay(String display) { this.display = display; } public void setCols(float[] cols) { this.cols = cols; } public void setAspectRatio(float aspectRatio) { this.aspectRatio = aspectRatio; } public String getBackgroundColor() { return backgroundColor; } public String getBackgroundImage() { return backgroundImage; } public float getWidth() { return width; } public float getHeight() { return height; } public int getzIndex() { return zIndex; } public String getDisplay() { return display; } public float[] getMargin() { return margin; } public float[] getPadding() { return padding; } public float[] getCols() { return cols; } public float getAspectRatio() { return aspectRatio; } public float getRatio() { return ratio; } public void setRatio(float ratio) { this.ratio = ratio; } public boolean isSlidable() { return slidable; } public void setSlidable(boolean slidable) { this.slidable = slidable; } public String getForLabel() { return forLabel; } public void setForLabel(String forLabel) { this.forLabel = forLabel; } public boolean isDisableReuse() { return disableReuse; } public void setDisableReuse(boolean disableReuse) { this.disableReuse = disableReuse; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/BannerContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class BannerContainer extends Container { public BannerContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_BANNER; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/BannerStyle.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Style; import java.util.Map; /** * Created by longerian on 2017/11/15. */ public class BannerStyle extends Style { protected boolean autoScroll; protected Map specialInterval; protected boolean infinite; protected String indicatorImg1; protected String indicatorImg2; protected String indicatorGravity; protected String indicatorPosition; protected float indicatorGap; protected float indicatorHeight; protected float indicatorMargin; protected int infiniteMinCount; protected float pageRatio; protected float hGap; protected float scrollMarginLeft; protected float scrollMarginRight; protected float itemRatio; protected float indicatorRadius; protected String indicatorColor; protected String defaultIndicatorColor; public boolean isAutoScroll() { return autoScroll; } public void setAutoScroll(boolean autoScroll) { this.autoScroll = autoScroll; } public Map getSpecialInterval() { return specialInterval; } public void setSpecialInterval(Map specialInterval) { this.specialInterval = specialInterval; } public boolean isInfinite() { return infinite; } public void setInfinite(boolean infinite) { this.infinite = infinite; } public String getIndicatorImg1() { return indicatorImg1; } public void setIndicatorImg1(String indicatorImg1) { this.indicatorImg1 = indicatorImg1; } public String getIndicatorImg2() { return indicatorImg2; } public void setIndicatorImg2(String indicatorImg2) { this.indicatorImg2 = indicatorImg2; } public String getIndicatorGravity() { return indicatorGravity; } public void setIndicatorGravity(String indicatorGravity) { this.indicatorGravity = indicatorGravity; } public String getIndicatorPosition() { return indicatorPosition; } public void setIndicatorPosition(String indicatorPosition) { this.indicatorPosition = indicatorPosition; } public float getIndicatorGap() { return indicatorGap; } public void setIndicatorGap(float indicatorGap) { this.indicatorGap = indicatorGap; } public float getIndicatorHeight() { return indicatorHeight; } public void setIndicatorHeight(float indicatorHeight) { this.indicatorHeight = indicatorHeight; } public float getIndicatorMargin() { return indicatorMargin; } public void setIndicatorMargin(float indicatorMargin) { this.indicatorMargin = indicatorMargin; } public int getInfiniteMinCount() { return infiniteMinCount; } public void setInfiniteMinCount(int infiniteMinCount) { this.infiniteMinCount = infiniteMinCount; } public float getPageRatio() { return pageRatio; } public void setPageRatio(float pageRatio) { this.pageRatio = pageRatio; } public float gethGap() { return hGap; } public void sethGap(float hGap) { this.hGap = hGap; } public float getScrollMarginLeft() { return scrollMarginLeft; } public void setScrollMarginLeft(float scrollMarginLeft) { this.scrollMarginLeft = scrollMarginLeft; } public float getScrollMarginRight() { return scrollMarginRight; } public void setScrollMarginRight(float scrollMarginRight) { this.scrollMarginRight = scrollMarginRight; } public float getItemRatio() { return itemRatio; } public void setItemRatio(float itemRatio) { this.itemRatio = itemRatio; } public float getIndicatorRadius() { return indicatorRadius; } public void setIndicatorRadius(float indicatorRadius) { this.indicatorRadius = indicatorRadius; } public String getIndicatorColor() { return indicatorColor; } public void setIndicatorColor(String indicatorColor) { this.indicatorColor = indicatorColor; } public String getDefaultIndicatorColor() { return defaultIndicatorColor; } public void setDefaultIndicatorColor(String defaultIndicatorColor) { this.defaultIndicatorColor = defaultIndicatorColor; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/CellType.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; /** * Created by longerian on 2017/11/5. */ public class CellType { public static final String TYPE_CONTAINER_FLOW = "container-flow"; public static final String TYPE_CONTAINER_1C_FLOW = "container-oneColumn"; public static final String TYPE_CONTAINER_2C_FLOW = "container-twoColumn"; public static final String TYPE_CONTAINER_3C_FLOW = "container-threeColumn"; public static final String TYPE_CONTAINER_4C_FLOW = "container-fourColumn"; public static final String TYPE_CONTAINER_5C_FLOW = "container-fiveColumn"; public static final String TYPE_CONTAINER_ON_PLUSN = "container-onePlusN"; public static final String TYPE_CONTAINER_FLOAT = "container-float"; public static final String TYPE_CONTAINER_BANNER = "container-banner"; public static final String TYPE_CONTAINER_SCROLL = "container-scroll"; public static final String TYPE_CONTAINER_STICKY = "container-sticky"; public static final String TYPE_CONTAINER_WATERFALL = "container-waterfall"; public static final String TYPE_CONTAINER_FIX = "container-fix"; public static final String TYPE_CONTAINER_SCROLL_FIX = "container-scrollFix"; public static final String TYPE_CONTAINER_SCROLL_FIX_BANNER = "container-scrollFixBanner"; } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/FiveColumnContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class FiveColumnContainer extends Container { public FiveColumnContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_5C_FLOW; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/FixContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; /** * Created by longerian on 2017/11/5. */ public class FixContainer extends OneChildContainer { public FixContainer() { style = new FixStyle(); } @Override public String getType() { return CellType.TYPE_CONTAINER_FIX; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/FixStyle.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Style; /** * Created by longerian on 2017/11/14. */ public class FixStyle extends Style { protected float x; protected float y; protected String align; protected String showType; public float getX() { return x; } public void setX(float x) { this.x = x; } public float getY() { return y; } public void setY(float y) { this.y = y; } public String getAlign() { return align; } public void setAlign(String align) { this.align = align; } public String getShowType() { return showType; } public void setShowType(String showType) { this.showType = showType; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/FloatContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; /** * Created by longerian on 2017/11/5. */ public class FloatContainer extends OneChildContainer { @Override public String getType() { return CellType.TYPE_CONTAINER_FLOAT; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/FlowContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class FlowContainer extends Container { public FlowContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_FLOW; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/FlowStyle.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Style; /** * Created by longerian on 2017/11/14. */ public class FlowStyle extends Style { protected int column; protected float vGap; protected float hGap; protected boolean autoExpand; public int getColumn() { return column; } public void setColumn(int column) { this.column = column; } public float getvGap() { return vGap; } public void setvGap(int vGap) { this.vGap = vGap; } public void setvGap(float vGap) { this.vGap = vGap; } public float gethGap() { return hGap; } public void sethGap(int hGap) { this.hGap = hGap; } public void sethGap(float hGap) { this.hGap = hGap; } public boolean isAutoExpand() { return autoExpand; } public void setAutoExpand(boolean autoExpand) { this.autoExpand = autoExpand; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/FourColumnContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class FourColumnContainer extends Container { public FourColumnContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_4C_FLOW; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/OneChildContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Cell; import com.alibaba.tac.sdk.tangram4tac.Container; import com.alibaba.tac.sdk.tangram4tac.Style; import java.util.List; /** * Created by longerian on 2017/11/5. */ public abstract class OneChildContainer extends Container { public void addChild(Cell child) { addChild(child, -1); } public void addChild(Cell child, int index) { if (items.isEmpty() && child != null) { if (index < 0) { items.add(child); } else if (index == 0) { items.add(0, child); } else if (index > 0 && index < items.size()) { items.add(index, child); } } } public void addChildren(List children) { addChildren(children, -1); } public void addChildren(List children, int index) { if (children != null && !children.isEmpty()) { if (items.isEmpty()) { if (children.size() > 0) { Cell child = children.get(0); if (index < 0) { items.add(child); } else if (index == 0) { items.add(0, child); } else if (index > 0 && index < items.size()) { items.add(index, child); } } } } } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/OneColumnContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class OneColumnContainer extends Container { public OneColumnContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_1C_FLOW; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/OnePlusNContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class OnePlusNContainer extends Container { public OnePlusNContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_ON_PLUSN; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/OnePlusNStyle.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Style; /** * Created by longerian on 2017/11/18. */ public class OnePlusNStyle extends Style { protected float[] rows; public void setRows(float[] rows) { this.rows = rows; } public float[] getRows() { return rows; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/ScrollContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class ScrollContainer extends Container { public ScrollContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_SCROLL; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/ScrollFixBannerContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class ScrollFixBannerContainer extends Container { @Override public String getType() { return CellType.TYPE_CONTAINER_SCROLL_FIX_BANNER; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/ScrollFixContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; /** * Created by longerian on 2017/11/5. */ public class ScrollFixContainer extends OneChildContainer { @Override public String getType() { return CellType.TYPE_CONTAINER_SCROLL_FIX; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/ScrollStyle.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Style; /** * Created by longerian on 2017/11/15. */ public class ScrollStyle extends Style { protected float pageWidth; protected float pageHeight; protected String defaultIndicatorColor; protected String indicatorColor; protected boolean hasIndicator; protected String footerType; protected boolean retainScrollState; public float getPageWidth() { return pageWidth; } public void setPageWidth(float pageWidth) { this.pageWidth = pageWidth; } public float getPageHeight() { return pageHeight; } public void setPageHeight(float pageHeight) { this.pageHeight = pageHeight; } public String getDefaultIndicatorColor() { return defaultIndicatorColor; } public void setDefaultIndicatorColor(String defaultIndicatorColor) { this.defaultIndicatorColor = defaultIndicatorColor; } public String getIndicatorColor() { return indicatorColor; } public void setIndicatorColor(String indicatorColor) { this.indicatorColor = indicatorColor; } public boolean isHasIndicator() { return hasIndicator; } public void setHasIndicator(boolean hasIndicator) { this.hasIndicator = hasIndicator; } public String getFooterType() { return footerType; } public void setFooterType(String footerType) { this.footerType = footerType; } public boolean isRetainScrollState() { return retainScrollState; } public void setRetainScrollState(boolean retainScrollState) { this.retainScrollState = retainScrollState; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/StickyContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; /** * Created by longerian on 2017/11/5. */ public class StickyContainer extends OneChildContainer { public StickyContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_STICKY; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/StickyStyle.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Style; /** * Created by longerian on 2017/11/14. */ public class StickyStyle extends Style { protected String sticky; public String getSticky() { return sticky; } public void setSticky(String sticky) { this.sticky = sticky; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/ThreeColumnContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class ThreeColumnContainer extends Container { public ThreeColumnContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_3C_FLOW; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/TwoColumnContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class TwoColumnContainer extends Container { public TwoColumnContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_2C_FLOW; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/WaterFallContainer.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Container; /** * Created by longerian on 2017/11/5. */ public class WaterFallContainer extends Container { public WaterFallContainer() { } @Override public String getType() { return CellType.TYPE_CONTAINER_WATERFALL; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/lib/WaterFallStyle.java ================================================ package com.alibaba.tac.sdk.tangram4tac.lib; import com.alibaba.tac.sdk.tangram4tac.Style; /** * Created by longerian on 2017/11/14. */ public class WaterFallStyle extends Style { protected float vGap; protected float hGap; protected float gap; protected int column; public float getvGap() { return vGap; } public void setvGap(float vGap) { this.vGap = vGap; } public float gethGap() { return hGap; } public void sethGap(float hGap) { this.hGap = hGap; } public float getGap() { return gap; } public void setGap(float gap) { this.gap = gap; this.vGap = gap; this.hGap = gap; } public int getColumn() { return column; } public void setColumn(int column) { this.column = column; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/render/DefaultRender.java ================================================ package com.alibaba.tac.sdk.tangram4tac.render; import com.alibaba.tac.sdk.tangram4tac.Cell; import com.alibaba.tac.sdk.tangram4tac.FieldExcluder; import com.alibaba.tac.sdk.tangram4tac.FieldNameMapper; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.util.*; /** * Created by longerian on 2017/11/5. */ public class DefaultRender implements IRender> { private static Map classFiledCache = new HashMap(); private static Map, List>> typesCache = new HashMap, List>>(); private static Map fieldExcluderCache = new HashMap(); private static Map fieldNameMapCache = new HashMap(); public DefaultRender() { } @Override public Map renderTo(Cell cell) { return internalRenderForClass(cell); } private Map internalRenderForClass(Object object) { Map outputData = new HashMap(); if (object != null) { Class selfClass = object.getClass(); List> clazzes = lookupCellTypes(selfClass); for (int i = 0, size = clazzes.size(); i < size; i++) { Class clazz = clazzes.get(i); internalRenderFields(object, clazz, outputData); } } return outputData; } private void internalRenderFields(Object object, Class clazz, Map output) { Field[] selfFields = classFiledCache.get(clazz); Field oneField; String name; Object value; FieldNameMapper fieldNameMapper; FieldExcluder fieldExcluder; if (selfFields == null) { selfFields = clazz.getDeclaredFields(); for (int i = 0 , length = selfFields.length; i < length; i++) { oneField = selfFields[i]; oneField.setAccessible(true); fieldExcluder = oneField.getAnnotation(FieldExcluder.class); if (fieldExcluder != null) { fieldExcluderCache.put(oneField, fieldExcluder); } fieldNameMapper = oneField.getAnnotation(FieldNameMapper.class); if (fieldNameMapper != null) { name = fieldNameMapper.key(); if (name != null && name.length() > 0) { fieldNameMapCache.put(oneField, name); } } } classFiledCache.put(clazz, selfFields); } for (int i = 0 , length = selfFields.length; i < length; i++) { oneField = selfFields[i]; fieldExcluder = fieldExcluderCache.get(oneField); if (fieldExcluder != null) { continue; } if (fieldNameMapCache.containsKey(oneField)) { name = fieldNameMapCache.get(oneField); } else { name = oneField.getName(); } try { value = oneField.get(object); internalRenderField(name, value, output); } catch (IllegalAccessException e) { } } } private void internalRenderField(String name, Object object, Map output) { if (object != null) { Object value = getFieldValue(object); if (value != null) { output.put(name, value); } } } private Object getFieldValue(Object object) { Class oneFieldClazz = object.getClass(); if (isBasicType(object)) { return getNonDefaultValueFromBasicType(object); } else if (Collection.class.isAssignableFrom(oneFieldClazz)) { Collection lists = (Collection) object; List outputLists = new ArrayList(); Iterator itr = lists.iterator(); while (itr.hasNext()) { Object item = itr.next(); Object value = getFieldValue(item); if (value != null) { outputLists.add(value); } else { outputLists.add(item); } } if (outputLists.isEmpty()) { return null; } else { return outputLists; } } else if (Map.class.isAssignableFrom(oneFieldClazz)) { Map maps = (Map) object; Map outputMaps = new HashMap(); Iterator itr = maps.keySet().iterator(); while (itr.hasNext()) { Object key = itr.next(); Object item = maps.get(key); Object value = getFieldValue(item); if (value != null) { outputMaps.put(String.valueOf(key), value); } } if (outputMaps.isEmpty()) { return null; } else { return outputMaps; } } else if (oneFieldClazz.isArray()) { int length = Array.getLength(object); Collection lists = new ArrayList(); for (int a = 0; a < length; a++) { lists.add(Array.get(object, a)); } List outputLists = new ArrayList(); Iterator itr = lists.iterator(); while (itr.hasNext()) { Object item = itr.next(); Object value = getFieldValue(item); if (value != null) { outputLists.add(value); } else { outputLists.add(item); } } if (outputLists.isEmpty()) { return null; } else { return outputLists; } } else { return internalRenderForClass(object); } } private List> lookupCellTypes(Class selfClass) { List> types = typesCache.get(selfClass); if (types == null) { types = new ArrayList>(); Class clazz = selfClass; while (clazz != null && !clazz.equals(Object.class)) { types.add(clazz); clazz = clazz.getSuperclass(); } typesCache.put(selfClass, types); } return types; } private boolean isBasicType(Object object) { return object instanceof Integer || object instanceof Float || object instanceof Double || object instanceof Short || object instanceof Long || object instanceof String || object instanceof Boolean; } private Object getNonDefaultValueFromBasicType(Object object) { if (object instanceof Integer) { if (((Integer) object).intValue() == 0) { return null; } } if (object instanceof Float) { if (((Float) object).floatValue() == 0.0f) { return null; } } if (object instanceof Double) { if (((Double) object).doubleValue() == 0.0) { return null; } } if (object instanceof Short) { if (((Short) object).shortValue() == 0) { return null; } } if (object instanceof Long) { if (((Long) object).longValue() == 0) { return null; } } if (object instanceof Boolean) { if (((Boolean) object).booleanValue() == false) { return null; } } if (object instanceof String) { if (((String) object).length() == 0) { return null; } } return object; } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/render/IRender.java ================================================ package com.alibaba.tac.sdk.tangram4tac.render; /** * Created by longerian on 2017/11/5. */ public interface IRender { O renderTo(I input); } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/utils/Pair.java ================================================ package com.alibaba.tac.sdk.tangram4tac.utils; /** * Created by longerian on 2017/10/31. */ public class Pair { public final F first; public final S second; public Pair(F first, S second) { this.first = first; this.second = second; } public boolean equals(Object o) { if(!(o instanceof Pair)) { return false; } else { Pair p = (Pair)o; return objectsEqual(p.first, this.first) && objectsEqual(p.second, this.second); } } private static boolean objectsEqual(Object a, Object b) { return a == b || a != null && a.equals(b); } public int hashCode() { return (this.first == null?0:this.first.hashCode()) ^ (this.second == null?0:this.second.hashCode()); } public static Pair create(A a, B b) { return new Pair(a, b); } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/tangram4tac/utils/Utils.java ================================================ package com.alibaba.tac.sdk.tangram4tac.utils; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.Scanner; /** * Created by longerian on 2017/11/14. */ public class Utils { public static String format ( final List> parameters, final String encoding) { final StringBuilder result = new StringBuilder(); for (final Pair parameter : parameters) { final String encodedName = encode(parameter.first, encoding); final String value = parameter.second; final String encodedValue = value != null ? encode(value, encoding) : ""; if (result.length() > 0) result.append(PARAMETER_SEPARATOR); result.append(encodedName); result.append(NAME_VALUE_SEPARATOR); result.append(encodedValue); } return result.toString(); } public static List > parse (final URI uri, final String encoding) { List > result = new ArrayList>(); final String query = uri.getRawQuery(); if (query != null && query.length() > 0) { parse(result, new Scanner(query), encoding); } return result; } private static final String PARAMETER_SEPARATOR = "&"; private static final String NAME_VALUE_SEPARATOR = "="; public static void parse ( final List > parameters, final Scanner scanner, final String encoding) { scanner.useDelimiter(PARAMETER_SEPARATOR); while (scanner.hasNext()) { String nameValue = scanner.next(); if (nameValue == null || nameValue.length() == 0) { continue; } int indexOfEq = nameValue.indexOf(NAME_VALUE_SEPARATOR); if (indexOfEq == -1) { continue; } String name = nameValue.substring(0, indexOfEq); String value = nameValue.substring(indexOfEq + 1); if ((value == null || value.length() == 0) || (name == null || name.length() == 0)) { continue; } name = decode(name, encoding); value = decode(value, encoding); parameters.add(new Pair(name, value)); } } public static URI createURI( final String scheme, final String host, int port, final String path, final String query, final String fragment) throws URISyntaxException { StringBuilder buffer = new StringBuilder(); if (host != null) { if (scheme != null) { buffer.append(scheme); buffer.append("://"); } else { buffer.append("//"); } buffer.append(host); if (port > 0) { buffer.append(':'); buffer.append(port); } } if (path == null || !path.startsWith("/")) { buffer.append('/'); } if (path != null) { buffer.append(path); } if (query != null) { buffer.append('?'); buffer.append(query); } if (fragment != null) { buffer.append('#'); buffer.append(fragment); } return new URI(buffer.toString()); } private static String decode (final String content, final String encoding) { try { return URLDecoder.decode(content, encoding != null ? encoding : "UTF-8"); } catch (UnsupportedEncodingException problem) { throw new IllegalArgumentException(problem); } } private static String encode (final String content, final String encoding) { try { return URLEncoder.encode(content, encoding != null ? encoding : "UTF-8"); } catch (UnsupportedEncodingException problem) { throw new IllegalArgumentException(problem); } } } ================================================ FILE: tac-sdk/src/main/java/com/alibaba/tac/sdk/utils/TacIPUtils.java ================================================ package com.alibaba.tac.sdk.utils; import java.net.*; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; /** * Created by changkun.hck on 2016/12/29. */ public class TacIPUtils { private static String staticIp; private static String staticHost; static { // ip init try { if(staticIp == null) { staticIp = Inet4Address.getLocalHost().getHostAddress(); } } catch (UnknownHostException var1) { var1.printStackTrace(); } // host init try { if(staticHost == null) { staticHost = Inet4Address.getLocalHost().getHostName(); } } catch (UnknownHostException var1) { var1.printStackTrace(); } } public static String getLocalHostName() { return staticHost; } public static String getLocalIp() { return staticIp; } @Deprecated private static Collection getAllHostAddress() { try { Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); Collection addresses = new ArrayList(); while (networkInterfaces.hasMoreElements()) { NetworkInterface networkInterface = networkInterfaces.nextElement(); Enumeration inetAddresses = networkInterface.getInetAddresses(); while (inetAddresses.hasMoreElements()) { InetAddress inetAddress = inetAddresses.nextElement(); addresses.add(inetAddress); } } return addresses; } catch (SocketException e) { } return Collections.emptyList(); } @Deprecated public static Collection getAllIpv4NoLoopbackAddresses() { Collection noLoopbackAddresses = new ArrayList(); Collection allInetAddresses = getAllHostAddress(); for (InetAddress address : allInetAddresses) { if (!address.isLoopbackAddress() && address instanceof Inet4Address) { noLoopbackAddresses.add(address.getHostAddress()); } } return noLoopbackAddresses; } @Deprecated public static String getFirstNoLoopbackAddress() { Collection allNoLoopbackAddresses = getAllIpv4NoLoopbackAddresses(); return allNoLoopbackAddresses.iterator().next(); } }