├── .gitignore ├── LICENSE ├── README.md ├── isona-core ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── spring4all │ │ └── isona │ │ ├── autoconfig │ │ └── IsonaProperties.java │ │ ├── service │ │ ├── DiscoveryService.java │ │ └── impl │ │ │ ├── AbstractDiscoveryService.java │ │ │ ├── ConsulDiscoveryService.java │ │ │ └── EurekaDiscoveryService.java │ │ └── utils │ │ └── HttpUtils.java │ └── test │ └── java │ └── com │ └── spring4all │ └── isona │ └── IsonaApplicationTests.java ├── isona-example-consul ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── spring4all │ │ │ └── isona │ │ │ └── example │ │ │ └── consul │ │ │ └── ConsulIsonaApplication.java │ └── resources │ │ └── application.properties │ └── test │ ├── java │ └── com │ │ └── spring4all │ │ └── isona │ │ └── example │ │ └── consul │ │ └── ConsulIsonaApplicationTests.java │ └── resources │ └── application.properties ├── isona-example-eureka ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── spring4all │ │ │ └── isona │ │ │ └── example │ │ │ └── eureka │ │ │ └── EurekaIsonaApplication.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── spring4all │ └── isona │ └── example │ └── eureka │ └── EurekaIsonaApplicationTests.java ├── isona-parent └── pom.xml ├── isona-storage ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── spring4all │ │ │ └── isona │ │ │ └── storage │ │ │ └── domain │ │ │ ├── IsonaInstance.java │ │ │ ├── IsonaInstanceRepo.java │ │ │ └── IsonaInstanceStatus.java │ └── resources │ │ └── application.properties │ └── test │ └── resources │ └── application.properties ├── isona-web ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── spring4all │ │ └── isona │ │ ├── autoconfig │ │ ├── CommonAutoConfig.java │ │ ├── consul │ │ │ ├── ConsulIsonaAutoConfig.java │ │ │ └── EnableConsulIsonaServer.java │ │ └── eureka │ │ │ ├── EnableEurekaIsonaServer.java │ │ │ └── EurekaIsonaAutoConfig.java │ │ ├── dto │ │ ├── InstanceDto.java │ │ └── ServiceDto.java │ │ ├── task │ │ └── InstanceSnapshotTask.java │ │ └── web │ │ ├── IndexController.java │ │ └── ServiceViewController.java │ └── resources │ ├── static │ └── assets │ │ ├── admin-tools │ │ └── admin-forms │ │ │ ├── css │ │ │ ├── admin-forms.css │ │ │ └── admin-forms.min.css │ │ │ └── js │ │ │ ├── additional-methods.min.js │ │ │ ├── jquery-ui-datepicker.min.js │ │ │ ├── jquery-ui-monthpicker.min.js │ │ │ ├── jquery.spectrum.min.js │ │ │ ├── jquery.stepper.min.js │ │ │ ├── jquery.steps.min.js │ │ │ └── jquery.validate.min.js │ │ ├── fonts │ │ ├── admindesigns │ │ │ ├── admindesigns.eot │ │ │ ├── admindesigns.svg │ │ │ ├── admindesigns.ttf │ │ │ └── admindesigns.woff │ │ ├── font-awesome │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ │ ├── glyphicons-pro │ │ │ ├── glyphicons-pro.css │ │ │ ├── glyphicons-regular.eot │ │ │ ├── glyphicons-regular.svg │ │ │ ├── glyphicons-regular.ttf │ │ │ └── glyphicons-regular.woff │ │ ├── glyphicons │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── icomoon │ │ │ ├── icomoon.css │ │ │ ├── icomoon.eot │ │ │ ├── icomoon.svg │ │ │ ├── icomoon.ttf │ │ │ └── icomoon.woff │ │ ├── iconsweets │ │ │ ├── IconSweets-Deluxe.eot │ │ │ ├── IconSweets-Deluxe.svg │ │ │ ├── IconSweets-Deluxe.ttf │ │ │ ├── IconSweets-Deluxe.woff │ │ │ └── iconsweets.css │ │ ├── octicons │ │ │ ├── octicons.css │ │ │ ├── octicons.svg │ │ │ ├── octicons.ttf │ │ │ └── octicons.woff │ │ ├── stateface │ │ │ ├── stateface-regular-webfont.eot │ │ │ ├── stateface-regular-webfont.svg │ │ │ ├── stateface-regular-webfont.ttf │ │ │ ├── stateface-regular-webfont.woff │ │ │ └── stateface.css │ │ └── zocial │ │ │ ├── zocial-regular-webfont.eot │ │ │ ├── zocial-regular-webfont.svg │ │ │ ├── zocial-regular-webfont.ttf │ │ │ └── zocial.css │ │ ├── img │ │ ├── avatars │ │ │ ├── 1.jpg │ │ │ ├── 2.jpg │ │ │ ├── 3.jpg │ │ │ ├── 4.jpg │ │ │ ├── 5.jpg │ │ │ ├── 6.jpg │ │ │ └── profile_avatar.jpg │ │ ├── favicon.ico │ │ ├── logos │ │ │ ├── logo.png │ │ │ ├── logo_grey.png │ │ │ └── logo_white.png │ │ ├── patterns │ │ │ ├── backgrounds │ │ │ │ └── 1.jpg │ │ │ ├── topbar-bg.jpg │ │ │ └── topbar-bg2.jpg │ │ ├── plugins │ │ │ ├── admindock-sprite.png │ │ │ ├── arrow_left_12x12.png │ │ │ ├── arrow_right_12x12.png │ │ │ └── grippy_large.png │ │ ├── sprites │ │ │ ├── favicons.png │ │ │ ├── flag-sm.png │ │ │ ├── flag-xs.png │ │ │ └── news-logo_sprite.png │ │ └── stock │ │ │ ├── 1.jpg │ │ │ ├── 2.jpg │ │ │ ├── 3.jpg │ │ │ ├── 4.jpg │ │ │ ├── 5.jpg │ │ │ └── products │ │ │ ├── thumb_1.jpg │ │ │ ├── thumb_2.jpg │ │ │ ├── thumb_3.jpg │ │ │ ├── thumb_4.jpg │ │ │ ├── thumb_5.jpg │ │ │ ├── thumb_6.jpg │ │ │ └── thumb_7.jpg │ │ ├── js │ │ ├── demo │ │ │ ├── charts │ │ │ │ ├── d3.js │ │ │ │ ├── flot.js │ │ │ │ └── highcharts.js │ │ │ ├── demo.js │ │ │ └── widgets.js │ │ ├── main.js │ │ └── utility │ │ │ └── utility.js │ │ └── skin │ │ └── default_skin │ │ └── css │ │ └── theme.css │ ├── templates │ ├── index.html │ ├── layout │ │ ├── footer.html │ │ ├── header.html │ │ ├── layout.html │ │ ├── menu.html │ │ └── topbar.html │ └── service │ │ └── service_list.html │ └── thymeleaf.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── statics └── images └── 001.jpeg /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | 24 | .idea 25 | *.iml 26 | src/template 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Isona 2 | 3 | 这是一款基于Spring Boot与Spring Cloud构建的微服务管理工具。一个类似于Spring Boot Admin的轮子,但该项目不仅仅是一个轮子,后续将会对接spring4all社区版的微服务脚手架应用实现对使用了Spring Cloud构建的微服务系统全面的监控管理等功能。 4 | 5 | ![](statics/images/001.jpeg) 6 | 7 | 支持我们就给我们`Star`吧!更多内容,请持续关注我们的社区:[spring4all.com](http://www.spring4all.com) 8 | 9 | ## 主要成员 10 | 11 | - [程序猿DD](http://blog.didispace.com) 12 | - **招募有志之士ing!!!** 13 | 14 | ## 功能简介 15 | 16 | - 基于Spring Cloud体系的微服务总览 17 | - Spring Boot Actuator各端点信息的可视化视图 18 | - 更多内容待开发... 19 | 20 | ## 使用说明 21 | 22 | **注意:由于目前还是测试版,并未提交各依赖包到中央仓库。用户可git clone本项目,然后在本地编译之后,按如下步骤使用!** 23 | 24 | 该项目的使用方向简单,如果没有个性化定制需要,只需要按如下操作就能马上启动一个Isona来对您使用Spring Cloud构建的微服务系统实现管理。 25 | 26 | - 第一步:创建一个基础的Spring Boot应用 27 | - 第二步:`pom.xml`中引入如下以来: 28 | 29 | ```xml 30 | 31 | com.spring4all 32 | isona-web 33 | 0.0.1-SNAPSHOT 34 | 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-eureka 39 | 40 | 41 | 42 | com.h2database 43 | h2 44 | 45 | ``` 46 | 47 | - 第三步:编写应用主类,具体如下: 48 | 49 | ```java 50 | @EnableEurekaIsonaServer 51 | @SpringBootApplication 52 | public class EurekaIsonaApplication { 53 | 54 | public static void main(String[] args) { 55 | SpringApplication.run(EurekaIsonaApplication.class, args); 56 | } 57 | 58 | } 59 | ``` 60 | 61 | > Tips:如果使用consul做注册中心的话,只需要替换下面两个地方即可: 62 | > - 将`spring-cloud-starter-eureka`的依赖换成:`spring-cloud-starter-consul-discovery` 63 | > - 将`@EnableEurekaIsonaServer`换成:`@EnableConsulIsonaServer` 64 | 65 | ## 更多配置 66 | 67 | 。。。 68 | -------------------------------------------------------------------------------- /isona-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.spring4all 7 | isona-core 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | isona-core 12 | Go ! Go ! Go ! 13 | 14 | 15 | com.spring4all 16 | isona-parent 17 | 0.0.1-SNAPSHOT 18 | 19 | 20 | 21 | 22 | com.spring4all 23 | isona-storage 24 | 0.0.1-SNAPSHOT 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-configuration-processor 30 | true 31 | 32 | 33 | org.springframework.cloud 34 | spring-cloud-commons 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-eureka 39 | true 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-starter-consul-discovery 44 | true 45 | 46 | 47 | com.squareup.okhttp3 48 | okhttp 49 | ${okhttp.version} 50 | 51 | 52 | com.alibaba 53 | fastjson 54 | ${fastjson.version} 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-maven-plugin 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /isona-core/src/main/java/com/spring4all/isona/autoconfig/IsonaProperties.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.autoconfig; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | @Data 7 | @ConfigurationProperties("isona") 8 | public class IsonaProperties { 9 | 10 | private ServiceProperties service = new ServiceProperties(); 11 | 12 | public String getSerivceManagementContextPath() { 13 | return this.service.getManagementContextPath(); 14 | } 15 | 16 | public String getSerivceInfoUrl() { 17 | return this.service.getInfoUrl(); 18 | } 19 | 20 | public String getSerivceMetricsUrl() { 21 | return this.service.getMetricsUrl(); 22 | } 23 | 24 | @Data 25 | static class ServiceProperties { 26 | 27 | /** 28 | * 监控端点的context-path,对应微服务的management.context-path配置内容 29 | **/ 30 | private String managementContextPath = ""; 31 | 32 | private String infoUrl = "/info"; 33 | private String metricsUrl = "/metrics"; 34 | 35 | } 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /isona-core/src/main/java/com/spring4all/isona/service/DiscoveryService.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.service; 2 | 3 | 4 | 5 | import com.spring4all.isona.storage.domain.IsonaInstance; 6 | 7 | import java.util.List; 8 | 9 | public interface DiscoveryService { 10 | 11 | List getServices(); 12 | 13 | List getServiceInstances(String serviceName); 14 | 15 | List getAllServiceInstances(); 16 | 17 | int getTotalInstances(); 18 | 19 | void fillInstanceInfo(IsonaInstance instanceInfo); 20 | 21 | void fillInstanceMetrics(IsonaInstance instanceInfo); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /isona-core/src/main/java/com/spring4all/isona/service/impl/AbstractDiscoveryService.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.service.impl; 2 | 3 | import com.spring4all.isona.autoconfig.IsonaProperties; 4 | import com.spring4all.isona.service.DiscoveryService; 5 | import com.spring4all.isona.utils.HttpUtils; 6 | import com.spring4all.isona.storage.domain.IsonaInstance; 7 | import lombok.Data; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.cloud.client.discovery.DiscoveryClient; 11 | 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | @Slf4j 16 | public abstract class AbstractDiscoveryService implements DiscoveryService { 17 | 18 | @Autowired 19 | protected DiscoveryClient discoveryClient; 20 | @Autowired 21 | protected IsonaProperties isonaProperties; 22 | 23 | @Override 24 | public List getServices() { 25 | return discoveryClient.getServices(); 26 | } 27 | 28 | @Override 29 | public int getTotalInstances() { 30 | int sum = 0; 31 | for (String service : getServices()) { 32 | sum += discoveryClient.getInstances(service).size(); 33 | } 34 | return sum; 35 | } 36 | 37 | @Override 38 | public void fillInstanceInfo(IsonaInstance instanceInfo) { 39 | 40 | // 访问/info收集信息 41 | StringBuffer url = new StringBuffer() 42 | .append("http://") 43 | .append(instanceInfo.getServiceAddress()) 44 | .append(":") 45 | .append(instanceInfo.getServicePort()) 46 | .append(isonaProperties.getSerivceManagementContextPath()) 47 | .append(isonaProperties.getSerivceInfoUrl()); 48 | 49 | try { 50 | Info info = (Info) HttpUtils.call(url.toString(), Info.class); 51 | if (info != null) { 52 | log.debug(info.toString()); 53 | if (info.getGit() != null) { 54 | instanceInfo.setBranch(info.getGit().getBranch()); 55 | instanceInfo.setCommitId(info.getGit().getCommit().getId()); 56 | instanceInfo.setCommitTime(info.getGit().getCommit().getTime()); 57 | } 58 | if (info.getBuild() != null) { 59 | instanceInfo.setBuildTime(info.getBuild().getTime()); 60 | } 61 | } 62 | } catch (Exception e) { 63 | log.warn(instanceInfo.getServiceName() + ", " + url + " : " + e.getMessage()); 64 | } 65 | } 66 | 67 | @Override 68 | public void fillInstanceMetrics(IsonaInstance instanceInfo) { 69 | // 访问/metrics收集信息 70 | StringBuffer url = new StringBuffer() 71 | .append("http://") 72 | .append(instanceInfo.getServiceAddress()) 73 | .append(":") 74 | .append(instanceInfo.getServicePort()) 75 | .append(isonaProperties.getSerivceManagementContextPath()) 76 | .append(isonaProperties.getSerivceMetricsUrl()); 77 | 78 | try { 79 | Map metrics = (Map) HttpUtils.call(url.toString(), Map.class); 80 | if (metrics != null) { 81 | log.debug(metrics.toString()); 82 | 83 | if(metrics.get("mem") != null) { 84 | int mem = (Integer) metrics.get("mem"); 85 | int memFree = (Integer) metrics.get("mem.free"); 86 | instanceInfo.setMemTotal(mem); 87 | instanceInfo.setMemUsed(mem - memFree); 88 | double p = (double) (mem - memFree) / mem * 100; 89 | instanceInfo.setMemPercent((int) p); 90 | 91 | int threads = (Integer) metrics.get("threads"); 92 | int threadsDaemon = (Integer) metrics.get("threads.daemon"); 93 | int threadsPeak = (Integer) metrics.get("threads.peak"); 94 | instanceInfo.setThreads(threads); 95 | instanceInfo.setThreadsDaemon(threadsDaemon); 96 | instanceInfo.setThreadsPeak(threadsPeak); 97 | } 98 | } 99 | } catch (Exception e) { 100 | log.warn(instanceInfo.getServiceName() + ", " + url + " : " + e.getMessage()); 101 | } 102 | } 103 | 104 | @Data 105 | static class Info { 106 | private String env; 107 | private GitInfo git; 108 | private BuildInfo build; 109 | 110 | @Data 111 | static class GitInfo { 112 | private Commit commit; 113 | private String branch; 114 | 115 | @Data 116 | static class Commit { 117 | private Long time; 118 | private String id; 119 | } 120 | } 121 | 122 | 123 | @Data 124 | static class BuildInfo { 125 | private String version; 126 | private String artifact; 127 | private String name; 128 | private String group; 129 | private Long time; 130 | } 131 | 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /isona-core/src/main/java/com/spring4all/isona/service/impl/ConsulDiscoveryService.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.service.impl; 2 | 3 | import com.ecwid.consul.v1.ConsulClient; 4 | import com.ecwid.consul.v1.health.model.Check; 5 | import com.ecwid.consul.v1.health.model.HealthService; 6 | import com.spring4all.isona.storage.domain.IsonaInstance; 7 | import com.spring4all.isona.storage.domain.IsonaInstanceStatus; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.cloud.consul.discovery.ConsulDiscoveryClient; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Vector; 15 | import java.util.concurrent.CountDownLatch; 16 | import java.util.concurrent.LinkedBlockingQueue; 17 | import java.util.concurrent.ThreadPoolExecutor; 18 | import java.util.concurrent.TimeUnit; 19 | 20 | @Slf4j 21 | public class ConsulDiscoveryService extends AbstractDiscoveryService { 22 | 23 | @Autowired 24 | private ConsulDiscoveryClient consulDiscoveryClient; 25 | 26 | @Autowired 27 | private ConsulClient consulClient; 28 | 29 | @Override 30 | public List getServiceInstances(String serviceName) { 31 | // 处理下consul返回的服务信息 32 | List result = new ArrayList<>(); 33 | List response = consulClient.getHealthServices(serviceName, false, null).getValue(); 34 | for(HealthService service : response) { 35 | if(service.getService().getService().equals("consul")) { 36 | continue; 37 | } 38 | 39 | IsonaInstance instanceInfo = new IsonaInstance(); 40 | 41 | // 基础信息 42 | instanceInfo.setServiceName(serviceName); 43 | instanceInfo.setServiceAddress(service.getService().getAddress()); 44 | instanceInfo.setServicePort(service.getService().getPort()); 45 | 46 | instanceInfo.setNode(service.getNode().getNode()); 47 | instanceInfo.setAddress(service.getNode().getAddress()); 48 | instanceInfo.setMetadata(service.getService().getTags().toString()); 49 | 50 | // 健康状态信息 51 | for (Check check : service.getChecks()) { 52 | switch (check.getStatus()) { 53 | case PASSING: instanceInfo.setStatus(IsonaInstanceStatus.UP);break; 54 | case UNKNOWN: instanceInfo.setStatus(IsonaInstanceStatus.UNKNOWN);break; 55 | case WARNING: instanceInfo.setStatus(IsonaInstanceStatus.WARNING);break; 56 | case CRITICAL: instanceInfo.setStatus(IsonaInstanceStatus.DOWN);break; 57 | default: instanceInfo.setStatus(IsonaInstanceStatus.UNKNOWN); break; 58 | } 59 | } 60 | 61 | // 填充其他信息 62 | fillInstanceMetrics(instanceInfo); 63 | 64 | result.add(instanceInfo); 65 | } 66 | 67 | return result; 68 | } 69 | 70 | @Override 71 | public List getAllServiceInstances() { 72 | List result = new Vector<>(); 73 | List serviceList = getServices(); 74 | 75 | // FIXME 当服务实例多了,会有潜在问题,待后续完善 76 | ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 77 | 500L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(200)); 78 | CountDownLatch c = new CountDownLatch(serviceList.size()); 79 | 80 | for (String service : serviceList) { 81 | executor.execute(()->{ 82 | result.addAll(getServiceInstances(service)); 83 | c.countDown(); 84 | }); 85 | } 86 | 87 | try { 88 | c.await(); 89 | } catch (InterruptedException e) { 90 | e.printStackTrace(); 91 | } 92 | 93 | log.info("Total Service : " + serviceList.size() + ", Total Instances : " + result.size()); 94 | return result; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /isona-core/src/main/java/com/spring4all/isona/service/impl/EurekaDiscoveryService.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.service.impl; 2 | 3 | import com.netflix.appinfo.InstanceInfo; 4 | import com.spring4all.isona.storage.domain.IsonaInstance; 5 | import com.spring4all.isona.storage.domain.IsonaInstanceStatus; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.cloud.client.ServiceInstance; 9 | import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | @Slf4j 15 | public class EurekaDiscoveryService extends AbstractDiscoveryService { 16 | 17 | @Autowired 18 | private EurekaDiscoveryClient eurekaDiscoveryClient; 19 | 20 | @Override 21 | public List getServiceInstances(String serviceName) { 22 | // TODO 处理下eureka返回的服务信息 23 | 24 | List result = new ArrayList<>(); 25 | 26 | for(ServiceInstance instance : eurekaDiscoveryClient.getInstances(serviceName)) { 27 | InstanceInfo eurekaInstance = ((EurekaDiscoveryClient.EurekaServiceInstance) instance).getInstanceInfo(); 28 | 29 | IsonaInstance instanceInfo = new IsonaInstance(); 30 | 31 | // 基础信息 32 | instanceInfo.setServiceName(serviceName); 33 | instanceInfo.setServiceAddress(eurekaInstance.getIPAddr()); 34 | instanceInfo.setServicePort(eurekaInstance.getPort()); 35 | 36 | instanceInfo.setNode(eurekaInstance.getIPAddr()); 37 | instanceInfo.setAddress(eurekaInstance.getIPAddr()); 38 | instanceInfo.setMetadata(eurekaInstance.getMetadata().toString()); 39 | 40 | // 健康状态信息 41 | switch (eurekaInstance.getStatus()) { 42 | case UP: instanceInfo.setStatus(IsonaInstanceStatus.UP);break; 43 | case UNKNOWN: instanceInfo.setStatus(IsonaInstanceStatus.UNKNOWN);break; 44 | case STARTING: instanceInfo.setStatus(IsonaInstanceStatus.WARNING);break; 45 | case OUT_OF_SERVICE: instanceInfo.setStatus(IsonaInstanceStatus.WARNING);break; 46 | case DOWN: instanceInfo.setStatus(IsonaInstanceStatus.DOWN);break; 47 | default: instanceInfo.setStatus(IsonaInstanceStatus.UNKNOWN); break; 48 | } 49 | 50 | // 填充其他信息 51 | fillInstanceMetrics(instanceInfo); 52 | 53 | result.add(instanceInfo); 54 | } 55 | 56 | return result; 57 | } 58 | 59 | @Override 60 | public List getAllServiceInstances() { 61 | List result = new ArrayList<>(); 62 | for (String service : getServices()) { 63 | result.addAll(getServiceInstances(service)); 64 | } 65 | return result; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /isona-core/src/main/java/com/spring4all/isona/utils/HttpUtils.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.utils; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import okhttp3.*; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | 8 | @Slf4j 9 | public class HttpUtils { 10 | 11 | private static OkHttpClient okHttpClient; 12 | private static HttpUtils httpUtils; 13 | 14 | @Autowired 15 | public HttpUtils(OkHttpClient okHttpClient) { 16 | httpUtils.okHttpClient = okHttpClient; 17 | } 18 | 19 | public static Object call(String url, Class clazz) { 20 | // TODO 可以更优雅一些 21 | try { 22 | Request request = new Request.Builder() 23 | .url(url) 24 | .get() 25 | .build(); 26 | 27 | Call call = okHttpClient.newCall(request); 28 | Response response = call.execute(); 29 | ResponseBody body = response.body(); 30 | return JSONObject.parseObject(body.string(), clazz); 31 | } catch (Exception e) { 32 | log.warn(url + ", " + e.getMessage()); 33 | return null; 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /isona-core/src/test/java/com/spring4all/isona/IsonaApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @Slf4j 9 | @RunWith(SpringRunner.class) 10 | @SpringBootTest 11 | public class IsonaApplicationTests { 12 | 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /isona-example-consul/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.spring4all 7 | isona-example-consul 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | isona-example-consul 12 | Go ! Go ! Go ! 13 | 14 | 15 | 16 | 17 | com.spring4all 18 | isona-web 19 | 0.0.1-SNAPSHOT 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-consul-discovery 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-devtools 30 | runtime 31 | 32 | 33 | org.projectlombok 34 | lombok 35 | true 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | 44 | 45 | com.h2database 46 | h2 47 | 48 | 49 | 50 | 51 | 52 | UTF-8 53 | UTF-8 54 | 1.8 55 | Edgware.SR1 56 | 1.5.10.RELEASE 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.cloud 63 | spring-cloud-dependencies 64 | ${spring-cloud.version} 65 | pom 66 | import 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-parent 71 | ${spring-boot.version} 72 | pom 73 | import 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | org.springframework.boot 82 | spring-boot-maven-plugin 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /isona-example-consul/src/main/java/com/spring4all/isona/example/consul/ConsulIsonaApplication.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.example.consul; 2 | 3 | import com.spring4all.isona.autoconfig.consul.EnableConsulIsonaServer; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @EnableConsulIsonaServer 8 | @SpringBootApplication 9 | public class ConsulIsonaApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(ConsulIsonaApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /isona-example-consul/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=isona-consul-eureka 2 | 3 | server.port=10020 4 | 5 | spring.cloud.consul.host=10.19.90.19 6 | spring.cloud.consul.port=8500 7 | spring.cloud.consul.discovery.health-check-path=${management.context-path}/health 8 | 9 | isona.service.management-context-path=/yhops 10 | 11 | management.context-path=/isona 12 | management.security.enabled=false 13 | 14 | # H2 15 | spring.h2.console.enabled=true 16 | spring.h2.console.path=/h2 17 | 18 | # Datasource 19 | spring.datasource.url=jdbc:h2:file:./database;AUTO_SERVER=TRUE;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE 20 | spring.datasource.username=sa 21 | spring.datasource.password= 22 | spring.datasource.driver-class-name=org.h2.Driver 23 | 24 | spring.jpa.properties.hibernate.hbm2ddl.auto=update 25 | -------------------------------------------------------------------------------- /isona-example-consul/src/test/java/com/spring4all/isona/example/consul/ConsulIsonaApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.example.consul; 2 | 3 | import com.spring4all.isona.service.DiscoveryService; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | 11 | @Slf4j 12 | @RunWith(SpringRunner.class) 13 | @SpringBootTest(classes = ConsulIsonaApplication.class) 14 | public class ConsulIsonaApplicationTests { 15 | 16 | @Autowired 17 | private DiscoveryService discoveryService; 18 | 19 | @Test 20 | public void getAllServices() { 21 | discoveryService.getServices().forEach(service -> log.info(service)); 22 | } 23 | 24 | @Test 25 | public void getAllServiceDetails() { 26 | discoveryService.getServices().forEach(service -> { 27 | discoveryService.getServiceInstances(service).forEach(instance -> { 28 | log.info(""); 29 | }); 30 | }); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /isona-example-consul/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=isona-consul-eureka 2 | 3 | server.port=10020 4 | 5 | spring.cloud.consul.host=10.19.90.19 6 | spring.cloud.consul.port=8500 7 | spring.cloud.consul.discovery.health-check-path=${management.context-path}/health 8 | 9 | management.context-path=/isona 10 | management.security.enabled=false -------------------------------------------------------------------------------- /isona-example-eureka/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.spring4all 7 | isona-example-eureka 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | isona-example-eureka 12 | Go ! Go ! Go ! 13 | 14 | 15 | 16 | 17 | com.spring4all 18 | isona-web 19 | 0.0.1-SNAPSHOT 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-eureka 25 | 26 | 27 | 28 | 29 | com.h2database 30 | h2 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-devtools 36 | runtime 37 | 38 | 39 | org.projectlombok 40 | lombok 41 | true 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-test 46 | test 47 | 48 | 49 | 50 | 51 | 52 | UTF-8 53 | UTF-8 54 | 1.8 55 | Edgware.SR1 56 | 1.5.10.RELEASE 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.cloud 63 | spring-cloud-dependencies 64 | ${spring-cloud.version} 65 | pom 66 | import 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-parent 71 | ${spring-boot.version} 72 | pom 73 | import 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | org.springframework.boot 82 | spring-boot-maven-plugin 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /isona-example-eureka/src/main/java/com/spring4all/isona/example/eureka/EurekaIsonaApplication.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.example.eureka; 2 | 3 | import com.spring4all.isona.autoconfig.eureka.EnableEurekaIsonaServer; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @EnableEurekaIsonaServer 8 | @SpringBootApplication 9 | public class EurekaIsonaApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(EurekaIsonaApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /isona-example-eureka/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=isona-example-eureka 2 | server.port=10010 3 | 4 | eureka.client.serviceUrl.defaultZone=http://eureka.didispace.com/eureka/ 5 | 6 | isona.service.management-context-path=/isona 7 | 8 | management.context-path=/isona 9 | management.security.enabled=false 10 | -------------------------------------------------------------------------------- /isona-example-eureka/src/test/java/com/spring4all/isona/example/eureka/EurekaIsonaApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.example.eureka; 2 | 3 | import com.spring4all.isona.service.DiscoveryService; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | 11 | @Slf4j 12 | @RunWith(SpringRunner.class) 13 | @SpringBootTest(classes = EurekaIsonaApplication.class) 14 | public class EurekaIsonaApplicationTests { 15 | 16 | @Autowired 17 | private DiscoveryService discoveryService; 18 | 19 | @Test 20 | public void getAllServices() { 21 | discoveryService.getServices().forEach(service -> log.info(service)); 22 | } 23 | 24 | @Test 25 | public void getAllServiceDetails() { 26 | discoveryService.getServices().forEach(service -> { 27 | discoveryService.getServiceInstances(service).forEach(instance -> { 28 | log.info(""); 29 | }); 30 | }); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /isona-parent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.spring4all 7 | isona-parent 8 | 0.0.1-SNAPSHOT 9 | pom 10 | 11 | isona-parent 12 | Go ! Go ! Go ! 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | Edgware.SR1 19 | 1.5.10.RELEASE 20 | 1.2.44 21 | 3.9.1 22 | 23 | 24 | 25 | 26 | 27 | org.springframework.cloud 28 | spring-cloud-dependencies 29 | ${spring-cloud.version} 30 | pom 31 | import 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-parent 36 | ${spring-boot.version} 37 | pom 38 | import 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.projectlombok 46 | lombok 47 | true 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-test 52 | test 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-maven-plugin 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /isona-storage/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.spring4all 7 | isona-storage 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | isona-storage 12 | Go ! Go ! Go ! 13 | 14 | 15 | com.spring4all 16 | isona-parent 17 | 0.0.1-SNAPSHOT 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | 27 | org.projectlombok 28 | lombok 29 | true 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | test 35 | 36 | 37 | 38 | 39 | 40 | UTF-8 41 | UTF-8 42 | 1.8 43 | Edgware.SR1 44 | 1.5.10.RELEASE 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-dependencies 52 | ${spring-cloud.version} 53 | pom 54 | import 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-parent 59 | ${spring-boot.version} 60 | pom 61 | import 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-maven-plugin 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /isona-storage/src/main/java/com/spring4all/isona/storage/domain/IsonaInstance.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.storage.domain; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.GeneratedValue; 7 | import javax.persistence.GenerationType; 8 | import javax.persistence.Id; 9 | import java.util.Date; 10 | 11 | @Data 12 | @Entity 13 | public class IsonaInstance { 14 | 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.IDENTITY) 17 | private Long id; 18 | 19 | /** 20 | * 服务名 21 | **/ 22 | private String serviceName; 23 | /** 24 | * 服务ip 25 | **/ 26 | private String serviceAddress; 27 | /** 28 | * 服务端口 29 | **/ 30 | private Integer servicePort; 31 | /** 32 | * 服务所在节点名 33 | **/ 34 | private String node; 35 | /** 36 | * 服务所在节点ip地址 37 | **/ 38 | private String address; 39 | /** 40 | * 当前服务状态 41 | **/ 42 | private IsonaInstanceStatus status; 43 | /** 44 | * 元数据,用key=value的方式记录,多个用英文逗号分割 45 | **/ 46 | private String metadata; 47 | 48 | /** 49 | * git提交id 50 | **/ 51 | private String commitId; 52 | /** 53 | * git分支 54 | **/ 55 | private String branch; 56 | /** 57 | * git提交时间 58 | **/ 59 | private Long commitTime; 60 | /** 61 | * 应用构建时间 62 | **/ 63 | private Long buildTime; 64 | /** 65 | * 版本信息 66 | **/ 67 | private String version; 68 | 69 | /** 70 | * 内存分配总数 71 | **/ 72 | private Integer memTotal = 0; 73 | /** 74 | * 已经使用内存 75 | **/ 76 | private Integer memUsed = 0; 77 | /** 78 | * 使用内存百分比 79 | **/ 80 | private Integer memPercent = 0; 81 | 82 | /** 83 | * 当前线程数 84 | **/ 85 | private Integer threads = 0; 86 | /** 87 | * 当前守护线程 88 | **/ 89 | private Integer threadsDaemon = 0; 90 | /** 91 | * 历史线程峰值 92 | **/ 93 | private Integer threadsPeak = 0; 94 | 95 | /** 96 | * 最近更新时间 97 | **/ 98 | private Date updateTime; 99 | 100 | 101 | } 102 | 103 | 104 | -------------------------------------------------------------------------------- /isona-storage/src/main/java/com/spring4all/isona/storage/domain/IsonaInstanceRepo.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.storage.domain; 2 | 3 | 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | public interface IsonaInstanceRepo extends CrudRepository { 7 | 8 | IsonaInstance findByServiceNameAndServiceAddressAndServicePort(String serviceName, String serviceAddress, Integer servicePort); 9 | 10 | Integer deleteByStatusEquals(String status); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /isona-storage/src/main/java/com/spring4all/isona/storage/domain/IsonaInstanceStatus.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.storage.domain; 2 | 3 | public enum IsonaInstanceStatus { 4 | UP, 5 | DOWN, 6 | WARNING, 7 | UNKNOWN 8 | } -------------------------------------------------------------------------------- /isona-storage/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringForAll/isona/69798c35ff6b16e76deb98a74726290ec788bc31/isona-storage/src/main/resources/application.properties -------------------------------------------------------------------------------- /isona-storage/src/test/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringForAll/isona/69798c35ff6b16e76deb98a74726290ec788bc31/isona-storage/src/test/resources/application.properties -------------------------------------------------------------------------------- /isona-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.spring4all 7 | isona-web 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | isona-web 12 | Go ! Go ! Go ! 13 | 14 | 15 | com.spring4all 16 | isona-parent 17 | 0.0.1-SNAPSHOT 18 | 19 | 20 | 21 | 22 | 23 | com.spring4all 24 | isona-core 25 | 0.0.1-SNAPSHOT 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-actuator 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-thymeleaf 40 | 41 | 42 | net.sourceforge.nekohtml 43 | nekohtml 44 | 1.9.22 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-devtools 50 | runtime 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-maven-plugin 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /isona-web/src/main/java/com/spring4all/isona/autoconfig/CommonAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.autoconfig; 2 | 3 | import com.spring4all.isona.utils.HttpUtils; 4 | import okhttp3.OkHttpClient; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.context.annotation.*; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | @Configuration 11 | @PropertySource("classpath:thymeleaf.properties") 12 | @ComponentScan("com.spring4all.isona.web") 13 | public class CommonAutoConfig { 14 | 15 | @Bean 16 | public OkHttpClient okHttpClient() { 17 | OkHttpClient client = new OkHttpClient.Builder() 18 | .connectTimeout(1, TimeUnit.SECONDS) 19 | .writeTimeout(2, TimeUnit.SECONDS) 20 | .readTimeout(5, TimeUnit.SECONDS) 21 | .build(); 22 | return client; 23 | } 24 | 25 | @Bean 26 | public HttpUtils httpUtils(OkHttpClient okHttpClient) { 27 | return new HttpUtils(okHttpClient); 28 | } 29 | 30 | @Bean 31 | public IsonaProperties isonaProperties() { 32 | return new IsonaProperties(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /isona-web/src/main/java/com/spring4all/isona/autoconfig/consul/ConsulIsonaAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.autoconfig.consul; 2 | 3 | import com.spring4all.isona.service.DiscoveryService; 4 | import com.spring4all.isona.service.impl.ConsulDiscoveryService; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | public class ConsulIsonaAutoConfig { 11 | 12 | @Bean 13 | @ConditionalOnMissingBean 14 | public DiscoveryService discoveryService() { 15 | return new ConsulDiscoveryService(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /isona-web/src/main/java/com/spring4all/isona/autoconfig/consul/EnableConsulIsonaServer.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.autoconfig.consul; 2 | 3 | import com.spring4all.isona.autoconfig.CommonAutoConfig; 4 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 5 | import org.springframework.context.annotation.Import; 6 | 7 | import java.lang.annotation.*; 8 | 9 | @Target(ElementType.TYPE) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | @Inherited 13 | @Import({CommonAutoConfig.class, ConsulIsonaAutoConfig.class}) 14 | @EnableDiscoveryClient 15 | public @interface EnableConsulIsonaServer { 16 | } 17 | -------------------------------------------------------------------------------- /isona-web/src/main/java/com/spring4all/isona/autoconfig/eureka/EnableEurekaIsonaServer.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.autoconfig.eureka; 2 | 3 | import com.spring4all.isona.autoconfig.CommonAutoConfig; 4 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 5 | import org.springframework.context.annotation.Import; 6 | 7 | import java.lang.annotation.*; 8 | 9 | @Target(ElementType.TYPE) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | @Inherited 13 | @Import({CommonAutoConfig.class, EurekaIsonaAutoConfig.class}) 14 | @EnableDiscoveryClient 15 | public @interface EnableEurekaIsonaServer { 16 | } 17 | -------------------------------------------------------------------------------- /isona-web/src/main/java/com/spring4all/isona/autoconfig/eureka/EurekaIsonaAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.autoconfig.eureka; 2 | 3 | import com.spring4all.isona.service.DiscoveryService; 4 | import com.spring4all.isona.service.impl.EurekaDiscoveryService; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | 11 | @Configuration 12 | public class EurekaIsonaAutoConfig { 13 | 14 | @Bean 15 | @ConditionalOnMissingBean 16 | public DiscoveryService discoveryService() { 17 | return new EurekaDiscoveryService(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /isona-web/src/main/java/com/spring4all/isona/dto/InstanceDto.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class InstanceDto { 7 | } 8 | -------------------------------------------------------------------------------- /isona-web/src/main/java/com/spring4all/isona/dto/ServiceDto.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ServiceDto { 7 | } 8 | -------------------------------------------------------------------------------- /isona-web/src/main/java/com/spring4all/isona/task/InstanceSnapshotTask.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.task; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | @Slf4j 6 | public class InstanceSnapshotTask { 7 | 8 | 9 | 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /isona-web/src/main/java/com/spring4all/isona/web/IndexController.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.web; 2 | 3 | 4 | import com.spring4all.isona.service.DiscoveryService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.ui.ModelMap; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | 10 | @Controller 11 | public class IndexController { 12 | 13 | @Autowired 14 | private DiscoveryService discoveryService; 15 | 16 | @GetMapping("/") 17 | public String dashboard(ModelMap map) { 18 | // 暂时不设专门的dashboard,直接跳转到服务详情页面,待功能更多之后设置 19 | return "redirect://serviceList"; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /isona-web/src/main/java/com/spring4all/isona/web/ServiceViewController.java: -------------------------------------------------------------------------------- 1 | package com.spring4all.isona.web; 2 | 3 | 4 | import com.spring4all.isona.service.DiscoveryService; 5 | import com.spring4all.isona.storage.domain.IsonaInstance; 6 | import com.spring4all.isona.storage.domain.IsonaInstanceStatus; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.ui.ModelMap; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | 12 | import java.util.List; 13 | 14 | @Controller 15 | public class ServiceViewController { 16 | 17 | @Autowired 18 | private DiscoveryService discoveryService; 19 | 20 | @GetMapping("/serviceList") 21 | public String services(ModelMap map) { 22 | List serviceList = discoveryService.getAllServiceInstances(); 23 | map.addAttribute("serviceList", serviceList); 24 | 25 | int instancesNum = serviceList.size(); 26 | int servicesNum = discoveryService.getServices().size(); 27 | int notUpInstancesNum = 0; 28 | int memDangerInstancesNum = 0; 29 | for(IsonaInstance instance : serviceList) { 30 | if(instance.getStatus() != IsonaInstanceStatus.UP) { 31 | notUpInstancesNum ++; 32 | } 33 | if(instance.getMemPercent() >= 80) { 34 | memDangerInstancesNum ++; 35 | } 36 | } 37 | 38 | map.addAttribute("instancesNum", instancesNum); 39 | map.addAttribute("servicesNum", servicesNum); 40 | map.addAttribute("notUpInstancesNum", notUpInstancesNum); 41 | map.addAttribute("memDangerInstancesNum", memDangerInstancesNum); 42 | 43 | return "service/service_list"; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /isona-web/src/main/resources/static/assets/admin-tools/admin-forms/js/additional-methods.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery Validation Plugin - v1.12.0 - 4/1/2014 2 | * http://jqueryvalidation.org/ 3 | * Copyright (c) 2014 Jörn Zaefferer; Licensed MIT */ 4 | !function(){function a(a){return a.replace(/<.[^<>]*?>/g," ").replace(/ | /gi," ").replace(/[.(),;:!?%#$'\"_+=\/\-“”’]*/g,"")}jQuery.validator.addMethod("maxWords",function(b,c,d){return this.optional(c)||a(b).match(/\b\w+\b/g).length<=d},jQuery.validator.format("Please enter {0} words or less.")),jQuery.validator.addMethod("minWords",function(b,c,d){return this.optional(c)||a(b).match(/\b\w+\b/g).length>=d},jQuery.validator.format("Please enter at least {0} words.")),jQuery.validator.addMethod("rangeWords",function(b,c,d){var e=a(b),f=/\b\w+\b/g;return this.optional(c)||e.match(f).length>=d[0]&&e.match(f).length<=d[1]},jQuery.validator.format("Please enter between {0} and {1} words."))}(),jQuery.validator.addMethod("accept",function(a,b,c){var d,e,f="string"==typeof c?c.replace(/\s/g,"").replace(/,/g,"|"):"image/*",g=this.optional(b);if(g)return g;if("file"===jQuery(b).attr("type")&&(f=f.replace(/\*/g,".*"),b.files&&b.files.length))for(d=0;dc;c++)d=h-c,e=f.substring(c,c+1),g+=d*e;return g%11===0},"Please specify a valid bank account number"),jQuery.validator.addMethod("bankorgiroaccountNL",function(a,b){return this.optional(b)||$.validator.methods.bankaccountNL.call(this,a,b)||$.validator.methods.giroaccountNL.call(this,a,b)},"Please specify a valid bank or giro account number"),jQuery.validator.addMethod("bic",function(a,b){return this.optional(b)||/^([A-Z]{6}[A-Z2-9][A-NP-Z1-2])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/.test(a)},"Please specify a valid BIC code"),jQuery.validator.addMethod("cifES",function(a){"use strict";var b,c,d,e,f,g,h=[];if(a=a.toUpperCase(),!a.match("((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)"))return!1;for(d=0;9>d;d++)h[d]=parseInt(a.charAt(d),10);for(c=h[2]+h[4]+h[6],e=1;8>e;e+=2)f=(2*h[e]).toString(),g=f.charAt(1),c+=parseInt(f.charAt(0),10)+(""===g?0:parseInt(g,10));return/^[ABCDEFGHJNPQRSUVW]{1}/.test(a)?(c+="",b=10-parseInt(c.charAt(c.length-1),10),a+=b,h[8].toString()===String.fromCharCode(64+b)||h[8].toString()===a.charAt(a.length-1)):!1},"Please specify a valid CIF number."),jQuery.validator.addMethod("creditcardtypes",function(a,b,c){if(/[^0-9\-]+/.test(a))return!1;a=a.replace(/\D/g,"");var d=0;return c.mastercard&&(d|=1),c.visa&&(d|=2),c.amex&&(d|=4),c.dinersclub&&(d|=8),c.enroute&&(d|=16),c.discover&&(d|=32),c.jcb&&(d|=64),c.unknown&&(d|=128),c.all&&(d=255),1&d&&/^(5[12345])/.test(a)?16===a.length:2&d&&/^(4)/.test(a)?16===a.length:4&d&&/^(3[47])/.test(a)?15===a.length:8&d&&/^(3(0[012345]|[68]))/.test(a)?14===a.length:16&d&&/^(2(014|149))/.test(a)?15===a.length:32&d&&/^(6011)/.test(a)?16===a.length:64&d&&/^(3)/.test(a)?16===a.length:64&d&&/^(2131|1800)/.test(a)?15===a.length:128&d?!0:!1},"Please enter a valid credit card number."),jQuery.validator.addMethod("currency",function(a,b,c){var d,e="string"==typeof c,f=e?c:c[0],g=e?!0:c[1];return f=f.replace(/,/g,""),f=g?f+"]":f+"]?",d="^["+f+"([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$",d=new RegExp(d),this.optional(b)||d.test(a)},"Please specify a valid currency"),jQuery.validator.addMethod("dateITA",function(a,b){var c,d,e,f,g,h=!1,i=/^\d{1,2}\/\d{1,2}\/\d{4}$/;return i.test(a)?(c=a.split("/"),d=parseInt(c[0],10),e=parseInt(c[1],10),f=parseInt(c[2],10),g=new Date(f,e-1,d,12,0,0,0),h=g.getFullYear()===f&&g.getMonth()===e-1&&g.getDate()===d?!0:!1):h=!1,this.optional(b)||h},"Please enter a correct date"),jQuery.validator.addMethod("dateNL",function(a,b){return this.optional(b)||/^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test(a)},"Please enter a correct date"),jQuery.validator.addMethod("extension",function(a,b,c){return c="string"==typeof c?c.replace(/,/g,"|"):"png|jpe?g|gif",this.optional(b)||a.match(new RegExp(".("+c+")$","i"))},jQuery.validator.format("Please enter a value with a valid extension.")),jQuery.validator.addMethod("giroaccountNL",function(a,b){return this.optional(b)||/^[0-9]{1,7}$/.test(a)},"Please specify a valid giro account number"),jQuery.validator.addMethod("iban",function(a,b){if(this.optional(b))return!0;var c,d,e,f,g,h,i,j,k,l=a.replace(/ /g,"").toUpperCase(),m="",n=!0,o="",p="";if(!/^([a-zA-Z0-9]{4} ){2,8}[a-zA-Z0-9]{1,4}|[a-zA-Z0-9]{12,34}$/.test(l))return!1;if(c=l.substring(0,2),h={AL:"\\d{8}[\\dA-Z]{16}",AD:"\\d{8}[\\dA-Z]{12}",AT:"\\d{16}",AZ:"[\\dA-Z]{4}\\d{20}",BE:"\\d{12}",BH:"[A-Z]{4}[\\dA-Z]{14}",BA:"\\d{16}",BR:"\\d{23}[A-Z][\\dA-Z]",BG:"[A-Z]{4}\\d{6}[\\dA-Z]{8}",CR:"\\d{17}",HR:"\\d{17}",CY:"\\d{8}[\\dA-Z]{16}",CZ:"\\d{20}",DK:"\\d{14}",DO:"[A-Z]{4}\\d{20}",EE:"\\d{16}",FO:"\\d{14}",FI:"\\d{14}",FR:"\\d{10}[\\dA-Z]{11}\\d{2}",GE:"[\\dA-Z]{2}\\d{16}",DE:"\\d{18}",GI:"[A-Z]{4}[\\dA-Z]{15}",GR:"\\d{7}[\\dA-Z]{16}",GL:"\\d{14}",GT:"[\\dA-Z]{4}[\\dA-Z]{20}",HU:"\\d{24}",IS:"\\d{22}",IE:"[\\dA-Z]{4}\\d{14}",IL:"\\d{19}",IT:"[A-Z]\\d{10}[\\dA-Z]{12}",KZ:"\\d{3}[\\dA-Z]{13}",KW:"[A-Z]{4}[\\dA-Z]{22}",LV:"[A-Z]{4}[\\dA-Z]{13}",LB:"\\d{4}[\\dA-Z]{20}",LI:"\\d{5}[\\dA-Z]{12}",LT:"\\d{16}",LU:"\\d{3}[\\dA-Z]{13}",MK:"\\d{3}[\\dA-Z]{10}\\d{2}",MT:"[A-Z]{4}\\d{5}[\\dA-Z]{18}",MR:"\\d{23}",MU:"[A-Z]{4}\\d{19}[A-Z]{3}",MC:"\\d{10}[\\dA-Z]{11}\\d{2}",MD:"[\\dA-Z]{2}\\d{18}",ME:"\\d{18}",NL:"[A-Z]{4}\\d{10}",NO:"\\d{11}",PK:"[\\dA-Z]{4}\\d{16}",PS:"[\\dA-Z]{4}\\d{21}",PL:"\\d{24}",PT:"\\d{21}",RO:"[A-Z]{4}[\\dA-Z]{16}",SM:"[A-Z]\\d{10}[\\dA-Z]{12}",SA:"\\d{2}[\\dA-Z]{18}",RS:"\\d{18}",SK:"\\d{20}",SI:"\\d{15}",ES:"\\d{20}",SE:"\\d{20}",CH:"\\d{5}[\\dA-Z]{12}",TN:"\\d{20}",TR:"\\d{5}[\\dA-Z]{17}",AE:"\\d{3}\\d{16}",GB:"[A-Z]{4}\\d{14}",VG:"[\\dA-Z]{4}\\d{16}"},g=h[c],"undefined"!=typeof g&&(i=new RegExp("^[A-Z]{2}\\d{2}"+g+"$",""),!i.test(l)))return!1;for(d=l.substring(4,l.length)+l.substring(0,4),j=0;j9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/)},"Please specify a valid mobile number"),jQuery.validator.addMethod("nieES",function(a){"use strict";return a=a.toUpperCase(),a.match("((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)")?/^[T]{1}/.test(a)?a[8]===/^[T]{1}[A-Z0-9]{8}$/.test(a):/^[XYZ]{1}/.test(a)?a[8]==="TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.replace("X","0").replace("Y","1").replace("Z","2").substring(0,8)%23):!1:!1},"Please specify a valid NIE number."),jQuery.validator.addMethod("nifES",function(a){"use strict";return a=a.toUpperCase(),a.match("((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)")?/^[0-9]{8}[A-Z]{1}$/.test(a)?"TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,0)%23)===a.charAt(8):/^[KLM]{1}/.test(a)?a[8]===String.fromCharCode(64):!1:!1},"Please specify a valid NIF number."),jQuery.validator.addMethod("nowhitespace",function(a,b){return this.optional(b)||/^\S+$/i.test(a)},"No white space please"),jQuery.validator.addMethod("pattern",function(a,b,c){return this.optional(b)?!0:("string"==typeof c&&(c=new RegExp(c)),c.test(a))},"Invalid format."),jQuery.validator.addMethod("phoneNL",function(a,b){return this.optional(b)||/^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test(a)},"Please specify a valid phone number."),jQuery.validator.addMethod("phoneUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/)},"Please specify a valid phone number"),jQuery.validator.addMethod("phoneUS",function(a,b){return a=a.replace(/\s+/g,""),this.optional(b)||a.length>9&&a.match(/^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/)},"Please specify a valid phone number"),jQuery.validator.addMethod("phonesUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/)},"Please specify a valid uk phone number"),jQuery.validator.addMethod("postalcodeNL",function(a,b){return this.optional(b)||/^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test(a)},"Please specify a valid postal code"),jQuery.validator.addMethod("postcodeUK",function(a,b){return this.optional(b)||/^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test(a)},"Please specify a valid UK postcode"),jQuery.validator.addMethod("require_from_group",function(a,b,c){var d=$(c[1],b.form),e=d.eq(0),f=e.data("valid_req_grp")?e.data("valid_req_grp"):$.extend({},this),g=d.filter(function(){return f.elementValue(this)}).length>=c[0];return e.data("valid_req_grp",f),$(b).data("being_validated")||(d.data("being_validated",!0),d.each(function(){f.element(this)}),d.data("being_validated",!1)),g},jQuery.validator.format("Please fill at least {0} of these fields.")),jQuery.validator.addMethod("skip_or_fill_minimum",function(a,b,c){var d=$(c[1],b.form),e=d.eq(0),f=e.data("valid_skip")?e.data("valid_skip"):$.extend({},this),g=d.filter(function(){return f.elementValue(this)}).length,h=0===g||g>=c[0];return e.data("valid_skip",f),$(b).data("being_validated")||(d.data("being_validated",!0),d.each(function(){f.element(this)}),d.data("being_validated",!1)),h},jQuery.validator.format("Please either skip these fields or fill at least {0} of them.")),jQuery.validator.addMethod("strippedminlength",function(a,b,c){return jQuery(a).text().length>=c},jQuery.validator.format("Please enter at least {0} characters")),jQuery.validator.addMethod("time",function(a,b){return this.optional(b)||/^([01]\d|2[0-3])(:[0-5]\d){1,2}$/.test(a)},"Please enter a valid time, between 00:00 and 23:59"),jQuery.validator.addMethod("time12h",function(a,b){return this.optional(b)||/^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test(a)},"Please enter a valid time in 12-hour am/pm format"),jQuery.validator.addMethod("url2",function(a,b){return this.optional(b)||/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(a)},jQuery.validator.messages.url),jQuery.validator.addMethod("vinUS",function(a){if(17!==a.length)return!1;var b,c,d,e,f,g,h=["A","B","C","D","E","F","G","H","J","K","L","M","N","P","R","S","T","U","V","W","X","Y","Z"],i=[1,2,3,4,5,6,7,8,1,2,3,4,5,7,9,2,3,4,5,6,7,8,9],j=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],k=0;for(b=0;17>b;b++){if(e=j[b],d=a.slice(b,b+1),8===b&&(g=d),isNaN(d)){for(c=0;c');if(k.data("stepper")){return}p.insertAfter(k);k.appendTo(p);k.stepper=(function(){return{limit:i,decimalRound:l,onStep:function(q){m.onStep=q},onWheel:function(q){m.onWheel=q},onArrow:function(q){m.onArrow=q},onButton:function(q){m.onButton=q},onKeyUp:function(q){m.onKeyUp=q}}})();k.data("stepper",k.stepper);if(m.ui){var f=a('
').appendTo(p),j=a(''+m.incrementButton+"").appendTo(f),o=a(''+m.decrementButton+"").appendTo(f);p.css({"margin-top":k.css("margin-top"),"margin-left":k.css("margin-left"),"margin-bottom":k.css("margin-bottom"),"margin-right":f.outerWidth()+parseInt(k.css("margin-right"))});k.css("margin",0);var e;j.mousedown(function(q){q.preventDefault();var r=n(m.arrowStep);d("Button",[r,true])});o.mousedown(function(q){q.preventDefault();var r=n(-m.arrowStep);d("Button",[r,false])});a(document).mouseup(function(){clearInterval(e)})}if(m.allowWheel){p.bind("DOMMouseScroll",h);p.bind("mousewheel",h)}p.keydown(function(r){var q=r.which,s=k.val();if(m.allowArrows){switch(q){case 38:s=n(m.arrowStep);d("Arrow",[s,true]);break;case 40:s=n(-m.arrowStep);d("Arrow",[s,false]);break}}if((q<37&&q>40)||(q>57&&q<91)||(q>105&&q!=110&&q!=190)){r.preventDefault()}if(m.type=="float"&&a.inArray(q,[110,190])!=-1&&s.indexOf(".")!=-1){r.preventDefault()}}).keyup(function(q){d("KeyUp",[k.val()])});function h(r){r.preventDefault();var t,q=r.originalEvent;if(q.wheelDelta){t=q.wheelDelta/120}else{if(q.detail){t=-q.detail/3}}if(t){if(m.preventWheelAcceleration){t=t<0?-1:1}var s=n(m.wheelStep*t);d("Wheel",[s,t>0])}}function n(q){if(!k.val()){k.val(0)}var s=m.type=="int"?parseInt:parseFloat,r=i(s(k.val())+q);k.val(r);d("Step",[r,q>0]);return r}function d(r,q){var s=m["on"+r];if(typeof s=="function"){s.apply(k,q)}}function i(r){var s=m.limit[0],q=m.limit[1];if(s!==null&&rq){r=q}}return l(r)}function l(r,q){if(typeof q=="undefined"){q=m.floatPrecission}var s=Math.pow(10,q);r=Math.round(r*s)/s;return r}})}})(jQuery); -------------------------------------------------------------------------------- /isona-web/src/main/resources/static/assets/admin-tools/admin-forms/js/jquery.steps.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Steps v1.1.0 - 09/04/2014 3 | * Copyright (c) 2014 Rafael Staib (http://www.jquery-steps.com) 4 | * Licensed under MIT http://www.opensource.org/licenses/MIT 5 | */ 6 | !function(a,b){function c(a,b){o(a).push(b)}function d(d,e,f){var g=d.children(e.headerTag),h=d.children(e.bodyTag);g.length>h.length?R(Z,"contents"):g.length *");e.removeData("loaded").removeData("mode").removeData("url"),e.removeAttr("id").removeAttr("role").removeAttr("tabindex").removeAttr("class").removeAttr("style")._removeAria("labelledby")._removeAria("hidden"),b.find(".content > [data-mode='async'],.content > [data-mode='iframe']").empty();var f=a('<{0} class="{1}">'.format(b.get(0).tagName,b.attr("class"))),g=b._id();return null!=g&&""!==g&&f._id(g),f.html(b.find(".content").html()),b.after(f),b.remove(),f}function h(a,b){var c=a.find(".steps li").eq(b.currentIndex);a.triggerHandler("finishing",[b.currentIndex])?(c.addClass("done").removeClass("error"),a.triggerHandler("finished",[b.currentIndex])):c.addClass("error")}function i(a){var b=a.data("eventNamespace");return null==b&&(b="."+q(a),a.data("eventNamespace",b)),b}function j(a,b){var c=q(a);return a.find("#"+c+V+b)}function k(a,b){var c=q(a);return a.find("#"+c+W+b)}function l(a,b){var c=q(a);return a.find("#"+c+X+b)}function m(a){return a.data("options")}function n(a){return a.data("state")}function o(a){return a.data("steps")}function p(a,b){var c=o(a);return(0>b||b>=c.length)&&R(Y),c[b]}function q(a){var b=a.data("uid");return null==b&&(b=a._id(),null==b&&(b="steps-uid-".concat(T),a._id(b)),T++,a.data("uid",b)),b}function r(a,c){if(S("enumType",a),S("keyOrValue",c),"string"==typeof c){var d=a[c];return d===b&&R("The enum key '{0}' does not exist.",c),d}if("number"==typeof c){for(var e in a)if(a[e]===c)return c;R("Invalid enum value '{0}'.",c)}else R("Invalid key or value type.")}function s(a,b,c){return B(a,b,c,v(c,1))}function t(a,b,c){return B(a,b,c,f(c,1))}function u(a,b,c,d){if((0>d||d>=c.stepCount)&&R(Y),!(b.forceMoveForward&&de||e>d.stepCount)&&R(Y),f=a.extend({},bb,f),y(b,e,f),d.currentIndex!==d.stepCount&&d.currentIndex>=e&&(d.currentIndex++,O(b,c,d)),d.stepCount++;var g=b.find(".content"),h=a("<{0}>{1}".format(c.headerTag,f.title)),i=a("<{0}>".format(c.bodyTag));return(null==f.contentMode||f.contentMode===$.html)&&i.html(f.content),0===e?g.prepend(i).prepend(h):k(b,e-1).after(i).after(h),K(b,d,i,e),N(b,c,d,h,e),F(b,c,d,e),e===d.currentIndex&&E(b,c,d),D(b,c,d),b}function y(a,b,c){o(a).splice(b,0,c)}function z(b){var c=a(this),d=m(c),e=n(c);if(d.suppressPaginationOnFocus&&c.find(":focus").is(":input"))return b.preventDefault(),!1;var f={left:37,right:39};b.keyCode===f.left?(b.preventDefault(),t(c,d,e)):b.keyCode===f.right&&(b.preventDefault(),s(c,d,e))}function A(b,c,d){if(d.stepCount>0){var e=d.currentIndex,f=p(b,e);if(!c.enableContentCache||!f.contentLoaded)switch(r($,f.contentMode)){case $.iframe:b.find(".content > .body").eq(d.currentIndex).empty().html('