From 4ed15bda06c8e9a407c12d527b8224737ba339d5 Mon Sep 17 00:00:00 2001 From: HuabingZhao Date: Sat, 13 Aug 2016 14:10:39 +0800 Subject: [PATCH] Initial code import Change-Id: I839b84e5200aedece6c33deb16bec1bf9dbb61df Signed-off-by: HuabingZhao --- README.md | 1 + apiroute/apiroute-service/pom.xml | 160 + .../src/main/java/org/openo/msb/ApiRouteApp.java | 550 ++ .../main/java/org/openo/msb/ApiRouteAppConfig.java | 111 + .../main/java/org/openo/msb/ConsulClientApp.java | 452 ++ .../main/java/org/openo/msb/api/ApiRouteInfo.java | 136 + .../main/java/org/openo/msb/api/ConsulInfo.java | 38 + .../org/openo/msb/api/CustomDateSerializer.java | 39 + .../java/org/openo/msb/api/CustomRouteInfo.java | 102 + .../main/java/org/openo/msb/api/DiscoverInfo.java | 49 + .../main/java/org/openo/msb/api/IuiRouteInfo.java | 102 + .../main/java/org/openo/msb/api/MetricsInfo.java | 191 + .../org/openo/msb/api/MicroServiceFullInfo.java | 44 + .../java/org/openo/msb/api/MicroServiceInfo.java | 38 + .../src/main/java/org/openo/msb/api/Node.java | 75 + .../src/main/java/org/openo/msb/api/NodeInfo.java | 74 + .../main/java/org/openo/msb/api/RouteServer.java | 68 + .../src/main/java/org/openo/msb/api/Service.java | 104 + .../java/org/openo/msb/api/ServiceAccessInfo.java | 88 + .../ExtendedInternalServerErrorException.java | 28 + .../api/exception/ExtendedNotFoundException.java | 28 + .../exception/ExtendedNotSupportedException.java | 27 + .../org/openo/msb/health/ApiRouteHealthCheck.java | 35 + .../org/openo/msb/resources/ApiRouteResource.java | 233 + .../openo/msb/resources/CustomRouteResource.java | 152 + .../org/openo/msb/resources/IuiRouteResource.java | 155 + .../org/openo/msb/resources/MetricsResource.java | 49 + .../openo/msb/resources/MicroServiceResource.java | 247 + .../openo/msb/resources/ServiceAccessResource.java | 65 + .../openo/msb/wrapper/ApiRouteServiceWrapper.java | 653 +++ .../msb/wrapper/CustomRouteServiceWrapper.java | 474 ++ .../openo/msb/wrapper/IuiRouteServiceWrapper.java | 453 ++ .../openo/msb/wrapper/MetricsServiceWrapper.java | 92 + .../org/openo/msb/wrapper/MicroServiceWrapper.java | 536 ++ .../openo/msb/wrapper/ServiceAccessWrapper.java | 172 + .../openo/msb/wrapper/consul/CatalogClient.java | 285 + .../java/org/openo/msb/wrapper/consul/Consul.java | 299 + .../openo/msb/wrapper/consul/ConsulException.java | 42 + .../org/openo/msb/wrapper/consul/HealthClient.java | 247 + .../consul/async/ConsulResponseCallback.java | 42 + .../msb/wrapper/consul/cache/CatalogCache.java | 70 + .../msb/wrapper/consul/cache/ConsulCache.java | 230 + .../msb/wrapper/consul/cache/ConsulCache4Map.java | 258 + .../msb/wrapper/consul/cache/HealthCache.java | 70 + .../msb/wrapper/consul/cache/ServiceCache.java | 50 + .../msb/wrapper/consul/model/ConsulResponse.java | 80 + .../wrapper/consul/model/catalog/CatalogNode.java | 41 + .../consul/model/catalog/CatalogService.java | 52 + .../consul/model/catalog/ImmutableCatalogNode.java | 306 + .../model/catalog/ImmutableCatalogService.java | 626 +++ .../wrapper/consul/model/catalog/ServiceInfo.java | 63 + .../wrapper/consul/model/health/ImmutableNode.java | 266 + .../consul/model/health/ImmutableService.java | 478 ++ .../msb/wrapper/consul/model/health/Node.java | 35 + .../msb/wrapper/consul/model/health/Service.java | 48 + .../wrapper/consul/model/health/ServiceHealth.java | 73 + .../msb/wrapper/consul/option/CatalogOptions.java | 39 + .../msb/wrapper/consul/option/ConsistencyMode.java | 21 + .../consul/option/ImmutableCatalogOptions.java | 251 + .../consul/option/ImmutableQueryOptions.java | 531 ++ .../openo/msb/wrapper/consul/option/Options.java | 29 + .../msb/wrapper/consul/option/ParamAdder.java | 23 + .../msb/wrapper/consul/option/QueryOptions.java | 100 + .../consul/util/Base64EncodingDeserializer.java | 45 + .../openo/msb/wrapper/consul/util/ClientUtil.java | 246 + .../org/openo/msb/wrapper/consul/util/Jackson.java | 34 + .../consul/util/ObjectMapperContextResolver.java | 35 + .../wrapper/consul/util/SecondsDeserializer.java | 43 + .../msb/wrapper/consul/util/SecondsSerializer.java | 34 + .../consul/util/UnsignedLongDeserializer.java | 37 + .../IMicroServiceChangeListener.java | 33 + .../MicroServiceChangeListener.java | 306 + .../java/org/openo/msb/wrapper/util/FileUtil.java | 66 + .../openo/msb/wrapper/util/JacksonJsonUtil.java | 120 + .../java/org/openo/msb/wrapper/util/JedisUtil.java | 221 + .../org/openo/msb/wrapper/util/MetricsUtil.java | 24 + .../org/openo/msb/wrapper/util/MicroServiceDB.java | 423 ++ .../openo/msb/wrapper/util/MicroServiceUtil.java | 96 + .../org/openo/msb/wrapper/util/RegExpTestUtil.java | 88 + .../java/org/openo/msb/wrapper/util/RouteUtil.java | 135 + .../main/resources/api-doc/META-INF/MANIFEST.MF | 3 + .../src/main/resources/api-doc/WEB-INF/web.xml | 40 + .../src/main/resources/api-doc/css/reset.css | 125 + .../src/main/resources/api-doc/css/screen.css | 1256 +++++ .../src/main/resources/api-doc/css/typography.css | 26 + .../api-doc/fonts/droid-sans-v6-latin-700.eot | Bin 0 -> 22924 bytes .../api-doc/fonts/droid-sans-v6-latin-700.svg | 411 ++ .../api-doc/fonts/droid-sans-v6-latin-700.ttf | Bin 0 -> 40516 bytes .../api-doc/fonts/droid-sans-v6-latin-700.woff | Bin 0 -> 25992 bytes .../api-doc/fonts/droid-sans-v6-latin-700.woff2 | Bin 0 -> 11480 bytes .../api-doc/fonts/droid-sans-v6-latin-regular.eot | Bin 0 -> 22008 bytes .../api-doc/fonts/droid-sans-v6-latin-regular.svg | 403 ++ .../api-doc/fonts/droid-sans-v6-latin-regular.ttf | Bin 0 -> 39072 bytes .../api-doc/fonts/droid-sans-v6-latin-regular.woff | Bin 0 -> 24868 bytes .../fonts/droid-sans-v6-latin-regular.woff2 | Bin 0 -> 11304 bytes .../resources/api-doc/images/explorer_icons.png | Bin 0 -> 5763 bytes .../main/resources/api-doc/images/logo_small.png | Bin 0 -> 770 bytes .../resources/api-doc/images/pet_store_api.png | Bin 0 -> 824 bytes .../src/main/resources/api-doc/images/throbber.gif | Bin 0 -> 9257 bytes .../main/resources/api-doc/images/wordnik_api.png | Bin 0 -> 980 bytes .../src/main/resources/api-doc/index.html | 118 + .../iframeResizer.contentWindow.min.js | 10 + .../api-doc/js/iframeResizer/iframeResizer.min.js | 9 + .../src/main/resources/api-doc/lib/backbone-min.js | 15 + .../main/resources/api-doc/lib/handlebars-2.0.0.js | 28 + .../resources/api-doc/lib/highlight.7.3.pack.js | 1 + .../main/resources/api-doc/lib/jquery-1.8.0.min.js | 2 + .../resources/api-doc/lib/jquery.ba-bbq.min.js | 18 + .../resources/api-doc/lib/jquery.slideto.min.js | 1 + .../resources/api-doc/lib/jquery.wiggle.min.js | 8 + .../src/main/resources/api-doc/lib/marked.js | 1272 +++++ .../src/main/resources/api-doc/lib/shred.bundle.js | 2765 ++++++++++ .../main/resources/api-doc/lib/shred/content.js | 193 + .../main/resources/api-doc/lib/swagger-client.js | 3315 +++++++++++ .../main/resources/api-doc/lib/swagger-oauth.js | 279 + .../main/resources/api-doc/lib/underscore-min.js | 6 + .../src/main/resources/api-doc/o2c.html | 20 + .../src/main/resources/api-doc/swagger-ui.js | 2241 ++++++++ .../src/main/resources/api-doc/swagger-ui.min.js | 2 + .../apiroute-service/src/main/resources/banner.txt | 5 + .../main/resources/iui-metrics/css/animate.min.css | 6 + .../src/main/resources/iui-metrics/css/metrics.css | 156 + .../i18n/loadi18nApp_iui-metrics_view.js | 43 + .../i18n/msb-iui-metrics-i18n-en-US.properties | 82 + .../i18n/msb-iui-metrics-i18n-zh-CN.properties | 78 + .../iui-metrics/img/loading-spinner-grey.gif | Bin 0 -> 5203 bytes .../resources/iui-metrics/img/netnumenLogo.png | Bin 0 -> 5639 bytes .../main/resources/iui-metrics/img/throbber.gif | Bin 0 -> 9257 bytes .../src/main/resources/iui-metrics/index.html | 256 + .../src/main/resources/iui-metrics/js/avalon.js | 5819 ++++++++++++++++++++ .../iui-metrics/js/bootstrap/css/bootstrap-dt.css | 5804 +++++++++++++++++++ .../iui-metrics/js/bootstrap/css/bootstrap.min.css | 7 + .../iui-metrics/js/bootstrap/js/bootstrap.js | 1951 +++++++ .../iui-metrics/js/bootstrap/js/bootstrap.min.js | 6 + .../js/bootstrap/js/bootstrap2-typeahead.min.js | 21 + .../js/dataTables/dataTables.bootstrap.css | 308 ++ .../js/dataTables/dataTables.bootstrap.min.js | 8 + .../js/dataTables/jquery.dataTables.min.js | 160 + .../iui-metrics/js/echarts/echarts-all.js | 35 + .../js/fontAwesome/css/font-awesome.css | 2026 +++++++ .../js/fontAwesome/css/font-awesome.css.map | 7 + .../js/fontAwesome/css/font-awesome.min.css | 4 + .../js/fontAwesome/fonts/FontAwesome.otf | Bin 0 -> 106260 bytes .../js/fontAwesome/fonts/fontawesome-webfont.eot | Bin 0 -> 68875 bytes .../js/fontAwesome/fonts/fontawesome-webfont.svg | 640 +++ .../js/fontAwesome/fonts/fontawesome-webfont.ttf | Bin 0 -> 138204 bytes .../js/fontAwesome/fonts/fontawesome-webfont.woff | Bin 0 -> 81284 bytes .../js/fontAwesome/fonts/fontawesome-webfont.woff2 | Bin 0 -> 64464 bytes .../iui-metrics/js/fontAwesome/less/animated.less | 34 + .../js/fontAwesome/less/bordered-pulled.less | 25 + .../iui-metrics/js/fontAwesome/less/core.less | 12 + .../js/fontAwesome/less/fixed-width.less | 6 + .../js/fontAwesome/less/font-awesome.less | 17 + .../iui-metrics/js/fontAwesome/less/icons.less | 677 +++ .../iui-metrics/js/fontAwesome/less/larger.less | 13 + .../iui-metrics/js/fontAwesome/less/list.less | 19 + .../iui-metrics/js/fontAwesome/less/mixins.less | 26 + .../iui-metrics/js/fontAwesome/less/path.less | 15 + .../js/fontAwesome/less/rotated-flipped.less | 20 + .../iui-metrics/js/fontAwesome/less/stacked.less | 20 + .../iui-metrics/js/fontAwesome/less/variables.less | 688 +++ .../iui-metrics/js/fontAwesome/scss/_animated.scss | 34 + .../js/fontAwesome/scss/_bordered-pulled.scss | 25 + .../iui-metrics/js/fontAwesome/scss/_core.scss | 12 + .../js/fontAwesome/scss/_fixed-width.scss | 6 + .../iui-metrics/js/fontAwesome/scss/_icons.scss | 677 +++ .../iui-metrics/js/fontAwesome/scss/_larger.scss | 13 + .../iui-metrics/js/fontAwesome/scss/_list.scss | 19 + .../iui-metrics/js/fontAwesome/scss/_mixins.scss | 26 + .../iui-metrics/js/fontAwesome/scss/_path.scss | 15 + .../js/fontAwesome/scss/_rotated-flipped.scss | 20 + .../iui-metrics/js/fontAwesome/scss/_stacked.scss | 20 + .../js/fontAwesome/scss/_variables.scss | 688 +++ .../js/fontAwesome/scss/font-awesome.scss | 17 + .../iframeResizer.contentWindow.min.js | 10 + .../js/iframeResizer/iframeResizer.min.js | 9 + .../resources/iui-metrics/js/images/sort_both.png | Bin 0 -> 5639 bytes .../js/jquery.i18n/jquery.i18n.properties-1.0.9.js | 479 ++ .../iui-metrics/js/jquery/jquery-1.10.2.min.js | 4 + .../main/resources/iui-metrics/js/metricsChart.js | 417 ++ .../resources/iui-metrics/js/metricsController.js | 228 + .../main/resources/iui-metrics/js/metricsUtil.js | 47 + .../main/resources/iui-route/css/animate.min.css | 6 + .../src/main/resources/iui-route/css/newRoute.css | 270 + .../src/main/resources/iui-route/css/route.css | 643 +++ .../src/main/resources/iui-route/default.html | 141 + .../iui-route/i18n/loadi18nApp_iui-route_view.js | 43 + .../i18n/msb-iui-route-i18n-en-US.properties | 164 + .../i18n/msb-iui-route-i18n-zh-CN.properties | 159 + .../resources/iui-route/img/checkbox-checked.png | Bin 0 -> 3053 bytes .../main/resources/iui-route/img/checkbox-init.png | Bin 0 -> 2833 bytes .../resources/iui-route/img/conductor-logo.png | Bin 0 -> 7470 bytes .../main/resources/iui-route/img/details_close.png | Bin 0 -> 3300 bytes .../main/resources/iui-route/img/details_open.png | Bin 0 -> 3304 bytes .../src/main/resources/iui-route/img/down.png | Bin 0 -> 2938 bytes .../iui-route/img/loading-spinner-grey.gif | Bin 0 -> 5203 bytes .../src/main/resources/iui-route/img/logo.png | Bin 0 -> 5379 bytes .../main/resources/iui-route/img/netnumenLogo.png | Bin 0 -> 5639 bytes .../iui-route/img/sidebar-toggler-grey.jpg | Bin 0 -> 14801 bytes .../src/main/resources/iui-route/img/throbber.gif | Bin 0 -> 9257 bytes .../src/main/resources/iui-route/img/up.png | Bin 0 -> 2926 bytes .../main/resources/iui-route/img/zte_logo_16.gif | Bin 0 -> 583 bytes .../src/main/resources/iui-route/index.html | 1039 ++++ .../src/main/resources/iui-route/js/avalon.js | 5819 ++++++++++++++++++++ .../resources/iui-route/js/bootbox/bootbox.min.js | 6 + .../resources/iui-route/js/bootstrap-growl.min.js | 2 + .../iui-route/js/bootstrap/css/bootstrap-dt.css | 5804 +++++++++++++++++++ .../iui-route/js/bootstrap/css/bootstrap.min.css | 7 + .../iui-route/js/bootstrap/js/bootstrap.js | 1951 +++++++ .../iui-route/js/bootstrap/js/bootstrap.min.js | 6 + .../js/bootstrap/js/bootstrap2-typeahead.min.js | 21 + .../js/dataTables/dataTables.bootstrap.css | 308 ++ .../js/dataTables/dataTables.bootstrap.min.js | 8 + .../js/dataTables/jquery.dataTables.min.js | 160 + .../iui-route/js/fontAwesome/css/font-awesome.css | 2026 +++++++ .../js/fontAwesome/css/font-awesome.css.map | 7 + .../js/fontAwesome/css/font-awesome.min.css | 4 + .../iui-route/js/fontAwesome/fonts/FontAwesome.otf | Bin 0 -> 106260 bytes .../js/fontAwesome/fonts/fontawesome-webfont.eot | Bin 0 -> 68875 bytes .../js/fontAwesome/fonts/fontawesome-webfont.svg | 640 +++ .../js/fontAwesome/fonts/fontawesome-webfont.ttf | Bin 0 -> 138204 bytes .../js/fontAwesome/fonts/fontawesome-webfont.woff | Bin 0 -> 81284 bytes .../js/fontAwesome/fonts/fontawesome-webfont.woff2 | Bin 0 -> 64464 bytes .../iui-route/js/fontAwesome/less/animated.less | 34 + .../js/fontAwesome/less/bordered-pulled.less | 25 + .../iui-route/js/fontAwesome/less/core.less | 12 + .../iui-route/js/fontAwesome/less/fixed-width.less | 6 + .../js/fontAwesome/less/font-awesome.less | 17 + .../iui-route/js/fontAwesome/less/icons.less | 677 +++ .../iui-route/js/fontAwesome/less/larger.less | 13 + .../iui-route/js/fontAwesome/less/list.less | 19 + .../iui-route/js/fontAwesome/less/mixins.less | 26 + .../iui-route/js/fontAwesome/less/path.less | 15 + .../js/fontAwesome/less/rotated-flipped.less | 20 + .../iui-route/js/fontAwesome/less/stacked.less | 20 + .../iui-route/js/fontAwesome/less/variables.less | 688 +++ .../iui-route/js/fontAwesome/scss/_animated.scss | 34 + .../js/fontAwesome/scss/_bordered-pulled.scss | 25 + .../iui-route/js/fontAwesome/scss/_core.scss | 12 + .../js/fontAwesome/scss/_fixed-width.scss | 6 + .../iui-route/js/fontAwesome/scss/_icons.scss | 677 +++ .../iui-route/js/fontAwesome/scss/_larger.scss | 13 + .../iui-route/js/fontAwesome/scss/_list.scss | 19 + .../iui-route/js/fontAwesome/scss/_mixins.scss | 26 + .../iui-route/js/fontAwesome/scss/_path.scss | 15 + .../js/fontAwesome/scss/_rotated-flipped.scss | 20 + .../iui-route/js/fontAwesome/scss/_stacked.scss | 20 + .../iui-route/js/fontAwesome/scss/_variables.scss | 688 +++ .../js/fontAwesome/scss/font-awesome.scss | 17 + .../iframeResizer.contentWindow.min.js | 10 + .../js/iframeResizer/iframeResizer.min.js | 9 + .../js/jquery-validation/additional-methods.js | 928 ++++ .../js/jquery-validation/additional-methods.min.js | 4 + .../js/jquery-validation/jquery.validate.js | 1357 +++++ .../js/jquery-validation/jquery.validate.min.js | 4 + .../jquery-validation/localization/messages_ar.js | 33 + .../localization/messages_ar.min.js | 4 + .../jquery-validation/localization/messages_bg.js | 33 + .../localization/messages_bg.min.js | 4 + .../jquery-validation/localization/messages_ca.js | 33 + .../localization/messages_ca.min.js | 4 + .../jquery-validation/localization/messages_cs.js | 33 + .../localization/messages_cs.min.js | 4 + .../jquery-validation/localization/messages_da.js | 30 + .../localization/messages_da.min.js | 4 + .../jquery-validation/localization/messages_de.js | 30 + .../localization/messages_de.min.js | 4 + .../jquery-validation/localization/messages_el.js | 33 + .../localization/messages_el.min.js | 4 + .../jquery-validation/localization/messages_es.js | 36 + .../localization/messages_es.min.js | 4 + .../localization/messages_es_AR.js | 37 + .../localization/messages_es_AR.min.js | 4 + .../jquery-validation/localization/messages_et.js | 31 + .../localization/messages_et.min.js | 4 + .../jquery-validation/localization/messages_eu.js | 33 + .../localization/messages_eu.min.js | 4 + .../jquery-validation/localization/messages_fa.js | 36 + .../localization/messages_fa.min.js | 4 + .../jquery-validation/localization/messages_fi.js | 31 + .../localization/messages_fi.min.js | 4 + .../jquery-validation/localization/messages_fr.js | 59 + .../localization/messages_fr.min.js | 4 + .../jquery-validation/localization/messages_gl.js | 38 + .../localization/messages_gl.min.js | 4 + .../jquery-validation/localization/messages_he.js | 33 + .../localization/messages_he.min.js | 4 + .../jquery-validation/localization/messages_hr.js | 33 + .../localization/messages_hr.min.js | 4 + .../jquery-validation/localization/messages_hu.js | 32 + .../localization/messages_hu.min.js | 4 + .../jquery-validation/localization/messages_id.js | 32 + .../localization/messages_id.min.js | 4 + .../jquery-validation/localization/messages_is.js | 31 + .../localization/messages_is.min.js | 4 + .../jquery-validation/localization/messages_it.js | 36 + .../localization/messages_it.min.js | 4 + .../jquery-validation/localization/messages_ja.js | 33 + .../localization/messages_ja.min.js | 4 + .../jquery-validation/localization/messages_ka.js | 33 + .../localization/messages_ka.min.js | 4 + .../jquery-validation/localization/messages_kk.js | 33 + .../localization/messages_kk.min.js | 4 + .../jquery-validation/localization/messages_ko.js | 33 + .../localization/messages_ko.min.js | 4 + .../jquery-validation/localization/messages_lt.js | 33 + .../localization/messages_lt.min.js | 4 + .../jquery-validation/localization/messages_lv.js | 33 + .../localization/messages_lv.min.js | 4 + .../jquery-validation/localization/messages_my.js | 33 + .../localization/messages_my.min.js | 4 + .../jquery-validation/localization/messages_nl.js | 43 + .../localization/messages_nl.min.js | 4 + .../jquery-validation/localization/messages_no.js | 33 + .../localization/messages_no.min.js | 4 + .../jquery-validation/localization/messages_pl.js | 33 + .../localization/messages_pl.min.js | 4 + .../localization/messages_pt_BR.js | 37 + .../localization/messages_pt_BR.min.js | 4 + .../localization/messages_pt_PT.js | 37 + .../localization/messages_pt_PT.min.js | 4 + .../jquery-validation/localization/messages_ro.js | 33 + .../localization/messages_ro.min.js | 4 + .../jquery-validation/localization/messages_ru.js | 33 + .../localization/messages_ru.min.js | 4 + .../jquery-validation/localization/messages_si.js | 33 + .../localization/messages_si.min.js | 4 + .../jquery-validation/localization/messages_sk.js | 30 + .../localization/messages_sk.min.js | 4 + .../jquery-validation/localization/messages_sl.js | 33 + .../localization/messages_sl.min.js | 4 + .../jquery-validation/localization/messages_sr.js | 33 + .../localization/messages_sr.min.js | 4 + .../localization/messages_sr_lat.js | 33 + .../localization/messages_sr_lat.min.js | 4 + .../jquery-validation/localization/messages_sv.js | 31 + .../localization/messages_sv.min.js | 4 + .../jquery-validation/localization/messages_th.js | 33 + .../localization/messages_th.min.js | 4 + .../jquery-validation/localization/messages_tj.js | 33 + .../localization/messages_tj.min.js | 4 + .../jquery-validation/localization/messages_tr.js | 33 + .../localization/messages_tr.min.js | 4 + .../jquery-validation/localization/messages_uk.js | 33 + .../localization/messages_uk.min.js | 4 + .../jquery-validation/localization/messages_vi.js | 33 + .../localization/messages_vi.min.js | 4 + .../jquery-validation/localization/messages_zh.js | 33 + .../localization/messages_zh.min.js | 4 + .../localization/messages_zh_TW.js | 34 + .../localization/messages_zh_TW.min.js | 4 + .../jquery-validation/localization/methods_de.js | 22 + .../localization/methods_de.min.js | 4 + .../localization/methods_es_CL.js | 22 + .../localization/methods_es_CL.min.js | 4 + .../jquery-validation/localization/methods_fi.js | 22 + .../localization/methods_fi.min.js | 4 + .../jquery-validation/localization/methods_nl.js | 19 + .../localization/methods_nl.min.js | 4 + .../jquery-validation/localization/methods_pt.js | 19 + .../localization/methods_pt.min.js | 4 + .../js/jquery.i18n/jquery.i18n.properties-1.0.9.js | 479 ++ .../iui-route/js/jquery/jquery-1.10.2.min.js | 4 + .../main/resources/iui-route/js/routeController.js | 1823 ++++++ .../src/main/resources/iui-route/js/routeFunc.js | 646 +++ .../src/main/resources/iui-route/js/routeUtil.js | 280 + .../msb/wrapper/ApiRouteServiceWrapperTest.java | 164 + .../msb/wrapper/CustomRouteServiceWrapperTest.java | 137 + .../msb/wrapper/IuiRouteServiceWrapperTest.java | 137 + .../openo/msb/wrapper/MicroServiceWrapperTest.java | 148 + .../msb/wrapper/util/JacksonJsonUtilTest.java | 48 + .../openo/msb/wrapper/util/RegExpTestUtilTest.java | 86 + .../org/openo/msb/wrapper/util/RouteUtilTest.java | 64 + apiroute/apiroute-standalone/pom.xml | 126 + .../assembly/resource/apiroute/apirouteService.exe | Bin 0 -> 59392 bytes .../assembly/resource/apiroute/apirouteService.xml | 13 + .../assembly/resource/apiroute/conf/apiroute.yml | 52 + .../resource/apiroute/ext/initServices/msb.json | 29 + .../resource/apiroute/ext/initServices/readme.txt | 63 + .../apiroute/ext/initSwaggerJson/api-doc1.json | 1 + .../apiroute/ext/initSwaggerJson/api-doc2.json | 1 + .../ext/initUrlRootPath/initUrlRootPath.json | 4 + .../initVisualRange/initVisualRangeMatches.json | 3 + .../apiroute/ext/initVisualRange/readme.txt | 14 + .../resource/apiroute/find_kill_process.bat | 23 + .../src/assembly/resource/apiroute/run.bat | 55 + .../src/assembly/resource/apiroute/run.sh | 43 + .../src/assembly/resource/apiroute/stop.bat | 34 + .../src/assembly/resource/apiroute/stop.sh | 44 + apiroute/pom.xml | 36 + openresty-ext/pom.xml | 97 + .../resources/openresty/nginx/conf/mime.types | 94 + .../resources/openresty/nginx/conf/nginx.conf | 57 + .../openresty/nginx/luaext/conf/msbinit.lua | 44 + .../openresty/nginx/luaext/conf/svcconf.lua | 34 + .../resources/openresty/nginx/luaext/dao/dao.lua | 120 + .../openresty/nginx/luaext/dao/db_access.lua | 74 + .../openresty/nginx/luaext/dao/redis_db.lua | 193 + .../openresty/nginx/luaext/lib/tools/db_cache.lua | 54 + .../openresty/nginx/luaext/lib/utils/log_util.lua | 28 + .../openresty/nginx/luaext/lib/utils/svc_util.lua | 59 + .../nginx/luaext/lib/utils/table_util.lua | 30 + .../nginx/luaext/loadbalance/balancer.lua | 40 + .../nginx/luaext/loadbalance/policy/roundrobin.lua | 60 + .../openresty/nginx/luaext/log/logger.lua | 26 + .../nginx/luaext/rewrite/commonrewrite.lua | 155 + .../nginx/luaext/rewrite/customrewrite.lua | 208 + .../openresty/nginx/luaext/vendor/shcache.lua | 440 ++ .../nginx/msb-enabled/location-ext-mount/README.md | 4 + .../nginx/msb-enabled/location-ext/README.md | 11 + .../resources/openresty/nginx/msb-enabled/msb.conf | 218 + .../resources/openresty/nginx/openrestyService.exe | Bin 0 -> 59392 bytes .../resources/openresty/nginx/openrestyService.xml | 13 + .../openresty/nginx/sites-enabled-mount/README.md | 4 + .../openresty/nginx/sites-enabled/README.md | 15 + .../openresty/nginx/stream-enabled/placeholder.txt | 0 .../src/assembly/resources/openresty/reload.sh | 26 + .../src/assembly/resources/openresty/run.bat | 52 + .../src/assembly/resources/openresty/run.sh | 40 + .../src/assembly/resources/openresty/run4docker.sh | 39 + .../src/assembly/resources/openresty/stop.bat | 51 + .../src/assembly/resources/openresty/stop.sh | 55 + openresty/pom.xml | 99 + .../resources/openresty-1.9.14.1001-win32.zip | Bin 0 -> 4254852 bytes .../resources/openresty-1.9.14.1001-win64.zip | Bin 0 -> 4648237 bytes .../resources/openresty-1.9.15.1-linux64.tar.gz | Bin 0 -> 13506533 bytes 426 files changed, 80420 insertions(+) create mode 100644 README.md create mode 100644 apiroute/apiroute-service/pom.xml create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/ApiRouteApp.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/ApiRouteAppConfig.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/ConsulClientApp.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/ApiRouteInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/ConsulInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/CustomDateSerializer.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/CustomRouteInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/DiscoverInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/IuiRouteInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/MetricsInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/MicroServiceFullInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/MicroServiceInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/Node.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/NodeInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/RouteServer.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/Service.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/ServiceAccessInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedInternalServerErrorException.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedNotFoundException.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedNotSupportedException.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/health/ApiRouteHealthCheck.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/resources/ApiRouteResource.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/resources/CustomRouteResource.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/resources/IuiRouteResource.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/resources/MetricsResource.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/resources/MicroServiceResource.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/resources/ServiceAccessResource.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/ApiRouteServiceWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/CustomRouteServiceWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/IuiRouteServiceWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/MetricsServiceWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/MicroServiceWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/ServiceAccessWrapper.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/CatalogClient.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/Consul.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/ConsulException.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/HealthClient.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/async/ConsulResponseCallback.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/CatalogCache.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ConsulCache.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ConsulCache4Map.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/HealthCache.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ServiceCache.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/ConsulResponse.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/CatalogNode.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/CatalogService.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ImmutableCatalogNode.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ImmutableCatalogService.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ServiceInfo.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ImmutableNode.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ImmutableService.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/Node.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/Service.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ServiceHealth.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/CatalogOptions.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ConsistencyMode.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ImmutableCatalogOptions.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ImmutableQueryOptions.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/Options.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ParamAdder.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/QueryOptions.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/Base64EncodingDeserializer.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ClientUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/Jackson.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ObjectMapperContextResolver.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/SecondsDeserializer.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/SecondsSerializer.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/UnsignedLongDeserializer.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/serviceListener/IMicroServiceChangeListener.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/serviceListener/MicroServiceChangeListener.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/FileUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/JacksonJsonUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/JedisUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MetricsUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MicroServiceDB.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MicroServiceUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/RegExpTestUtil.java create mode 100644 apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/RouteUtil.java create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/META-INF/MANIFEST.MF create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/WEB-INF/web.xml create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/css/reset.css create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/css/screen.css create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/css/typography.css create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.eot create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.svg create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.ttf create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.woff create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.woff2 create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.eot create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.svg create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.ttf create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.woff create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.woff2 create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/images/explorer_icons.png create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/images/logo_small.png create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/images/pet_store_api.png create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/images/throbber.gif create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/images/wordnik_api.png create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/index.html create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/js/iframeResizer/iframeResizer.contentWindow.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/js/iframeResizer/iframeResizer.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/backbone-min.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/handlebars-2.0.0.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/highlight.7.3.pack.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/jquery-1.8.0.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/jquery.ba-bbq.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/jquery.slideto.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/jquery.wiggle.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/marked.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/shred.bundle.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/shred/content.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/swagger-client.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/swagger-oauth.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/lib/underscore-min.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/o2c.html create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/swagger-ui.js create mode 100644 apiroute/apiroute-service/src/main/resources/api-doc/swagger-ui.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/banner.txt create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/css/animate.min.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/css/metrics.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/i18n/loadi18nApp_iui-metrics_view.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/i18n/msb-iui-metrics-i18n-en-US.properties create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/i18n/msb-iui-metrics-i18n-zh-CN.properties create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/img/loading-spinner-grey.gif create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/img/netnumenLogo.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/img/throbber.gif create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/index.html create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/avalon.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/bootstrap/css/bootstrap-dt.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/bootstrap/css/bootstrap.min.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/bootstrap/js/bootstrap.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/bootstrap/js/bootstrap.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/bootstrap/js/bootstrap2-typeahead.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/dataTables/dataTables.bootstrap.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/dataTables/dataTables.bootstrap.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/dataTables/jquery.dataTables.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/echarts/echarts-all.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/css/font-awesome.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/css/font-awesome.css.map create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/css/font-awesome.min.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/FontAwesome.otf create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/fontawesome-webfont.eot create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/fontawesome-webfont.svg create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/fontawesome-webfont.ttf create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/fontawesome-webfont.woff create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/fonts/fontawesome-webfont.woff2 create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/animated.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/bordered-pulled.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/core.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/fixed-width.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/font-awesome.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/icons.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/larger.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/list.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/mixins.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/path.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/rotated-flipped.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/stacked.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/less/variables.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_animated.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_bordered-pulled.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_core.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_fixed-width.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_icons.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_larger.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_list.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_mixins.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_path.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_rotated-flipped.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_stacked.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/_variables.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/fontAwesome/scss/font-awesome.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/iframeResizer/iframeResizer.contentWindow.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/iframeResizer/iframeResizer.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/images/sort_both.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/jquery.i18n/jquery.i18n.properties-1.0.9.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/jquery/jquery-1.10.2.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/metricsChart.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/metricsController.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-metrics/js/metricsUtil.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/css/animate.min.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/css/newRoute.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/css/route.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/default.html create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/i18n/loadi18nApp_iui-route_view.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/i18n/msb-iui-route-i18n-en-US.properties create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/i18n/msb-iui-route-i18n-zh-CN.properties create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/checkbox-checked.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/checkbox-init.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/conductor-logo.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/details_close.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/details_open.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/down.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/loading-spinner-grey.gif create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/logo.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/netnumenLogo.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/sidebar-toggler-grey.jpg create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/throbber.gif create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/up.png create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/img/zte_logo_16.gif create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/index.html create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/avalon.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/bootbox/bootbox.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap-growl.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/css/bootstrap-dt.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/css/bootstrap.min.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/js/bootstrap.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/js/bootstrap.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/js/bootstrap2-typeahead.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/dataTables/dataTables.bootstrap.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/dataTables/dataTables.bootstrap.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/dataTables/jquery.dataTables.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/css/font-awesome.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/css/font-awesome.css.map create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/css/font-awesome.min.css create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/FontAwesome.otf create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/fontawesome-webfont.eot create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/fontawesome-webfont.svg create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/fontawesome-webfont.ttf create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/fontawesome-webfont.woff create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/fonts/fontawesome-webfont.woff2 create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/animated.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/bordered-pulled.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/core.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/fixed-width.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/font-awesome.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/icons.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/larger.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/list.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/mixins.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/path.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/rotated-flipped.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/stacked.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/less/variables.less create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_animated.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_bordered-pulled.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_core.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_fixed-width.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_icons.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_larger.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_list.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_mixins.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_path.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_rotated-flipped.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_stacked.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/_variables.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/fontAwesome/scss/font-awesome.scss create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/iframeResizer/iframeResizer.contentWindow.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/iframeResizer/iframeResizer.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/additional-methods.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/additional-methods.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/jquery.validate.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/jquery.validate.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ar.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ar.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_bg.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_bg.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ca.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ca.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_cs.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_cs.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_da.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_da.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_de.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_de.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_el.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_el.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_es.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_es.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_es_AR.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_es_AR.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_et.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_et.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_eu.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_eu.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fa.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fa.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fi.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fi.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fr.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_fr.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_gl.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_gl.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_he.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_he.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_hr.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_hr.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_hu.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_hu.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_id.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_id.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_is.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_is.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_it.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_it.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ja.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ja.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ka.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ka.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_kk.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_kk.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ko.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ko.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_lt.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_lt.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_lv.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_lv.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_my.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_my.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_nl.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_nl.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_no.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_no.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pl.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pl.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pt_BR.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pt_BR.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pt_PT.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_pt_PT.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ro.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ro.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ru.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_ru.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_si.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_si.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sk.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sk.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sl.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sl.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sr.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sr.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sr_lat.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sr_lat.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sv.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_sv.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_th.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_th.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_tj.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_tj.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_tr.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_tr.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_uk.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_uk.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_vi.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_vi.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_zh.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_zh.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_zh_TW.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/messages_zh_TW.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_de.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_de.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_es_CL.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_es_CL.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_fi.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_fi.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_nl.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_nl.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_pt.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery-validation/localization/methods_pt.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery.i18n/jquery.i18n.properties-1.0.9.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/jquery/jquery-1.10.2.min.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/routeController.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/routeFunc.js create mode 100644 apiroute/apiroute-service/src/main/resources/iui-route/js/routeUtil.js create mode 100644 apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/ApiRouteServiceWrapperTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/CustomRouteServiceWrapperTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/IuiRouteServiceWrapperTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/MicroServiceWrapperTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/util/JacksonJsonUtilTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/util/RegExpTestUtilTest.java create mode 100644 apiroute/apiroute-service/src/test/java/org/openo/msb/wrapper/util/RouteUtilTest.java create mode 100644 apiroute/apiroute-standalone/pom.xml create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/apirouteService.exe create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/apirouteService.xml create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/conf/apiroute.yml create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initServices/msb.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initServices/readme.txt create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initSwaggerJson/api-doc1.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initSwaggerJson/api-doc2.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initUrlRootPath/initUrlRootPath.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initVisualRange/initVisualRangeMatches.json create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/ext/initVisualRange/readme.txt create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/find_kill_process.bat create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/run.bat create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/run.sh create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/stop.bat create mode 100644 apiroute/apiroute-standalone/src/assembly/resource/apiroute/stop.sh create mode 100644 apiroute/pom.xml create mode 100644 openresty-ext/pom.xml create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/conf/mime.types create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/conf/nginx.conf create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/conf/msbinit.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/conf/svcconf.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/dao/dao.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/dao/db_access.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/dao/redis_db.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/lib/tools/db_cache.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/lib/utils/log_util.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/lib/utils/svc_util.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/lib/utils/table_util.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/loadbalance/balancer.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/loadbalance/policy/roundrobin.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/log/logger.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/rewrite/commonrewrite.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/rewrite/customrewrite.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/luaext/vendor/shcache.lua create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/msb-enabled/location-ext-mount/README.md create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/msb-enabled/location-ext/README.md create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/msb-enabled/msb.conf create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/openrestyService.exe create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/openrestyService.xml create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/sites-enabled-mount/README.md create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/sites-enabled/README.md create mode 100644 openresty-ext/src/assembly/resources/openresty/nginx/stream-enabled/placeholder.txt create mode 100644 openresty-ext/src/assembly/resources/openresty/reload.sh create mode 100644 openresty-ext/src/assembly/resources/openresty/run.bat create mode 100644 openresty-ext/src/assembly/resources/openresty/run.sh create mode 100644 openresty-ext/src/assembly/resources/openresty/run4docker.sh create mode 100644 openresty-ext/src/assembly/resources/openresty/stop.bat create mode 100644 openresty-ext/src/assembly/resources/openresty/stop.sh create mode 100644 openresty/pom.xml create mode 100644 openresty/src/assembly/resources/openresty-1.9.14.1001-win32.zip create mode 100644 openresty/src/assembly/resources/openresty-1.9.14.1001-win64.zip create mode 100644 openresty/src/assembly/resources/openresty-1.9.15.1-linux64.tar.gz diff --git a/README.md b/README.md new file mode 100644 index 0000000..9663944 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Microservice Bus diff --git a/apiroute/apiroute-service/pom.xml b/apiroute/apiroute-service/pom.xml new file mode 100644 index 0000000..1d812c6 --- /dev/null +++ b/apiroute/apiroute-service/pom.xml @@ -0,0 +1,160 @@ + + + + + org.openo.msb.msb-core.apiroute + apiroute-parent + 1.0.0-SNAPSHOT + + 4.0.0 + org.openo.msb.msb-core.apiroute + apiroute-service + openo/msb/msb-core/apiroute/apiroute-service + jar + 1.0.0-SNAPSHOT + + + + + io.dropwizard + dropwizard-core + + + io.dropwizard + dropwizard-assets + + + io.dropwizard + dropwizard-client + + + io.swagger + swagger-jersey2-jaxrs + compile + + + + redis.clients + jedis + + + org.projectlombok + lombok + + + net.sf.json-lib + json-lib + + + junit + junit + test + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + true + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + org.openo.msb.ApiRouteApp + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + src/main/java + + **/*.properties + + + + src/main/resources + + + + diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/ApiRouteApp.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/ApiRouteApp.java new file mode 100644 index 0000000..0e0860d --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/ApiRouteApp.java @@ -0,0 +1,550 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb; + +import io.dropwizard.Application; +import io.dropwizard.assets.AssetsBundle; +import io.dropwizard.jetty.HttpConnectorFactory; +import io.dropwizard.server.SimpleServerFactory; +import io.dropwizard.setup.Bootstrap; +import io.dropwizard.setup.Environment; +import io.swagger.jaxrs.config.BeanConfig; +import io.swagger.jaxrs.listing.ApiListingResource; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URL; +import java.util.List; + +import net.sf.json.JSONObject; + +import org.apache.commons.lang3.StringUtils; +import org.openo.msb.api.ApiRouteInfo; +import org.openo.msb.api.ConsulInfo; +import org.openo.msb.api.CustomRouteInfo; +import org.openo.msb.api.DiscoverInfo; +import org.openo.msb.api.IuiRouteInfo; +import org.openo.msb.api.RouteServer; +import org.openo.msb.api.exception.ExtendedNotFoundException; +import org.openo.msb.health.ApiRouteHealthCheck; +import org.openo.msb.resources.ApiRouteResource; +import org.openo.msb.resources.CustomRouteResource; +import org.openo.msb.resources.IuiRouteResource; +import org.openo.msb.resources.MetricsResource; +import org.openo.msb.resources.MicroServiceResource; +import org.openo.msb.resources.ServiceAccessResource; +import org.openo.msb.wrapper.ApiRouteServiceWrapper; +import org.openo.msb.wrapper.CustomRouteServiceWrapper; +import org.openo.msb.wrapper.IuiRouteServiceWrapper; +import org.openo.msb.wrapper.serviceListener.MicroServiceChangeListener; +import org.openo.msb.wrapper.util.FileUtil; +import org.openo.msb.wrapper.util.JacksonJsonUtil; +import org.openo.msb.wrapper.util.JedisUtil; +import org.openo.msb.wrapper.util.MetricsUtil; +import org.openo.msb.wrapper.util.MicroServiceDB; +import org.openo.msb.wrapper.util.RegExpTestUtil; +import org.openo.msb.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.annotation.JsonInclude; + +public class ApiRouteApp extends Application { + + private static final Logger LOGGER = LoggerFactory.getLogger(ApiRouteApp.class); + + public static void main(String[] args) throws Exception { + new ApiRouteApp().run(args); + + } + + private ApiRouteAppConfig config; + + @Override + public String getName() { + return " MicroService Bus "; + } + + @Override + public void initialize(Bootstrap bootstrap) { + + + } + + @Override + public void run(ApiRouteAppConfig configuration, Environment environment) { + + initRootPath(); + + + new AssetsBundle("/iui-metrics", "/"+RouteUtil.IUI_ROOT_PATH+"/microservices/metrics", + "index.html", "iui-metrics").run(environment); + + new AssetsBundle("/iui-route", "/"+RouteUtil.IUI_ROOT_PATH+"/microservices", "index.html", + "iui-microservices").run(environment); + + new AssetsBundle("/api-doc", "/"+RouteUtil.IUI_ROOT_PATH+"/microservices/api-doc", + "index.html", "api-doc").run(environment); + + new AssetsBundle("/ext", "/"+RouteUtil.IUI_ROOT_PATH+"/microservices/ext", + "index.html", "ext").run(environment); + + + + + final ApiRouteHealthCheck healthCheck = + new ApiRouteHealthCheck(configuration.getDefaultWorkspace()); + environment.healthChecks().register("template", healthCheck); + environment.jersey().register(new ApiRouteResource()); + environment.jersey().register(new IuiRouteResource()); + environment.jersey().register(new MetricsResource()); + environment.jersey().register(new CustomRouteResource()); + environment.jersey().register(new ServiceAccessResource()); + environment.jersey().register(new MicroServiceResource()); + + config = configuration; + + initSwaggerConfig(environment, configuration); + initRedisConfig(configuration); + checkRedisConnect(); + initMetricsConfig(configuration); + initVisualRangeMatches(); + + registerServiceChangeListener(); + + + } + + private void initMetricsConfig(ApiRouteAppConfig configuration) { + + SimpleServerFactory simpleServerFactory = + (SimpleServerFactory) configuration.getServerFactory(); + HttpConnectorFactory httpConnectorFactory = + (HttpConnectorFactory) simpleServerFactory.getConnector(); + MetricsUtil.adminContextPath = + "http://127.0.0.1:" + httpConnectorFactory.getPort() + + simpleServerFactory.getAdminContextPath() + "/metrics"; + } + + private void initSwaggerConfig(Environment environment, ApiRouteAppConfig configuration) { + + environment.jersey().register(new ApiListingResource()); + environment.getObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL); + + BeanConfig config = new BeanConfig(); + config.setTitle("MicroService Bus rest API"); + config.setVersion("1.0.0"); + config.setResourcePackage("org.openo.msb.resources"); + SimpleServerFactory simpleServerFactory = + (SimpleServerFactory) configuration.getServerFactory(); + String basePath = simpleServerFactory.getApplicationContextPath(); + String rootPath = simpleServerFactory.getJerseyRootPath(); + + rootPath = rootPath.substring(0, rootPath.indexOf("/*")); + + basePath = + basePath.equals("/") ? rootPath : (new StringBuilder()).append(basePath) + .append(rootPath).toString(); + + LOGGER.info("getApplicationContextPath: " + basePath); + config.setBasePath(basePath); + config.setScan(true); + } + + + private void initRootPath(){ + try { + + URL urlRootPath = ApiRouteApp.class.getResource("/ext/initUrlRootPath/initUrlRootPath.json"); + if (urlRootPath != null) { + String path = urlRootPath.getPath(); + + LOGGER.info("read initUrlRootPath:" + path); + + String fileContent = FileUtil.readFile(path); + JSONObject jsonObj = JSONObject.fromObject(fileContent); + RouteUtil.IUI_ROOT_PATH=jsonObj.get("iuiRootPath").toString(); + RouteUtil.API_ROOT_PATH=jsonObj.get("apiRootPath").toString(); + } + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error("read initUrlRootPath Files throw exception", e); + } + + } + private void initRedisConfig(ApiRouteAppConfig configuration) { + + String path = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); + String jarPath = path.substring(0, path.lastIndexOf("/")); + + LOGGER.info("jarpath: " + jarPath); + LOGGER.info("getDefaultWorkspace " + configuration.getDefaultWorkspace()); + + String confDir = + jarPath + "/" + configuration.getDefaultWorkspace() + "/" + + configuration.getPropertiesDir(); + String propertiesPath = confDir + "/" + configuration.getPropertiesName(); + + JedisUtil.propertiesPath = propertiesPath; + + LOGGER.info("propertiesPath: " + propertiesPath); + LOGGER.info("confDir: " + confDir); + + try { + File dirFile = new File(confDir); + + if (!dirFile.exists()) { + dirFile.mkdirs(); + } + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.info("create RedisConfig confDir error: " + confDir + e.getMessage()); + } + + + try { + File propertiesFile = new File(propertiesPath); + if (!propertiesFile.exists()) { + + + propertiesFile.createNewFile(); + + BufferedWriter output = new BufferedWriter(new FileWriter(propertiesFile)); + StringBuilder contentBuilder = new StringBuilder(); + contentBuilder.append("redis.host=127.0.0.1\n").append("redis.port=6379\n") + .append("#connectionTimeout\n").append("redis.connectionTimeout=2000\n") + .append("#redis dbIndex,defaule:0\n") + .append("redis.db_index=0\n\n") + .append("#--------------redis pool config--------------\n") + .append("#maxTotal\n").append("redis.pool.maxTotal=100\n") + .append("#maxIdle\n").append("redis.pool.maxIdle=20\n") + .append("#maxWaitMillis:ms\n") + .append("redis.pool.maxWaitMillis=1000\n") + .append("#testOnBorrow\n") + .append("redis.pool.testOnBorrow=false\n") + .append("#testOnReturn\n") + .append("redis.pool.testOnReturn=true\n") + .append("#nginx Port\n").append("server.port=10080\n"); + + output.write(contentBuilder.toString()); + output.close(); + + } + } catch (IOException e) { + // TODO Auto-generated catch block + LOGGER.info("create RedisConfig File error: " + propertiesPath + e.getMessage()); + } + } + + + private void checkRedisConnect() { + + new Thread(new Runnable() { + public void run() { + int n = 0; + while (true) { + if (ApiRouteServiceWrapper.checkRedisConnect() == false) { + n++; + System.out.println(n + + "/10 : Initial Route Configuration——redis connection fail..."); + + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + LOGGER.error("Thread.sleep throw except:"+e.getMessage()); + } + + + if (n >= 10) { + System.out.println("Initial Route Configuration fail,timeout exit"); + LOGGER.error("Initial Route Configuration——redis connection fail,timeout exit..."); + break; + } + } else { + System.out.println("starting to initial Route Configuration"); + // initRouteInfoFromConfig(); + initRouteInfoFromJson(); + System.out.println("starting to initial consul Configuration"); + runConsulClientApp(); + + break; + } + } + + } + }).start(); + } + + + + + /** + * @Title: initVisualRangeMatches + * @Description: TODO(According to the environment variable or a JSON file configuration initialization VisualRange filter conditions) + * @return: void + */ + private void initVisualRangeMatches(){ + try { + if(System.getenv("APIGATEWAY_VISUAL_RANGE")==null) + { + + URL visualRangePath = ApiRouteApp.class.getResource("/ext/initVisualRange/initVisualRangeMatches.json"); + if (visualRangePath != null) { + String path = visualRangePath.getPath(); + + LOGGER.info("read initVisualRangeMatches:" + path); + + String fileContent = FileUtil.readFile(path); + JSONObject jsonObj = JSONObject.fromObject(fileContent); + String visualRangeArray=jsonObj.get("visualRange").toString(); + + + RouteUtil.visualRangeMatches=StringUtils.split(visualRangeArray, ","); + + + + } + } + else{ + RouteUtil.visualRangeMatches=StringUtils.split(System.getenv("APIGATEWAY_VISUAL_RANGE"), ","); + } + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error("read initVisualRangeMatches Files or env(APIGATEWAY_VISUAL_RANGE) throw exception", e); + } + } + + /** + * @Title: initRouteInfoFromJson + * @Description: TODO(按照JSON文件配置初始化route数据) + * @return: void + */ + private void initRouteInfoFromJson() { + + URL apiDocsPath = ApiRouteApp.class.getResource("/ext/initServices"); + if (apiDocsPath != null) { + String path = apiDocsPath.getPath(); + + LOGGER.info("read JsonFilefolder:" + path); + + try { + File[] files = FileUtil.readFileFolder(path); + for (int i = 0; i < files.length; i++) { + File file = files[i]; + if (file.isFile() && file.getName().endsWith(".json")) { + LOGGER.info("read JsonFile:" + file.getPath()); + String fileContent = FileUtil.readFile(file.getPath()); + saveInitService2redis(fileContent); + } else { + LOGGER.warn(file.getName() + " is not a right file"); + } + } + + + + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + LOGGER.error("read initServices Files throw FileNotFoundException", e); + } catch (IOException e) { + // TODO Auto-generated catch block + LOGGER.error("read initServices Files throw IOexception", e); + } + + } + + + + } + + + + private void saveInitService2redis(String fileContent) { + try { + List routeList = + (List) JacksonJsonUtil.jsonToListBean(fileContent); + for (ApiRouteInfo route : routeList) { + String url = route.getUrl(); + + if (RegExpTestUtil.urlRegExpTest(route.getServiceName())) { + + try{ + CustomRouteInfo dbCustomRoute = + CustomRouteServiceWrapper.getInstance().getCustomRouteInstance( + route.getServiceName()); + } + catch(ExtendedNotFoundException e){ + + LOGGER.info("initCustomRoute: ServiceName--" + route.getServiceName()); + + CustomRouteInfo customRouteInfo = new CustomRouteInfo(); + customRouteInfo.setControl(route.getControl()); + customRouteInfo.setServers(route.getServers()); + customRouteInfo.setServiceName(route.getServiceName()); + customRouteInfo.setStatus(route.getStatus()); + customRouteInfo.setUrl(route.getUrl()); + + + CustomRouteServiceWrapper.getInstance().saveCustomRouteInstance( + customRouteInfo, ""); + + + } + } else { + + if (RegExpTestUtil.apiRouteUrlRegExpTest(url) || url.startsWith("/api/microservices/")) { + + + try{ + ApiRouteInfo dbApiRoute = + ApiRouteServiceWrapper.getInstance().getApiRouteInstance( + route.getServiceName(), route.getVersion()); + } + catch(ExtendedNotFoundException e){ + LOGGER.info("initapiRoute: ServiceName--" + route.getServiceName()); + ApiRouteServiceWrapper.getInstance().saveApiRouteInstance(route, ""); + } + + + } else if (RegExpTestUtil.iuiRouteUrlRegExpTest(url) || url.equals("/iui/microservices")) { + + try{ + IuiRouteInfo dbIuiRoute = + IuiRouteServiceWrapper.getInstance().getIuiRouteInstance( + route.getServiceName()); + } + catch(ExtendedNotFoundException e){ + + LOGGER.info(" initiuiRoute: ServiceName--" + route.getServiceName()); + IuiRouteInfo iuiRouteInfo = new IuiRouteInfo(); + iuiRouteInfo.setControl(route.getControl()); + iuiRouteInfo.setServers(route.getServers()); + iuiRouteInfo.setServiceName(route.getServiceName()); + iuiRouteInfo.setStatus(route.getStatus()); + + if(url.equals("/iui/microservices")){ + iuiRouteInfo.setUrl("/"+RouteUtil.IUI_ROOT_PATH+"/microservices"); + } + else{ + iuiRouteInfo.setUrl(route.getUrl()); + } + + IuiRouteServiceWrapper.getInstance().saveIuiRouteInstance(iuiRouteInfo); + + } + + } else { + LOGGER.error("init Service throw exception——serviceName: " + route.getServiceName()+",url:"+url); + } + } + + + + } + + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error("read initServices Files throw exception", e); + } + + } + + + + /** + * The listener registration service changes + */ + private void registerServiceChangeListener() { + MicroServiceDB.getInstance().addServiceChangeListener(new MicroServiceChangeListener()); + } + + // Open the consul to monitor subscription service + private void runConsulClientApp() { + DiscoverInfo config_discoverInfo = config.getDiscoverInfo(); + + ConsulInfo config_consulInfo=config.getConsulInfo(); + + RouteUtil.discoverInfo.setEnabled(config_discoverInfo.isEnabled()); + + if (config_discoverInfo.isEnabled()) { + try{ + if(System.getenv("SDCLIENT_SVC_PORT")==null) + { + //yml + RouteUtil.discoverInfo.setIp(config_discoverInfo.getIp()); + RouteUtil.discoverInfo.setPort(config_discoverInfo.getPort()); + + } + else{ + + String discoverAddress=System.getenv("SDCLIENT_SVC_PORT").split("//")[1]; + String sdIP=discoverAddress.split(":")[0]; + int sdPort=Integer + .parseInt(discoverAddress.split(":")[1]); + + RouteUtil.discoverInfo.setIp(sdIP); + RouteUtil.discoverInfo.setPort(sdPort); + + config_consulInfo.setIp(sdIP); + config_consulInfo.setPort(sdPort); + + + } + + + + //Registration service discovery routing + //api + ApiRouteInfo discoverApiService=new ApiRouteInfo(); + discoverApiService.setServiceName("msdiscover"); + discoverApiService.setUrl("/api/microservices/v1"); + discoverApiService.setVersion("v1"); + discoverApiService.setMetricsUrl("/admin/metrics"); + discoverApiService.setApiJson("/api/microservices/v1/swagger.json"); + + RouteServer[] servers=new RouteServer[1]; + servers[0]=new RouteServer(RouteUtil.discoverInfo.getIp(),String.valueOf(RouteUtil.discoverInfo.getPort())); + discoverApiService.setServers(servers); + + ApiRouteServiceWrapper.getInstance().saveApiRouteInstance(discoverApiService, ""); + + //iui + IuiRouteInfo discoverIUIService=new IuiRouteInfo(); + discoverIUIService.setServiceName("msdiscover"); + discoverIUIService.setUrl("/iui/microservices"); + discoverIUIService.setServers(servers); + IuiRouteServiceWrapper.getInstance().saveIuiRouteInstance(discoverIUIService); + + + + ConsulClientApp consulClientApp = new ConsulClientApp(config_consulInfo.getIp(), config_consulInfo.getPort()); + // Monitor service change + consulClientApp.startServiceListen(); + LOGGER.info("start monitor consul service--" +config_consulInfo.getIp() + ":" + + config_consulInfo.getPort()); + } + catch(Exception e){ + LOGGER.error("start monitor consul service fail:"+e.getMessage()); + } + } + + + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/ApiRouteAppConfig.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/ApiRouteAppConfig.java new file mode 100644 index 0000000..6439462 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/ApiRouteAppConfig.java @@ -0,0 +1,111 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb; + +import io.dropwizard.Configuration; + +import javax.validation.Valid; + +import org.hibernate.validator.constraints.NotEmpty; +import org.openo.msb.api.ConsulInfo; +import org.openo.msb.api.DiscoverInfo; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ApiRouteAppConfig extends Configuration { + @NotEmpty + private String defaultWorkspace = "apiroute-works"; + + @NotEmpty + private String defaultName = "Stranger"; + + @NotEmpty + private String propertiesName="redis.properties"; + + @NotEmpty + private String propertiesDir="conf"; + + + @Valid + private DiscoverInfo discoverInfo; + + @Valid + private ConsulInfo consulInfo; + + @JsonProperty + public ConsulInfo getConsulInfo() { + return consulInfo; + } + + @JsonProperty + public void setConsulInfo(ConsulInfo consulInfo) { + this.consulInfo = consulInfo; + } + + + public String getPropertiesDir() { + return propertiesDir; + } + + public void setPropertiesDir(String propertiesDir) { + this.propertiesDir = propertiesDir; + } + + public String getPropertiesName() { + return propertiesName; + } + + public void setPropertiesName(String propertiesName) { + this.propertiesName = propertiesName; + } + + @JsonProperty + public String getDefaultWorkspace() { + return defaultWorkspace; + } + + @JsonProperty + public void setDefaultWorkspace(String defaultWorkspace) { + this.defaultWorkspace = defaultWorkspace; + } + + @JsonProperty + public String getDefaultName() { + return defaultName; + } + + @JsonProperty + public void setDefaultName(String name) { + this.defaultName = name; + } + + + + @JsonProperty + public DiscoverInfo getDiscoverInfo() { + return discoverInfo; + } + + @JsonProperty + public void setDiscoverInfo(DiscoverInfo discoverInfo) { + this.discoverInfo = discoverInfo; + } + + + + +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/ConsulClientApp.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/ConsulClientApp.java new file mode 100644 index 0000000..bb0ff66 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/ConsulClientApp.java @@ -0,0 +1,452 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.commons.lang3.StringUtils; +import org.openo.msb.api.MicroServiceFullInfo; +import org.openo.msb.api.MicroServiceInfo; +import org.openo.msb.api.Node; +import org.openo.msb.wrapper.MicroServiceWrapper; +import org.openo.msb.wrapper.consul.CatalogClient; +import org.openo.msb.wrapper.consul.Consul; +import org.openo.msb.wrapper.consul.HealthClient; +import org.openo.msb.wrapper.consul.cache.CatalogCache; +import org.openo.msb.wrapper.consul.cache.ConsulCache; +import org.openo.msb.wrapper.consul.cache.ConsulCache4Map; +import org.openo.msb.wrapper.consul.cache.HealthCache; +import org.openo.msb.wrapper.consul.cache.ServiceCache; +import org.openo.msb.wrapper.consul.model.catalog.CatalogService; +import org.openo.msb.wrapper.consul.model.catalog.ServiceInfo; +import org.openo.msb.wrapper.consul.model.health.Service; +import org.openo.msb.wrapper.consul.model.health.ServiceHealth; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConsulClientApp { + + private final Consul consul; + private final CatalogClient catalogClient; + private final HealthClient healthClient; + private AtomicReference> cacheList = new AtomicReference>( + new ArrayList()); + + + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulClientApp.class); + + public ConsulClientApp(String ip, int port) { + URL url = null; + try { + url = new URL("http", ip, port, ""); + } catch (MalformedURLException e1) { + // TODO Auto-generated catch block + LOGGER.error("start ConsulClientApp throw exception", e1); + throw new RuntimeException(e1); + } + this.consul = Consul.builder().withUrl(url).build(); // connect to Consul on localhost + this.catalogClient = consul.catalogClient(); + this.healthClient = consul.healthClient(); + } + + public Consul getConsul() { + return consul; + } + + public CatalogClient getCatalogClient() { + return catalogClient; + } + + private void stopNodeListen(String serviceName) { + try { + + ListIterator cacheListLit = cacheList.get().listIterator(); + while (cacheListLit.hasNext()) { + HealthCache cache = (HealthCache) cacheListLit.next(); + if (cache.getServiceName().equals(serviceName)) { + + cache.stop(); + cacheListLit.remove(); + LOGGER.info(cache.getServiceName() + " NodeListen stoped"); + break; + } + } + + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error("stop Node:[" + serviceName + "] Listen throw exception", e); + } + + + } + + /** + * @Title startServiceListen + * @Description TODO(Open the consul registration services to monitor) + * @return void + */ + public void startServiceListen() { + final ServiceCache serviceCache = ServiceCache.newCache(catalogClient, 30); + serviceCache.addListener(new ConsulCache4Map.Listener>>() { + @Override + public void notify(List oldValues, List newValues) { + // do Something with updated server List + LOGGER.info("--new service notify--"); + + List deRegisterServiceList = getDiffrent(oldValues, newValues); + + + for (ServiceInfo serviceInfo : deRegisterServiceList) { + try { + + MicroServiceWrapper.getInstance().deleteMicroService( + serviceInfo.getServiceName(), serviceInfo.getVersion()); + + + stopNodeListen(serviceInfo.getServiceName()); + LOGGER.info("Cancel MicroServiceInfo and stop node listen successs:" + + serviceInfo.getServiceName()); + } catch (Exception e) { + LOGGER.error("Cancel MicroServiceInfo and stop node listen FAIL : ", e); + + } + + } + + + List registerServiceList = getDiffrent(newValues, oldValues); + for (ServiceInfo serviceInfo : registerServiceList) { + + // if (deRegisterServiceList.contains(serviceInfo)) continue; + + + LOGGER.info(" new serviceName:" + serviceInfo.getServiceName() + " version:" + + serviceInfo.getVersion()); + // Open Node to monitor new registration service + startHealthNodeListen(serviceInfo.getServiceName(), serviceInfo.getVersion()); + + } + + + } + + }); + + try { + LOGGER.info("start...consul ... service..Listening."); + serviceCache.start(); + + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error("start...service..Listen throw exception", e); + } + } + + + /** + * @Title startHealthNodeListen + * @Description TODO(Open a service node changes to monitor, only to return to health service) + * @param serviceName + * @return + * @return HealthCache + */ + private HealthCache startHealthNodeListen(final String serviceName, final String version) { + final HealthCache healthCache = HealthCache.newCache(healthClient, serviceName, 30); + healthCache.addListener(new HealthCache.Listener() { + @Override + public void notify(Map newValues) { + // do Something with updated server map + LOGGER.info(serviceName + "--new node notify--"); + + if (newValues.isEmpty()) { + LOGGER.info(serviceName + "--nodeList is Empty--"); + + + MicroServiceWrapper.getInstance().deleteMicroService(serviceName, version); + + // try { + // healthCache.stop(); + // } catch (Exception e) { + // LOGGER.equals(serviceName+"-- stop Node error:"+e.getMessage()); + // } + + } else { + + MicroServiceInfo microServiceInfo = new MicroServiceInfo(); + HashSet nodes = new HashSet(); + String url = ""; + String version = "", visualRange = "", protocol = "",lb_policy=""; + + for (Map.Entry entry : newValues.entrySet()) { + String nodeName = entry.getKey().toString(); + ServiceHealth value = (ServiceHealth) entry.getValue(); + + Node node = new Node(); + Service service = value.getService(); + node.setIp(service.getAddress()); + node.setPort(String.valueOf(service.getPort())); + + + try { + List tagList = service.getTags(); + for (String tag : tagList) { + if (tag.startsWith("url")) { + if (tag.split(":").length == 2) { + url = tag.split(":")[1]; + } else { + url = ""; + } + + + continue; + } + if (tag.startsWith("version")) { + if (tag.split(":").length == 2) { + version = tag.split(":")[1]; + } else { + version = ""; + } + continue; + } + if (tag.startsWith("protocol")) { + protocol = tag.split(":")[1]; + continue; + } + if (tag.startsWith("visualRange")) { + visualRange = tag.split(":")[1]; + continue; + } + + if (tag.startsWith("lb_policy")) { + lb_policy = tag.split(":")[1]; + continue; + } + + } + + + } catch (Exception e) { + LOGGER.error(serviceName + " read tag throw exception", e); + System.out.println(serviceName + " read tag throw exception"); + } + + nodes.add(node); + } + + microServiceInfo.setNodes(nodes); + microServiceInfo.setProtocol(protocol); + microServiceInfo.setUrl(url); + microServiceInfo.setServiceName(serviceName); + microServiceInfo.setLb_policy(lb_policy); + if (!visualRange.isEmpty()) { + microServiceInfo.setVisualRange(visualRange); + } + microServiceInfo.setVersion(version); + + try { + MicroServiceFullInfo microServiceFullInfo = + MicroServiceWrapper.getInstance().saveMicroServiceInstance( + microServiceInfo, false, "", ""); + LOGGER.info("register MicroServiceInfo successs:" + + microServiceFullInfo.getServiceName()); + } catch (Exception e) { + LOGGER.error("register MicroServiceInfo FAIL : " + serviceName, e); + + } + } + } + }); + try { + LOGGER.info(serviceName + " Node Listen start"); + cacheList.get().add(healthCache); + healthCache.start(); + + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error(serviceName + " Node Listen start throw exception", e); + } + + return healthCache; + } + + /** + * @Title startNodeListen + * @Description TODO(Open a service node changes to monitor) + * @param serviceName + * @return + * @return CatalogCache + */ + @Deprecated + private CatalogCache startNodeListen(final String serviceName) { + final CatalogCache catalogCache = CatalogCache.newCache(catalogClient, serviceName, 30); + catalogCache.addListener(new ConsulCache.Listener() { + @Override + public void notify(Map newValues) { + // do Something with updated server map + System.out.println(serviceName + "--new node notify--"); + LOGGER.info(serviceName + "--new node notify--"); + + if (newValues.isEmpty()) { + System.out.println(serviceName + "-- nodeList is Empty--"); + LOGGER.info(serviceName + "--nodeList is Empty-stop service[" + serviceName + + "] listen-"); + try { + catalogCache.stop(); + } catch (Exception e) { + LOGGER.equals(serviceName + "-- stop Node error:" + e.getMessage()); + } + + } else { + + MicroServiceInfo microServiceInfo = new MicroServiceInfo(); + HashSet nodes = new HashSet(); + String url = ""; + String version = "", visualRange = "", protocol = ""; + + for (Map.Entry entry : newValues.entrySet()) { + String nodeName = entry.getKey().toString(); + CatalogService value = (CatalogService) entry.getValue(); + + Node node = new Node(); + node.setIp(value.getServiceAddress()); + node.setPort(String.valueOf(value.getServicePort())); + + + try { + List tagList = value.getServiceTags(); + for (String tag : tagList) { + if (tag.startsWith("url")) { + if (tag.split(":").length == 2) { + url = tag.split(":")[1]; + } else { + url = ""; + } + + + continue; + } + if (tag.startsWith("version")) { + if (tag.split(":").length == 2) { + version = tag.split(":")[1]; + } else { + version = ""; + } + continue; + } + if (tag.startsWith("protocol")) { + protocol = tag.split(":")[1]; + continue; + } + if (tag.startsWith("visualRange")) { + visualRange = tag.split(":")[1]; + continue; + } + if (tag.startsWith("ttl")) { + int ttl = Integer.parseInt(tag.split(":")[1]); + node.setTtl(ttl); + continue; + } + } + + + } catch (Exception e) { + LOGGER.error(serviceName + " read tag throw exception", e); + System.out.println(serviceName + " read tag throw exception"); + } + + nodes.add(node); + + + System.out.println(nodeName + ":" + value.getServiceAddress() + " " + + value.getServicePort() + " " + value.getServiceTags()); + } + + microServiceInfo.setNodes(nodes); + microServiceInfo.setProtocol(protocol); + microServiceInfo.setUrl(url); + microServiceInfo.setServiceName(serviceName); + if (!visualRange.isEmpty()) { + microServiceInfo.setVisualRange(visualRange); + } + microServiceInfo.setVersion(version); + + try { + MicroServiceFullInfo microServiceFullInfo = + MicroServiceWrapper.getInstance().saveMicroServiceInstance( + microServiceInfo, false, "", ""); + LOGGER.info("register MicroServiceInfo successs:" + microServiceFullInfo); + System.out.println("register MicroServiceInfo successs:" + serviceName); + } catch (Exception e) { + LOGGER.error("register MicroServiceInfo FAIL : ", e); + + } + } + } + }); + try { + System.out.println(serviceName + " Node Listen start"); + LOGGER.info(serviceName + " Node Listen start"); + catalogCache.start(); + + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error(serviceName + " Node Listen start throw exception", e); + } + + return catalogCache; + } + + + /** + * @Title getDiffrent + * @Description TODO(Extract the list1 and list2 different data sets) + * @param list1 + * @param list2 + * @return + * @return List + */ + private List getDiffrent(List list1, List list2) { + + List diff = new ArrayList(); + + + + for (ServiceInfo serviceInfo : list1) { + if (!list2.contains(serviceInfo)) { + diff.add(serviceInfo); + } + } + + return diff; + } + + public static void main(String[] args) { + ConsulClientApp consulTest = new ConsulClientApp("127.0.0.1", 10081); + consulTest.startServiceListen(); + + + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ApiRouteInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ApiRouteInfo.java new file mode 100644 index 0000000..54f3f34 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ApiRouteInfo.java @@ -0,0 +1,136 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + + +public class ApiRouteInfo implements Serializable{ + private static final long serialVersionUID = 1L; + @ApiModelProperty(required = true) + private String serviceName; + + @ApiModelProperty(example = "v1", required = true) + private String version; + + @ApiModelProperty(value = "Target Service URL,start with /",example = "/test", required = true) + private String url; + + private String apiJson=""; //swagger json Path + + @ApiModelProperty(value = "[apiJson Type] 0:local file 1: remote file", allowableValues = "0,1", example = "1") + private String apiJsonType="1"; + private String metricsUrl=""; + + @ApiModelProperty(value = "[control Range] 0:default 1:readonly 2:hidden ", allowableValues = "0,1,2", example = "0") + private String control="0"; + + @ApiModelProperty(value = "[status] 1:abled 0:disabled ", allowableValues = "0,1", example = "1") + private String status="1"; + + @ApiModelProperty(value = "[visual Range]interSystem:0,inSystem:1", allowableValues = "0,1", example = "1") + private String visualRange = "1"; + + @ApiModelProperty(value = "[LB Policy]non_ip_hash:0,ip_hash:1", allowableValues = "0,1", example = "0") + private String useOwnUpstream="0"; //负载均衡策略 + + @ApiModelProperty(required = true) + private RouteServer servers[]; + + + + public String getServiceName() { + return serviceName; + } + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + public String getVersion() { + return version; + } + public void setVersion(String version) { + this.version = version; + } + + public String getApiJson() { + return apiJson; + } + public void setApiJson(String apiJson) { + this.apiJson = apiJson; + } + + public String getUrl() { + return url; + } + public void setUrl(String url) { + this.url = url; + } + public RouteServer[] getServers() { + return servers; + } + public void setServers(RouteServer[] servers) { + this.servers = servers; + } + + + public String getApiJsonType() { + return apiJsonType; + } + public void setApiJsonType(String apiJsonType) { + this.apiJsonType = apiJsonType; + } + public String getMetricsUrl() { + return metricsUrl; + } + public void setMetricsUrl(String metricsUrl) { + this.metricsUrl = metricsUrl; + } + public String getControl() { + return control; + } + public void setControl(String control) { + this.control = control; + } + public String getStatus() { + return status; + } + public void setStatus(String status) { + this.status = status; + } + public String getVisualRange() { + return visualRange; + } + public void setVisualRange(String visualRange) { + this.visualRange = visualRange; + } + public String getUseOwnUpstream() { + return useOwnUpstream; + } + + public void setUseOwnUpstream(String useOwnUpstream) { + this.useOwnUpstream = useOwnUpstream; + } + + + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ConsulInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ConsulInfo.java new file mode 100644 index 0000000..12c0dcb --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ConsulInfo.java @@ -0,0 +1,38 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import java.io.Serializable; + +public class ConsulInfo implements Serializable{ + private static final long serialVersionUID = 1L; + private String ip; + private int port; + + public String getIp() { + return ip; + } + public void setIp(String ip) { + this.ip = ip; + } + public int getPort() { + return port; + } + public void setPort(int port) { + this.port = port; + } + +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/CustomDateSerializer.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/CustomDateSerializer.java new file mode 100644 index 0000000..f99dcad --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/CustomDateSerializer.java @@ -0,0 +1,39 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +public class CustomDateSerializer extends JsonSerializer { + + @Override + public void serialize(Date value, + JsonGenerator jsonGenerator, + SerializerProvider provider) + throws IOException, JsonProcessingException { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); + jsonGenerator.writeString(sdf.format(value)); + } + + +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/CustomRouteInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/CustomRouteInfo.java new file mode 100644 index 0000000..2b84a38 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/CustomRouteInfo.java @@ -0,0 +1,102 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; + +public class CustomRouteInfo implements Serializable{ + + private static final long serialVersionUID = 1L; + @ApiModelProperty(required = true) + private String serviceName; + + @ApiModelProperty(value = "Target Service URL,start with /",example = "/test", required = true) + private String url; + + @ApiModelProperty(value = "[control Range] 0:default 1:readonly 2:hidden ", allowableValues = "0,1,2", example = "0") + private String control="0"; + + @ApiModelProperty(value = "[status] 1:abled 0:disabled ", allowableValues = "0,1", example = "1") + private String status="1"; + + @ApiModelProperty(value = "[visual Range]interSystem:0,inSystem:1", allowableValues = "0,1", example = "1") + private String visualRange = "1"; + + @ApiModelProperty(value = "[LB Policy]non_ip_hash:0,ip_hash:1", allowableValues = "0,1", example = "0") + private String useOwnUpstream="0"; //负载均衡策略 + + @ApiModelProperty(required = true) + private RouteServer servers[]; + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public RouteServer[] getServers() { + return servers; + } + + public void setServers(RouteServer[] servers) { + this.servers = servers; + } + + public String getControl() { + return control; + } + + public void setControl(String control) { + this.control = control; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getVisualRange() { + return visualRange; + } + + public void setVisualRange(String visualRange) { + this.visualRange = visualRange; + } + + public String getUseOwnUpstream() { + return useOwnUpstream; + } + + public void setUseOwnUpstream(String useOwnUpstream) { + this.useOwnUpstream = useOwnUpstream; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/DiscoverInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/DiscoverInfo.java new file mode 100644 index 0000000..122dbc4 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/DiscoverInfo.java @@ -0,0 +1,49 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import java.io.Serializable; + +public class DiscoverInfo implements Serializable{ + private static final long serialVersionUID = 1L; + private String ip; + private int port; + private boolean enabled; + + + public String getIp() { + return ip; + } + public void setIp(String ip) { + this.ip = ip; + } + public int getPort() { + return port; + } + public void setPort(int port) { + this.port = port; + } + public boolean isEnabled() { + return enabled; + } + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/IuiRouteInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/IuiRouteInfo.java new file mode 100644 index 0000000..0087fdf --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/IuiRouteInfo.java @@ -0,0 +1,102 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; + + +public class IuiRouteInfo implements Serializable{ + private static final long serialVersionUID = 1L; + @ApiModelProperty(required = true) + private String serviceName; + + @ApiModelProperty(value = "Target Service URL,start with /",example = "/test", required = true) + private String url; + + @ApiModelProperty(value = "[control Range] 0:default 1:readonly 2:hidden ", allowableValues = "0,1,2", example = "0") + private String control="0"; + + @ApiModelProperty(value = "[status] 1:abled 0:disabled ", allowableValues = "0,1", example = "1") + private String status="1"; + + @ApiModelProperty(value = "[visual Range]interSystem:0,inSystem:1", allowableValues = "0,1", example = "1") + private String visualRange = "1"; + + @ApiModelProperty(value = "[LB Policy]non_ip_hash:0,ip_hash:1", allowableValues = "0,1", example = "0") + private String useOwnUpstream="0"; //负载均衡策略 + + @ApiModelProperty(required = true) + private RouteServer servers[]; + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public RouteServer[] getServers() { + return servers; + } + + public void setServers(RouteServer[] servers) { + this.servers = servers; + } + + public String getControl() { + return control; + } + + public void setControl(String control) { + this.control = control; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getVisualRange() { + return visualRange; + } + + public void setVisualRange(String visualRange) { + this.visualRange = visualRange; + } + + public String getUseOwnUpstream() { + return useOwnUpstream; + } + + public void setUseOwnUpstream(String useOwnUpstream) { + this.useOwnUpstream = useOwnUpstream; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MetricsInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MetricsInfo.java new file mode 100644 index 0000000..324b582 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MetricsInfo.java @@ -0,0 +1,191 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import com.fasterxml.jackson.annotation.JsonProperty; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MetricsInfo{ + private Gauges gauges; + private Timers timers; +} + +@Data +@NoArgsConstructor +@AllArgsConstructor +class Gauges { + + @JsonProperty("jvm.attribute.uptime") + private JVMMetrics jvm_attribute_uptime; + + @JsonProperty("jvm.memory.pools.Eden-Space.usage") + private JVMMetrics jvm_memory_pools_Eden_Space_usage; + + @JsonProperty("jvm.memory.pools.PS-Eden-Space.usage") + private JVMMetrics jvm_memory_pools_PS_Eden_Space_usage; + + @JsonProperty("jvm.memory.pools.Perm-Gen.usage") + private JVMMetrics jvm_memory_pools_Perm_Gen_usage; + + @JsonProperty("jvm.memory.pools.PS-Perm-Gen.usage") + private JVMMetrics jvm_memory_pools_PS_Perm_Gen_usage; + + @JsonProperty("jvm.memory.pools.Survivor-Space.usage") + private JVMMetrics jvm_memory_pools_Survivor_Space_usage; + + @JsonProperty("jvm.memory.pools.PS-Survivor-Space.usage") + private JVMMetrics jvm_memory_pools_PS_Survivor_Space_usage; + + @JsonProperty("jvm.memory.pools.Tenured-Gen.usage") + private JVMMetrics jvm_memory_pools_Tenured_Gen_usage; + + @JsonProperty("jvm.memory.pools.PS-Old-Gen.usage") + private JVMMetrics jvm_memory_pools_PS_Old_Gen_usage; + + @JsonProperty("jvm.memory.pools.Code-Cache.usage") + private JVMMetrics jvm_memory_pools_Code_Cache_usage; + + @JsonProperty("jvm.memory.heap.init") + private JVMMetrics jvm_memory_heap_init; + + @JsonProperty("jvm.memory.non-heap.init") + private JVMMetrics jvm_memory_non_heap_init; + + @JsonProperty("jvm.memory.heap.used") + private JVMMetrics jvm_memory_heap_used; + + @JsonProperty("jvm.memory.non-heap.used") + private JVMMetrics jvm_memory_non_heap_used; + + @JsonProperty("jvm.memory.heap.max") + private JVMMetrics jvm_memory_heap_max; + + @JsonProperty("jvm.threads.runnable.count") + private JVMMetrics jvm_threads_runnable_count; + + @JsonProperty("jvm.threads.timed_waiting.count") + private JVMMetrics jvm_threads_timed_waiting_count; + + @JsonProperty("jvm.threads.waiting.count") + private JVMMetrics jvm_threads_waiting_count; + + @JsonProperty("jvm.threads.blocked.count") + private JVMMetrics jvm_threads_blocked_count; + + @JsonProperty("jvm.threads.count") + private JVMMetrics jvm_threads_count; + + + +} + +@Data +@NoArgsConstructor +@AllArgsConstructor +class Timers{ + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.ApiRouteResource.addApiRoute") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_ApiRouteResource_addApiRoute; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.ApiRouteResource.deleteApiRoute") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_ApiRouteResource_deleteApiRoute; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.ApiRouteResource.getApiDocs") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_ApiRouteResource_getApiDocs; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.ApiRouteResource.getApiRoute") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_ApiRouteResource_getApiRoute; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.ApiRouteResource.getApiRoutes") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_ApiRouteResource_getApiRoutes; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.ApiRouteResource.getServerIP") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_ApiRouteResource_getServerIP; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.ApiRouteResource.updateApiRoute") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_ApiRouteResource_updateApiRoute; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.IuiRouteResource.addIuiRoute") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_IuiRouteResource_addIuiRoute; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.IuiRouteResource.deleteIuiRoute") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_IuiRouteResource_deleteIuiRoute; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.IuiRouteResource.getIuiRoute") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_IuiRouteResource_getIuiRoute; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.IuiRouteResource.getIuiRoutes") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_IuiRouteResource_getIuiRoutes; + + @JsonProperty("com.zte.ums.nfv.eco.hsif.msb.resources.IuiRouteResource.updateIuiRoute") + private HttpMetrics com_zte_ums_nfv_eco_hsif_msb_resources_IuiRouteResource_updateIuiRoute; + + @JsonProperty("io.dropwizard.jetty.MutableServletContextHandler.get-requests") + private HttpMetrics io_dropwizard_jetty_MutableServletContextHandler_get_requests; + + @JsonProperty("io.dropwizard.jetty.MutableServletContextHandler.post-requests") + private HttpMetrics io_dropwizard_jetty_MutableServletContextHandler_post_requests; + + @JsonProperty("io.dropwizard.jetty.MutableServletContextHandler.put-requests") + private HttpMetrics io_dropwizard_jetty_MutableServletContextHandler_put_requests; + + @JsonProperty("io.dropwizard.jetty.MutableServletContextHandler.delete-requests") + private HttpMetrics io_dropwizard_jetty_MutableServletContextHandler_delete_requests; + + @JsonProperty("io.dropwizard.jetty.MutableServletContextHandler.other-requests") + private HttpMetrics io_dropwizard_jetty_MutableServletContextHandler_other_requests; + +} + +@Data +@NoArgsConstructor +@AllArgsConstructor +class JVMMetrics{ + private double value; +} + +@Data +@NoArgsConstructor +@AllArgsConstructor +class HttpMetrics{ + private int count; + private double max; + private double mean; + private double min; + private double p50; + private double p75; + private double p95; + private double p98; + private double p99; + private double p999; + private double stddev; + private double m15_rate; + private double m1_rate; + private double m5_rate; + private double mean_rate; + private String duration_units; + private String rate_units; +} + + diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MicroServiceFullInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MicroServiceFullInfo.java new file mode 100644 index 0000000..ccf1977 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MicroServiceFullInfo.java @@ -0,0 +1,44 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import java.io.Serializable; +import java.util.Set; + +public class MicroServiceFullInfo extends Service implements Serializable { + private static final long serialVersionUID = 1L; + + private Set nodes; + + private String status = "1"; //0:disable 1:enable + + public Set getNodes() { + return nodes; + } + + public void setNodes(Set nodes) { + this.nodes = nodes; + } + + public String getStatus() { + return status; + } + public void setStatus(String status) { + this.status = status; + } + + +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MicroServiceInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MicroServiceInfo.java new file mode 100644 index 0000000..e6e1b73 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/MicroServiceInfo.java @@ -0,0 +1,38 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import java.io.Serializable; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class MicroServiceInfo extends Service implements Serializable { + private static final long serialVersionUID = 1L; + + private Set nodes; + + public Set getNodes() { + return nodes; + } + + public void setNodes(Set nodes) { + this.nodes = nodes; + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/Node.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/Node.java new file mode 100644 index 0000000..f0d179f --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/Node.java @@ -0,0 +1,75 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +public class Node implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(required = true) + private String ip; + + @ApiModelProperty(required = true) + private String port; + + private int ttl=-1; + + + + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + + public int getTtl() { + return ttl; + } + + public void setTtl(int ttl) { + this.ttl = ttl; + } + + public Node(){ + + } + + public Node(String ip,String port,int ttl){ + this.ip = ip; + this.port = port; + this.ttl = ttl; + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/NodeInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/NodeInfo.java new file mode 100644 index 0000000..9988e1d --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/NodeInfo.java @@ -0,0 +1,74 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +public class NodeInfo extends Node { + + private static final long serialVersionUID = 8955786461351557306L; + + private String nodeId; + + + + @JsonSerialize(using = CustomDateSerializer.class) + private Date expiration; + + @JsonSerialize(using = CustomDateSerializer.class) + private Date created_at; + + @JsonSerialize(using = CustomDateSerializer.class) + private Date updated_at; + + public Date getExpiration() { + return expiration; + } + + public void setExpiration(Date expiration) { + this.expiration = expiration; + } + + public Date getCreated_at() { + return created_at; + } + + public void setCreated_at(Date created_at) { + this.created_at = created_at; + } + + public Date getUpdated_at() { + return updated_at; + } + + public void setUpdated_at(Date updated_at) { + this.updated_at = updated_at; + } + + public String getNodeId() { + return nodeId; + } + + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/RouteServer.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/RouteServer.java new file mode 100644 index 0000000..3e01c4f --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/RouteServer.java @@ -0,0 +1,68 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; + + +public class RouteServer implements Serializable{ + private static final long serialVersionUID = 1L; + @ApiModelProperty(required = true) + private String ip; + + @ApiModelProperty(required = true) + private String port; + private int weight=0; + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public RouteServer(){ + + } + + public RouteServer(String ip,String port){ + this.ip=ip; + this.port=port; + this.weight=0; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/Service.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/Service.java new file mode 100644 index 0000000..51f0e86 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/Service.java @@ -0,0 +1,104 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class Service implements Serializable { + private static final long serialVersionUID = 1L; + // 服务名 + @ApiModelProperty(required = true) + private String serviceName; + // 版本号 + @ApiModelProperty(example = "v1") + private String version=""; + // 服务url + @ApiModelProperty(value = "Target Service URL,start with /",example = "/api/serviceName/v1", required = true) + private String url=""; + // 服务对应协议,比如REST、UI、MQ、FTP、SNMP、TCP、UDP + @ApiModelProperty(value = "Service Protocol", allowableValues = "REST,UI, MQ, FTP,SNMP,TCP,UDP", example = "REST",required = true) + private String protocol = ""; + + //服务的可见范围 系统间:0 系统内:1 + @ApiModelProperty(value = "[visual Range]interSystem:0,inSystem:1", allowableValues = "0,1", example = "1") + private String visualRange = "1"; + + //负载均衡策略类型 + @ApiModelProperty(value = "lb policy", allowableValues = "round-robin,hash,least_conn", example = "hash") + private String lb_policy=""; + + @ApiModelProperty(required = true) + private Set nodes; + + public Set getNodes() { + return nodes; + } + + public void setNodes(Set nodes) { + this.nodes = nodes; + } + + public String getServiceName() { + return serviceName; + } + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + public String getVersion() { + return version; + } + public void setVersion(String version) { + this.version = version; + } + public String getUrl() { + return url; + } + public void setUrl(String url) { + this.url = url; + } + public String getProtocol() { + return protocol; + } + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getVisualRange() { + return visualRange; + } + + public void setVisualRange(String visualRange) { + this.visualRange = visualRange; + } + + + public String getLb_policy() { + return lb_policy; + } + + public void setLb_policy(String lb_policy) { + this.lb_policy = lb_policy; + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ServiceAccessInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ServiceAccessInfo.java new file mode 100644 index 0000000..34c4a67 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/ServiceAccessInfo.java @@ -0,0 +1,88 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api; + +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonInclude; + +public class ServiceAccessInfo implements Serializable { + private static final long serialVersionUID = 1L; + // (api|iui|custom|p2p) + private String serviceType; + + private String serviceName; + @JsonInclude(JsonInclude.Include.NON_NULL) + private String version; + + private String accessAddr; + + /** + * @return the serviceType + */ + public String getServiceType() { + return serviceType; + } + + /** + * @param serviceType the serviceType to set + */ + public void setServiceType(String serviceType) { + this.serviceType = serviceType; + } + + /** + * @return the serviceName + */ + public String getServiceName() { + return serviceName; + } + + /** + * @param serviceName the serviceName to set + */ + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + /** + * @return the version + */ + public String getVersion() { + return version; + } + + /** + * @param version the version to set + */ + public void setVersion(String version) { + this.version = version; + } + + /** + * @return the accessAddr + */ + public String getAccessAddr() { + return accessAddr; + } + + /** + * @param accessAddr the accessAddr to set + */ + public void setAccessAddr(String accessAddr) { + this.accessAddr = accessAddr; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedInternalServerErrorException.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedInternalServerErrorException.java new file mode 100644 index 0000000..ce57dc6 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedInternalServerErrorException.java @@ -0,0 +1,28 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api.exception; + +import javax.ws.rs.InternalServerErrorException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +public class ExtendedInternalServerErrorException extends InternalServerErrorException { + + public ExtendedInternalServerErrorException(final String message) { + super(Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(message).type(MediaType.TEXT_PLAIN).build()); + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedNotFoundException.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedNotFoundException.java new file mode 100644 index 0000000..ca5f747 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedNotFoundException.java @@ -0,0 +1,28 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api.exception; + +import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +public class ExtendedNotFoundException extends NotFoundException { + + public ExtendedNotFoundException(final String message) { + super(Response.status(Response.Status.NOT_FOUND).entity(message).type(MediaType.TEXT_PLAIN).build()); + } +} + diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedNotSupportedException.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedNotSupportedException.java new file mode 100644 index 0000000..84bfbaa --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/api/exception/ExtendedNotSupportedException.java @@ -0,0 +1,27 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.api.exception; + +import javax.ws.rs.NotSupportedException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +public class ExtendedNotSupportedException extends NotSupportedException { + + public ExtendedNotSupportedException(final String message) { + super(Response.status(Response.Status.UNSUPPORTED_MEDIA_TYPE).entity(message).type(MediaType.TEXT_PLAIN).build()); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/health/ApiRouteHealthCheck.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/health/ApiRouteHealthCheck.java new file mode 100644 index 0000000..dc2af2a --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/health/ApiRouteHealthCheck.java @@ -0,0 +1,35 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.health; + +import com.codahale.metrics.health.HealthCheck; + +public class ApiRouteHealthCheck extends HealthCheck { + private final String template; + + public ApiRouteHealthCheck(String template) { + this.template = template; + } + + @Override + protected Result check() throws Exception { + final String saying = String.format(template, "TEST"); + if (!saying.contains("TEST")) { + return Result.unhealthy("template doesn't include a name"); + } + return Result.healthy(); + } +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/ApiRouteResource.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/ApiRouteResource.java new file mode 100644 index 0000000..cd86047 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/ApiRouteResource.java @@ -0,0 +1,233 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.net.URI; + +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.UriInfo; + +import org.apache.http.HttpStatus; +import org.openo.msb.api.ApiRouteInfo; +import org.openo.msb.api.DiscoverInfo; +import org.openo.msb.wrapper.ApiRouteServiceWrapper; +import org.openo.msb.wrapper.CustomRouteServiceWrapper; +import org.openo.msb.wrapper.IuiRouteServiceWrapper; +import org.openo.msb.wrapper.util.JacksonJsonUtil; +import org.openo.msb.wrapper.util.RouteUtil; + +import com.codahale.metrics.annotation.Timed; + +@Path("/apiRoute") +@Api(tags = { "ApiRoute" }) +@Produces(MediaType.APPLICATION_JSON) +public class ApiRouteResource { + + @Context + UriInfo uriInfo; // actual uri info + + @GET + @Path("/") + @ApiOperation(value = "get all ApiRoute ", code = HttpStatus.SC_OK,response = ApiRouteInfo.class, responseContainer = "List") + @ApiResponses(value = {@ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get ApiRouteInfo List fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public ApiRouteInfo[] getApiRoutes() { + return ApiRouteServiceWrapper.getInstance().getAllApiRouteInstances(); + } + + @POST + @Path("/") + @ApiOperation(value = "add one ApiRoute ", code = HttpStatus.SC_CREATED,response = ApiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable ApiRouteInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "add ApiRouteInfo fail", response = String.class), + @ApiResponse(code = HttpStatus.SC_BAD_REQUEST, message = "Unprocessable ApiRouteInfo JSON REQUEST", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response addApiRoute( + @ApiParam(value = "ApiRoute Instance Info", required = true) ApiRouteInfo apiRouteInfo) { + ApiRouteInfo new_apiRouteInfo = ApiRouteServiceWrapper.getInstance().saveApiRouteInstance(apiRouteInfo,""); + URI returnURI = + uriInfo.getAbsolutePathBuilder() + .path("/" + new_apiRouteInfo.getServiceName()+"/version/"+new_apiRouteInfo.getVersion()).build(); + return Response.created(returnURI).entity(new_apiRouteInfo).build(); + + } + + @GET + @Path("/{serviceName}/version/{version}") + @ApiOperation(value = "get one ApiRoute ",code = HttpStatus.SC_OK, response = ApiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "ApiRouteInfo not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable ApiRouteInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get ApiRouteInfo fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public ApiRouteInfo getApiRoute( + @ApiParam(value = "ApiRoute serviceName", required = true) @PathParam("serviceName") String serviceName, + @ApiParam(value = "ApiRoute version,if the version is empty, please enter \"null\"", required = false) @PathParam("version") @DefaultValue("") String version) { + + return ApiRouteServiceWrapper.getInstance().getApiRouteInstance(serviceName,version); + + } + + @PUT + @Path("/{serviceName}/version/{version}") + @ApiOperation(value = "update one ApiRoute by serviceName and version", code = HttpStatus.SC_CREATED,response = ApiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable ApiRouteInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "update ApiRouteInfo fail", response = String.class), + @ApiResponse(code = HttpStatus.SC_BAD_REQUEST, message = "Unprocessable ApiRouteInfo JSON REQUEST", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response updateApiRoute( + @ApiParam(value = "ApiRoute serviceName", required = true) @PathParam("serviceName") String serviceName, + @ApiParam(value = "ApiRoute version,if the version is empty, please enter \"null\"", required = false) @PathParam("version") @DefaultValue("") String version, + @ApiParam(value = "ApiRoute Instance Info", required = true) ApiRouteInfo apiRouteInfo) { + + ApiRouteInfo new_apiRouteInfo = ApiRouteServiceWrapper.getInstance().updateApiRouteInstance(serviceName,version,apiRouteInfo,""); + URI returnURI = + uriInfo.getAbsolutePathBuilder() + .path("/" + new_apiRouteInfo.getServiceName()+"/version/"+new_apiRouteInfo.getVersion()).build(); + return Response.created(returnURI).entity(new_apiRouteInfo).build(); + + } + + + + @DELETE + @Path("/{serviceName}/version/{version}") + @ApiOperation(value = "delete one ApiRoute by serviceName and version", code = HttpStatus.SC_NO_CONTENT) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_NO_CONTENT, message = "delete ApiRouteInfo succeed "), + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "ApiRouteInfo not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "delete ApiRouteInfo fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public void deleteApiRoute( + @ApiParam(value = "ApiRoute serviceName", required = true) @PathParam("serviceName") String serviceName, + @ApiParam(value = "ApiRoute version,if the version is empty, please enter \"null\"", required = false) @PathParam("version") @DefaultValue("") String version) { + + + ApiRouteServiceWrapper.getInstance().deleteApiRoute(serviceName, version,"*",""); + + } + + + @GET + @Path("/apiDocs") + @ApiOperation(value = "get all Local apiDoc ", code = HttpStatus.SC_OK, response = String.class, responseContainer = "List") + @ApiResponses(value = {@ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get apiDoc List fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public String[] getApiDocs() { + + String[] apiDocs = ApiRouteServiceWrapper.getInstance().getAllApiDocs(); + return apiDocs; + + } + + @GET + @Path("/apiGatewayPort") + @ApiOperation(value = "get apiGateway Port ", code = HttpStatus.SC_OK, response = String.class) + @ApiResponses(value = {@ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get apiGateway Port fail", response = String.class)}) + @Produces(MediaType.TEXT_PLAIN) + @Timed + public String getApiGatewayPort() { + + return ApiRouteServiceWrapper.getInstance().getApiGatewayPort(); + + } + + @GET + @Path("/discoverInfo") + @ApiOperation(value = "get discover Info ", code = HttpStatus.SC_OK,response = DiscoverInfo.class) + @ApiResponses(value = {@ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get discover Info fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public DiscoverInfo getServiceDiscoverInfo() { + + return ApiRouteServiceWrapper.getInstance().getServiceDiscoverInfo(); + } + + @PUT + @Path("/{serviceName}/version/{version}/status/{status}") + @ApiOperation(value = "update one ApiRoute status by serviceName and version", code = HttpStatus.SC_CREATED,response = ApiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable ApiRouteInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "ApiRouteInfo not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "update status fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response updateApiRouteStatus( + @ApiParam(value = "ApiRoute serviceName", required = true) @PathParam("serviceName") String serviceName, + @ApiParam(value = "ApiRoute version,if the version is empty, please enter \"null\"", required = false) @PathParam("version") @DefaultValue("") String version, + @ApiParam(value = "ApiRoute status,1:abled 0:disabled", required = true) @PathParam("status") String status) { + + ApiRouteInfo new_apiRouteInfo = ApiRouteServiceWrapper.getInstance().updateApiRouteStatus(serviceName,version,status); + return Response.created(uriInfo.getAbsolutePathBuilder().build()).entity(new_apiRouteInfo).build(); + + } + + @GET + @Path("/export") + @ApiOperation(value = "export all route service Info by json-file", code = HttpStatus.SC_OK,response = String.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "export fail", response = String.class), + @ApiResponse(code = HttpStatus.SC_NOT_ACCEPTABLE, message = " not Acceptable client-side", response = String.class)}) + @Produces(MediaType.TEXT_PLAIN) + public Response exportService() throws Exception { + + + Object[] apirouteArray= ApiRouteServiceWrapper.getInstance().getAllApiRouteInstances(); + Object[] iuirouteArray= IuiRouteServiceWrapper.getInstance().getAllIuiRouteInstances(); + Object[] customrouteArray= CustomRouteServiceWrapper.getInstance().getAllCustomRouteInstances(); + + Object[] temprouteArray =RouteUtil.concat(apirouteArray, iuirouteArray); + Object[] allrouteArray=RouteUtil.concat(temprouteArray, customrouteArray); + + + String allrouteJson=JacksonJsonUtil.beanToJson(allrouteArray); + + ResponseBuilder response = Response.ok(allrouteJson); + response.header("Content-Disposition", + "attachment; filename=\"RouteService.json\""); + return response.build(); + + + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/CustomRouteResource.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/CustomRouteResource.java new file mode 100644 index 0000000..89cdf5f --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/CustomRouteResource.java @@ -0,0 +1,152 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.net.URI; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.http.HttpStatus; +import org.openo.msb.api.CustomRouteInfo; +import org.openo.msb.wrapper.CustomRouteServiceWrapper; + +import com.codahale.metrics.annotation.Timed; + +@Path("/customRoute") +@Api(tags = { "CustomRoute" }) +@Produces(MediaType.APPLICATION_JSON) +public class CustomRouteResource { + + @Context + UriInfo uriInfo; // actual uri info + + @GET + @Path("/all") + @ApiOperation(value = "get all CustomRoute ", code = HttpStatus.SC_OK,response = CustomRouteInfo.class, responseContainer = "List") + @ApiResponses(value = {@ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get CustomRouteInfo List fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public CustomRouteInfo[] getCustomRoutes() { + return CustomRouteServiceWrapper.getInstance().getAllCustomRouteInstances(); + } + + @POST + @Path("/instance") + @ApiOperation(value = "add one CustomRoute ", code = HttpStatus.SC_CREATED,response = CustomRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable CustomRouteInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "add CustomRouteInfo fail", response = String.class), + @ApiResponse(code = HttpStatus.SC_BAD_REQUEST, message = "Unprocessable CustomRouteInfo JSON REQUEST", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response addCustomRoute( + @ApiParam(value = "CustomRoute Instance Info", required = true) CustomRouteInfo customRouteInfo) { + CustomRouteInfo new_customRouteInfo = CustomRouteServiceWrapper.getInstance().saveCustomRouteInstance(customRouteInfo,""); + URI returnURI = + uriInfo.getAbsolutePathBuilder() + .path("/instance?serviceName=" + new_customRouteInfo.getServiceName()).build(); + return Response.created(returnURI).entity(new_customRouteInfo).build(); + + } + + @GET + @Path("/instance") + @ApiOperation(value = "get one CustomRoute ",code = HttpStatus.SC_OK, response = CustomRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "CustomRoute not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable CustomRoute Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get CustomRoute fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public CustomRouteInfo getCustomRoute( + @ApiParam(value = "CustomRoute serviceName", required = false) @QueryParam("serviceName") String serviceName) { + + + return CustomRouteServiceWrapper.getInstance().getCustomRouteInstance(serviceName); + + } + + @PUT + @Path("/instance") + @ApiOperation(value = "update one CustomRoute by serviceName", code = HttpStatus.SC_CREATED,response = CustomRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable CustomRoute Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "update CustomRoute fail", response = String.class), + @ApiResponse(code = HttpStatus.SC_BAD_REQUEST, message = "Unprocessable CustomRoute JSON REQUEST", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response updateCustomRoute( + @ApiParam(value = "CustomRoute serviceName", required = true) @QueryParam("serviceName") String serviceName, + @ApiParam(value = "CustomRoute Instance Info", required = true) CustomRouteInfo customRoute) { + + CustomRouteInfo new_customRouteInfo= CustomRouteServiceWrapper.getInstance().updateCustomRouteInstance(serviceName,customRoute,""); + + return Response.created(uriInfo.getAbsolutePathBuilder().build()).entity(new_customRouteInfo).build(); + + } + + @DELETE + @Path("/instance") + @ApiOperation(value = "delete one CustomRoute by serviceName", code = HttpStatus.SC_NO_CONTENT) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_NO_CONTENT, message = "delete customRoute succeed "), + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "customRoute not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "delete customRoute fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public void deleteCustomRoute( + @ApiParam(value = "CustomRoute serviceName", required = true) @QueryParam("serviceName") String serviceName) { + + CustomRouteServiceWrapper.getInstance().deleteCustomRoute(serviceName,"*",""); + + } + + @PUT + @Path("/status") + @ApiOperation(value = "update one CustomRoute status by serviceName ",code = HttpStatus.SC_CREATED, response = CustomRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable customRoute Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "customRoute not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "update status fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response updateCustomRouteStatus( + @ApiParam(value = "CustomRoute serviceName", required = true) @QueryParam("serviceName") String serviceName, + @ApiParam(value = "CustomRoute status,1:abled 0:disabled", required = true) @QueryParam("status") String status) { + + CustomRouteInfo new_customRouteInfo = CustomRouteServiceWrapper.getInstance().updateCustomRouteStatus(serviceName,status); + return Response.created(uriInfo.getAbsolutePathBuilder().build()).entity(new_customRouteInfo).build(); + + + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/IuiRouteResource.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/IuiRouteResource.java new file mode 100644 index 0000000..b852fa9 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/IuiRouteResource.java @@ -0,0 +1,155 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.net.URI; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.http.HttpStatus; +import org.openo.msb.api.IuiRouteInfo; +import org.openo.msb.wrapper.IuiRouteServiceWrapper; + +import com.codahale.metrics.annotation.Timed; + +@Path("/iuiRoute") +@Api(tags = { "iuiRoute" }) +@Produces(MediaType.APPLICATION_JSON) +public class IuiRouteResource { + + @Context + UriInfo uriInfo; // actual uri info + + @GET + @Path("/") + @ApiOperation(value = "get all iuiRoute ", code = HttpStatus.SC_OK,response = IuiRouteInfo.class, responseContainer = "List") + @ApiResponses(value = {@ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get iuiRouteInfo List fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public IuiRouteInfo[] getIuiRoutes() { + return IuiRouteServiceWrapper.getInstance().getAllIuiRouteInstances(); + } + + @POST + @Path("/") + @ApiOperation(value = "add one iuiRoute ", code = HttpStatus.SC_CREATED,response = IuiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable iuiRouteInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "add iuiRouteInfo fail", response = String.class), + @ApiResponse(code = HttpStatus.SC_BAD_REQUEST, message = "Unprocessable iuiRouteInfo JSON REQUEST", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response addIuiRoute( + @ApiParam(value = "iuiRoute Instance Info", required = true) IuiRouteInfo iuiRouteInfo) { + IuiRouteInfo new_iuiRouteInfo = IuiRouteServiceWrapper.getInstance().saveIuiRouteInstance(iuiRouteInfo); + URI returnURI = + uriInfo.getAbsolutePathBuilder() + .path("/" + new_iuiRouteInfo.getServiceName()).build(); + return Response.created(returnURI).entity(new_iuiRouteInfo).build(); + + } + + @GET + @Path("/{serviceName}") + @ApiOperation(value = "get one iuiRoute ",code = HttpStatus.SC_OK, response = IuiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "IuiRouteInfo not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable IuiRouteInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get IuiRouteInfo fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public IuiRouteInfo getIuiRoute( + @ApiParam(value = "iuiRoute serviceName", required = true) @PathParam("serviceName") String serviceName) { + + return IuiRouteServiceWrapper.getInstance().getIuiRouteInstance(serviceName); + + } + + @PUT + @Path("/{serviceName}") + @ApiOperation(value = "update one iuiRoute by serviceName", code = HttpStatus.SC_CREATED,response = IuiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable IuiRouteInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "update IuiRouteInfo fail", response = String.class), + @ApiResponse(code = HttpStatus.SC_BAD_REQUEST, message = "Unprocessable IuiRouteInfo JSON REQUEST", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response updateIuiRoute( + @ApiParam(value = "iuiRoute serviceName", required = true) @PathParam("serviceName") String serviceName, + @ApiParam(value = "iuiRoute Instance Info", required = true) IuiRouteInfo iuiRouteInfo) { + + IuiRouteInfo new_iuiRouteInfo = IuiRouteServiceWrapper.getInstance().updateIuiRouteInstance(serviceName,iuiRouteInfo); + URI returnURI = + uriInfo.getAbsolutePathBuilder() + .path("/" + serviceName).build(); + return Response.created(returnURI).entity(new_iuiRouteInfo).build(); + } + + @DELETE + @Path("/{serviceName}") + @ApiOperation(value = "delete one iuiRoute by serviceName", code = HttpStatus.SC_NO_CONTENT) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_NO_CONTENT, message = "delete IuiRouteInfo succeed "), + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "IuiRouteInfo not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "delete IuiRouteInfo fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public void deleteIuiRoute( + @ApiParam(value = "iuiRoute serviceName", required = true) @PathParam("serviceName") String serviceName) { + + IuiRouteServiceWrapper.getInstance().deleteIuiRoute(serviceName,"*"); + + } + + @PUT + @Path("/{serviceName}/status/{status}") + @ApiOperation(value = "update one iuiRoute status by serviceName ",code = HttpStatus.SC_CREATED, response = IuiRouteInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable IuiRouteInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "IuiRouteInfo not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "update IuiRouteInfo status fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response updateIuiRouteStatus( + @ApiParam(value = "iuiRoute serviceName", required = true) @PathParam("serviceName") String serviceName, + @ApiParam(value = "iuiRoute status,1:abled 0:disabled", required = true) @PathParam("status") String status) { + + IuiRouteInfo new_iuiRouteInfo = IuiRouteServiceWrapper.getInstance().updateIuiRouteStatus(serviceName,status); + URI returnURI = + uriInfo.getAbsolutePathBuilder() + .path("/" + serviceName).build(); + return Response.created(returnURI).entity(new_iuiRouteInfo).build(); + + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/MetricsResource.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/MetricsResource.java new file mode 100644 index 0000000..d15799b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/MetricsResource.java @@ -0,0 +1,49 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.apache.http.impl.client.CloseableHttpClient; +import org.openo.msb.api.MetricsInfo; +import org.openo.msb.wrapper.MetricsServiceWrapper; + +import com.codahale.metrics.annotation.Timed; + +@Path("/metrics") +@Api(tags = { "metrics" }) +@Produces(MediaType.APPLICATION_JSON) +public class MetricsResource { + + + @GET + @Path("/") + @ApiOperation(value = "get Metrics Info ", response = MetricsInfo.class) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public MetricsInfo getMetricsInfo() { + return MetricsServiceWrapper.getMetricsInfo(); + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/MicroServiceResource.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/MicroServiceResource.java new file mode 100644 index 0000000..5ea80ae --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/MicroServiceResource.java @@ -0,0 +1,247 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.net.URI; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.openo.msb.api.MicroServiceFullInfo; +import org.openo.msb.api.MicroServiceInfo; +import org.openo.msb.wrapper.MicroServiceWrapper; +import org.openo.msb.wrapper.util.MicroServiceUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.codahale.metrics.annotation.Timed; + +@Path("/services") +@Api(tags = {"MSB-Service Resource"}) +@Produces(MediaType.APPLICATION_JSON) +public class MicroServiceResource { + + @Context + UriInfo uriInfo; // actual uri info + + + private static final Logger LOGGER = LoggerFactory.getLogger(MicroServiceResource.class); + + @GET + @Path("/") + @ApiOperation(value = "get all microservices ", code = HttpStatus.SC_OK, response = MicroServiceFullInfo.class, responseContainer = "List") + @ApiResponses(value = {@ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get microservice List fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public MicroServiceFullInfo[] getMicroService() { + return MicroServiceWrapper.getInstance().getAllMicroServiceInstances(); + } + + @POST + @Path("/") + @ApiOperation(value = "add one microservice ", code = HttpStatus.SC_CREATED, response = MicroServiceFullInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable MicroServiceInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "add microservice fail", response = String.class), + @ApiResponse(code = HttpStatus.SC_BAD_REQUEST, message = "Unprocessable MicroServiceInfo JSON REQUEST", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response addMicroService( + @ApiParam(value = "MicroServiceInfo Instance Info", required = true) MicroServiceInfo microServiceInfo, + @Context HttpServletRequest request, + @ApiParam(value = "createOrUpdate", required = false) @QueryParam("createOrUpdate") @DefaultValue("true") boolean createOrUpdate, + @ApiParam(value = "port", required = false) @QueryParam("port") @DefaultValue("") String port) { + + String ip=MicroServiceUtil.getRealIp(request); + + MicroServiceFullInfo microServiceFullInfo = + MicroServiceWrapper.getInstance().saveMicroServiceInstance(microServiceInfo, + createOrUpdate,ip,port); + URI returnURI = + uriInfo.getAbsolutePathBuilder() + .path("/" + microServiceInfo.getServiceName() + "/version/" + + microServiceInfo.getVersion()).build(); + return Response.created(returnURI).entity(microServiceFullInfo).build(); + } + + + + @GET + @Path("/{serviceName}/version/{version}") + @ApiOperation(value = "get one microservice ", code = HttpStatus.SC_OK, response = MicroServiceFullInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "microservice not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable MicroServiceInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "get microservice fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public MicroServiceFullInfo getMicroService( + @ApiParam(value = "microservice serviceName") @PathParam("serviceName") String serviceName, + @ApiParam(value = "microservice version,if the version is empty, please enter \"null\"") @PathParam("version") @DefaultValue("") String version, + @ApiParam(value = "port", required = false) @QueryParam("port") @DefaultValue("") String port) { + + + return MicroServiceWrapper.getInstance().getMicroServiceInstance(serviceName, version,port); + + + } + + @PUT + @Path("/{serviceName}/version/{version}") + @ApiOperation(value = "update one microservice by serviceName and version", code = HttpStatus.SC_CREATED, response = MicroServiceFullInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable MicroServiceInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "update microservice fail", response = String.class), + @ApiResponse(code = HttpStatus.SC_BAD_REQUEST, message = "Unprocessable MicroServiceInfo JSON REQUEST", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response updateMicroService( + @ApiParam(value = "microservice serviceName") @PathParam("serviceName") String serviceName, + @ApiParam(value = "microservice version,if the version is empty, please enter \"null\"") @PathParam("version") @DefaultValue("") String version, + @ApiParam(value = "microservice Instance Info", required = true) MicroServiceInfo microServiceInfo) { + + MicroServiceFullInfo microServiceFullInfo = MicroServiceWrapper.getInstance().updateMicroServiceInstance(serviceName, version, + microServiceInfo); + return Response.created(uriInfo.getAbsolutePathBuilder().build()).entity(microServiceFullInfo).build(); + + } + + @PUT + @Path("/{serviceName}/version/{version}/nodes/{ip}/{port}") + @ApiOperation(value = "update single node by serviceName and version and node", code = HttpStatus.SC_CREATED, response = MicroServiceFullInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable MicroServiceInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "microservice not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "update node fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response updateNode( + @ApiParam(value = "microservice serviceName", required = true) @PathParam("serviceName") String serviceName, + @ApiParam(value = "microservice version,if the version is empty, please enter \"null\"", required = false) @PathParam("version") @DefaultValue("") String version, + @ApiParam(value = "ip") @PathParam("ip") String ip, + @ApiParam(value = "port") @PathParam("port") String port, + @ApiParam(value = "ttl") @QueryParam("ttl") int ttl) { + + MicroServiceFullInfo microServiceFullInfo = MicroServiceWrapper.getInstance().updateMicroServiceNode(serviceName, version, ip,port, ttl); + + return Response.created(uriInfo.getAbsolutePathBuilder().build()).entity(microServiceFullInfo).build(); + + } + + + + @DELETE + @Path("/{serviceName}/version/{version}/nodes/{ip}/{port}") + @ApiOperation(value = "delete single node by serviceName and version and node", code = HttpStatus.SC_NO_CONTENT) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_NO_CONTENT, message = "delete node succeed "), + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "node not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable MicroServiceInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "delete node fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public void deleteNode( + @ApiParam(value = "microservice serviceName", required = true) @PathParam("serviceName") String serviceName, + @ApiParam(value = "microservice version,if the version is empty, please enter \"null\"", required = false) @PathParam("version") @DefaultValue("") String version, + @ApiParam(value = "ip") @PathParam("ip") String ip, + @ApiParam(value = "port") @PathParam("port") String port) { + + MicroServiceWrapper.getInstance().deleteMicroServiceInstance(serviceName, version, ip,port); + + } + + + @DELETE + @Path("/{serviceName}/version/{version}") + @ApiOperation(value = "delete one full microservice by serviceName and version", code = HttpStatus.SC_NO_CONTENT) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_NO_CONTENT, message = "delete microservice succeed "), + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "microservice not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable MicroServiceInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "delete microservice fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public void deleteMicroService( + @ApiParam(value = "microservice serviceName", required = true) @PathParam("serviceName") String serviceName, + @ApiParam(value = "microservice version,if the version is empty, please enter \"null\"", required = false) @PathParam("version") @DefaultValue("") String version, + @ApiParam(value = "port", required = false) @QueryParam("port") @DefaultValue("") String port) { + + + MicroServiceWrapper.getInstance().deleteMicroService(serviceName, version,port); + + } + + @PUT + @Path("/{serviceName}/version/{version}/status/{status}") + @ApiOperation(value = "update microservice status by serviceName and version", code = HttpStatus.SC_CREATED, response = MicroServiceFullInfo.class) + @ApiResponses(value = { + @ApiResponse(code = HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, message = "Unprocessable MicroServiceInfo Entity ", response = String.class), + @ApiResponse(code = HttpStatus.SC_NOT_FOUND, message = "microservice not found", response = String.class), + @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = "update status fail", response = String.class)}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public Response updateServiceStatus( + @ApiParam(value = "microservice serviceName", required = true) @PathParam("serviceName") String serviceName, + @ApiParam(value = "microservice version,if the version is empty, please enter \"null\"", required = false) @PathParam("version") @DefaultValue("") String version, + @ApiParam(value = "status,1:abled 0:disabled") @PathParam("status") String status) { + + MicroServiceFullInfo microServiceFullInfo = MicroServiceWrapper.getInstance().updateMicroServiceStatus(serviceName, version,status); + + return Response.created(uriInfo.getAbsolutePathBuilder().build()).entity(microServiceFullInfo).build(); + + } + +// @PUT +// @Path("/ttl") +// @ApiOperation(value = "test json date iso8601 ", code = 200, response = String.class) +// @Produces(MediaType.APPLICATION_JSON) +// @Timed +// public String testIso8601( +// @ApiParam(value = "microservice Instance Info", required = true) MicroServiceFullInfo microServiceInfo) { +// Set nodes=microServiceInfo.getNodes(); +// String rtn="rtn:"; +// for(NodeInfo node:nodes){ +// Date date=node.getExpiration(); +// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); +// String datettl=sdf.format(date); +// rtn+=datettl; +// } +// +// +// return rtn; +// } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/ServiceAccessResource.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/ServiceAccessResource.java new file mode 100644 index 0000000..6a2d741 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/resources/ServiceAccessResource.java @@ -0,0 +1,65 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.resources; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.util.List; + +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +import org.openo.msb.api.ServiceAccessInfo; +import org.openo.msb.wrapper.ServiceAccessWrapper; + +import com.codahale.metrics.annotation.Timed; + + +@Path("/serviceaccess") +@Api(tags = {"ServiceAccess"}) +@Produces(MediaType.APPLICATION_JSON) +public class ServiceAccessResource { + + @GET + @Path("/{serviceName}") + @ApiOperation(value = "get the msb access address of the service ", response = ServiceAccessInfo.class) + @ApiResponses(value = {@ApiResponse(code = 500, message = "get access address error ")}) + @Produces(MediaType.APPLICATION_JSON) + @Timed + public List getApiRoute( + @ApiParam(value = "serviceName") @PathParam("serviceName") String serviceName, + @ApiParam(value = "service type", allowableValues = "api,iui,custom,p2p") @QueryParam("type") String serviceType, + @ApiParam(value = "version") @QueryParam("version") @DefaultValue("") String version, + @ApiParam(hidden = true) @HeaderParam("Host") String host) { + + host=host.split(":")[0]; + + return ServiceAccessWrapper.getInstance().getApiRouteAccessAddr(serviceType, serviceName, + version, host); + + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/ApiRouteServiceWrapper.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/ApiRouteServiceWrapper.java new file mode 100644 index 0000000..6f0ed7d --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/ApiRouteServiceWrapper.java @@ -0,0 +1,653 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.openo.msb.api.ApiRouteInfo; +import org.openo.msb.api.DiscoverInfo; +import org.openo.msb.api.RouteServer; +import org.openo.msb.api.exception.ExtendedInternalServerErrorException; +import org.openo.msb.api.exception.ExtendedNotFoundException; +import org.openo.msb.api.exception.ExtendedNotSupportedException; +import org.openo.msb.wrapper.util.JedisUtil; +import org.openo.msb.wrapper.util.RegExpTestUtil; +import org.openo.msb.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.Jedis; + + + +/** + * @ClassName: ApiRouteServiceWrapper + * @Description: TODO(ApiRoute服务类) + * @author tanghua10186366 + * @date 2015年9月25日 上午9:44:04 + * + */ +public class ApiRouteServiceWrapper { + + private static final Logger LOGGER = LoggerFactory.getLogger(ApiRouteServiceWrapper.class); + + + private static ApiRouteServiceWrapper instance = new ApiRouteServiceWrapper(); + + private ApiRouteServiceWrapper() {} + + public static ApiRouteServiceWrapper getInstance() { + return instance; + } + + /** + * @Title: getAllApiRouteInstances + * @Description: TODO(获取全部服务列表) + * @param: @return + * @return: ApiRouteInfoBean[] + */ + public ApiRouteInfo[] getAllApiRouteInstances() { + + + Jedis jedis = null; + ApiRouteInfo[] apiRouteList = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + + // 获取全部服务列表 + String routekey = + RouteUtil + .getPrefixedKey("", RouteUtil.APIROUTE, "*", RouteUtil.ROUTE_PATH_INFO); + Set routeSet = jedis.keys(routekey); + apiRouteList = new ApiRouteInfo[routeSet.size()]; + + int i = 0; + for (String routePath : routeSet) { + String[] routePathArray = routePath.split(":"); + ApiRouteInfo apiRoute = + getApiRouteInstance(routePathArray[3], routePathArray[4], jedis); + apiRouteList[i] = apiRoute; + i++; + } + + + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new ExtendedInternalServerErrorException("call redis throw exception:" + + e.getMessage()); + + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + return apiRouteList; + } + + + + public static boolean checkRedisConnect() { + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis != null) { + return true; + } + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + return false; + } + + /** + * @Title: getApiRouteInstance + * @Description: TODO(通过服务名+版本号获取单个服务对象信息) + * @param: @param serviceName + * @param: @param version + * @param: @return + * @return: ApiRouteInfo + */ + public ApiRouteInfo getApiRouteInstance(String serviceName, String version) { + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new ExtendedNotSupportedException("version (" + version + + ") is not a valid format"); + } + } + + + ApiRouteInfo apiRouteInfo = null; + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + + apiRouteInfo = getApiRouteInstance(serviceName, version, jedis); + + + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new ExtendedInternalServerErrorException("call redis throw exception:" + + e.getMessage()); + + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + if (null == apiRouteInfo) { + String errInfo = + "ApiRouteInfo not found: serviceName-" + serviceName + " ,version-" + version; + LOGGER.warn(errInfo); + throw new ExtendedNotFoundException(errInfo); + + } + + return apiRouteInfo; + + } + + public ApiRouteInfo getApiRouteInstance(String serviceName, String version, Jedis jedis) + throws Exception { + if ("null".equals(version)) { + version = ""; + } + + ApiRouteInfo apiRouteInfo = null; + + + // 获取info信息 + String routekey = + RouteUtil.getPrefixedKey("", RouteUtil.APIROUTE, serviceName, version, + RouteUtil.ROUTE_PATH_INFO); + Map infomap = jedis.hgetAll(routekey); + if (!infomap.isEmpty()) { + apiRouteInfo = new ApiRouteInfo(); + apiRouteInfo.setServiceName(serviceName); + apiRouteInfo.setVersion(version); + apiRouteInfo.setUrl(infomap.get("url")); + apiRouteInfo.setMetricsUrl(infomap.get("metricsUrl")); + apiRouteInfo.setApiJson(infomap.get("apijson")); + apiRouteInfo.setApiJsonType(infomap.get("apiJsonType")); + apiRouteInfo.setControl(infomap.get("control")); + apiRouteInfo.setStatus(infomap.get("status")); + apiRouteInfo.setVisualRange(infomap.get("visualRange")); + apiRouteInfo.setUseOwnUpstream(infomap.get("useOwnUpstream")); + + + // 获取负载均衡信息 + String serviceLBkey = + RouteUtil.getPrefixedKey("", RouteUtil.APIROUTE, serviceName, version, + RouteUtil.ROUTE_PATH_LOADBALANCE); + Set serviceLBset = jedis.keys(serviceLBkey + ":*"); + int serverNum = serviceLBset.size(); + RouteServer[] apiRouteServerList = new RouteServer[serverNum]; + int i = 0; + for (String serviceInfo : serviceLBset) { + Map serviceLBmap = jedis.hgetAll(serviceInfo); + RouteServer server = new RouteServer(); + server.setIp(serviceLBmap.get("ip")); + server.setPort(serviceLBmap.get("port")); + server.setWeight(Integer.parseInt(serviceLBmap.get("weight"))); + apiRouteServerList[i] = server; + i++; + } + + apiRouteInfo.setServers(apiRouteServerList); + + // 获取生命周期信息 + +// ApiRouteLifeCycle lifeCycle = new ApiRouteLifeCycle(); +// String serviceLifekey = +// RouteUtil.getPrefixedKey("", RouteUtil.APIROUTE, serviceName, version, +// RouteUtil.APIROUTE_PATH_LIFE); +// Map serviceLifeMap = jedis.hgetAll(serviceLifekey); +// +// lifeCycle.setInstallPath(serviceLifeMap.get("path")); +// lifeCycle.setStartScript(serviceLifeMap.get("start")); +// lifeCycle.setStopScript(serviceLifeMap.get("stop")); +// +// apiRouteInfo.setLifeCycle(lifeCycle); + } + + + return apiRouteInfo; + } + + /** + * @Title: updateApiRouteInstance + * @Description: TODO(更新单个服务信息) + * @param: @param serviceName + * @param: @param version + * @param: @param apiRouteInfo + * @param: @return + * @return: ApiRouteInfo + */ + public synchronized ApiRouteInfo updateApiRouteInstance(String serviceName, String version, + ApiRouteInfo apiRouteInfo, String serverPort) { + + if ("null".equals(version)) { + version = ""; + } + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new ExtendedNotSupportedException("version (" + version + + ") is not a valid format"); + } + } + + + + + try { + + + if (serviceName.equals(apiRouteInfo.getServiceName()) + && version.equals(apiRouteInfo.getVersion())) { + // 删除已存在负载均衡服务器信息 + deleteApiRoute(serviceName, version, RouteUtil.ROUTE_PATH_LOADBALANCE + "*", + serverPort); + + } else { + // 如果已修改服务名或者版本号,先删除此服务全部已有信息 + deleteApiRoute(serviceName, version, "*", serverPort); + } + + + saveApiRouteInstance(apiRouteInfo, serverPort); + + + } catch (ExtendedNotSupportedException e) { + throw e; + } catch (Exception e) { + LOGGER.error("update ApiRoute throw exception", e); + throw new ExtendedInternalServerErrorException("update apiRouteInfo throw exception" + + e.getMessage()); + + } + + return apiRouteInfo; + + } + + /** + * @Title updateApiRouteStatus + * @Description TODO(更新单个服务状态) + * @param serviceName + * @param version + * @param status + * @return + * @return RouteResult + */ + public synchronized ApiRouteInfo updateApiRouteStatus(String serviceName, String version, + String status) { + + if ("null".equals(version)) { + version = ""; + } + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new ExtendedNotSupportedException("version (" + version + + ") is not a valid format"); + } + } + + if (!RouteUtil.contain(RouteUtil.statusRangeMatches, status)) { + throw new ExtendedNotSupportedException( + "save ApiRouteInfo Status FAIL:status is wrong,value range:(" + + RouteUtil.show(RouteUtil.statusRangeMatches) + ")"); + } + + ApiRouteInfo new_apiRouteInfo = getApiRouteInstance(serviceName, version); + + + // 准备info信息 + String serviceInfokey = + RouteUtil.getPrefixedKey("", RouteUtil.APIROUTE, serviceName, version, + RouteUtil.ROUTE_PATH_INFO); + Map serviceInfoMap = new HashMap(); + serviceInfoMap.put("status", status); + + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new Exception("fetch from jedis pool failed,null object!"); + } + // 保存info信息 + jedis.hmset(serviceInfokey, serviceInfoMap); + new_apiRouteInfo.setStatus(status); + + + } catch (Exception e) { + LOGGER.error("update ApiRoute status throw exception", e); + throw new ExtendedInternalServerErrorException("update ApiRoute status throw exception" + + e.getMessage()); + + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + return new_apiRouteInfo; + } + + + /** + * @Title: saveApiRouteInstance + * @Description: TODO(存储单个服务信息) + * @param: @param apiRouteInfo + * @param: @return + * @return: ApiRouteInfo + */ + public synchronized ApiRouteInfo saveApiRouteInstance(ApiRouteInfo apiRouteInfo, + String serverPort) { + + + + if (StringUtils.isBlank(apiRouteInfo.getServiceName()) + || apiRouteInfo.getServers().length == 0) { + throw new ExtendedNotSupportedException( + "save apiRouteInfo FAIL: Some required fields are empty"); + } + + if (StringUtils.isNotBlank(apiRouteInfo.getVersion())) { + if (!RegExpTestUtil.versionRegExpTest(apiRouteInfo.getVersion())) { + throw new ExtendedNotSupportedException("version (" + apiRouteInfo.getVersion() + + ") is not a valid format"); + } + } + + if (StringUtils.isNotBlank(apiRouteInfo.getUrl())) { + if (!RegExpTestUtil.urlRegExpTest(apiRouteInfo.getUrl())) { + throw new ExtendedNotSupportedException( + "save apiRouteInfo FAIL:url is not a valid format(url must be begin with /)"); + + } + } + + if (!RouteUtil.contain(RouteUtil.visualRangeRange, apiRouteInfo.getVisualRange())) { + throw new ExtendedNotSupportedException( + "save apiRouteInfo FAIL:VisualRange is wrong,value range:(" + + RouteUtil.show(RouteUtil.visualRangeMatches) + ")"); + } + + if (!RouteUtil.contain(RouteUtil.controlRangeMatches, apiRouteInfo.getControl())) { + throw new ExtendedNotSupportedException( + "save apiRouteInfo FAIL:control is wrong,value range:(" + + RouteUtil.show(RouteUtil.controlRangeMatches) + ")"); + } + + if (!RouteUtil.contain(RouteUtil.statusRangeMatches, apiRouteInfo.getStatus())) { + throw new ExtendedNotSupportedException( + "save apiRouteInfo FAIL:status is wrong,value range:(" + + RouteUtil.show(RouteUtil.statusRangeMatches) + ")"); + } + + if (!RouteUtil.contain(RouteUtil.useOwnUpstreamRangeMatches, apiRouteInfo.getUseOwnUpstream())) { + throw new ExtendedNotSupportedException( + "save apiRouteInfo FAIL:useOwnUpstream is wrong,value range:(" + + RouteUtil.show(RouteUtil.useOwnUpstreamRangeMatches) + ")"); + } + + // 检查服务实例格式 + RouteServer[] serverList = apiRouteInfo.getServers(); + for (int i = 0; i < serverList.length; i++) { + RouteServer server = serverList[i]; + if (!RegExpTestUtil.ipRegExpTest(server.getIp())) { + throw new ExtendedNotSupportedException("save apiRouteInfo FAIL:IP(" + + server.getIp() + ")is not a valid ip address"); + } + + if (!RegExpTestUtil.portRegExpTest(server.getPort())) { + throw new ExtendedNotSupportedException("save apiRouteInfo FAIL:Port(" + + server.getPort() + ")is not a valid Port address"); + } + } + + // 准备info信息 + String serviceInfokey = + RouteUtil.getPrefixedKey(serverPort, RouteUtil.APIROUTE, + apiRouteInfo.getServiceName().trim(), apiRouteInfo.getVersion().trim(), + RouteUtil.ROUTE_PATH_INFO); + Map serviceInfoMap = new HashMap(); + serviceInfoMap.put("url", "/".equals(apiRouteInfo.getUrl().trim()) ? "" : apiRouteInfo + .getUrl().trim()); + serviceInfoMap.put("apijson", apiRouteInfo.getApiJson()); + serviceInfoMap.put("apiJsonType", apiRouteInfo.getApiJsonType()); + serviceInfoMap.put("metricsUrl", apiRouteInfo.getMetricsUrl()); + serviceInfoMap.put("control", apiRouteInfo.getControl()); + serviceInfoMap.put("status", apiRouteInfo.getStatus()); + serviceInfoMap.put("visualRange", apiRouteInfo.getVisualRange()); + serviceInfoMap.put("useOwnUpstream", apiRouteInfo.getUseOwnUpstream()); + + // 准备负载均衡信息 + String serviceLBkey = + RouteUtil.getPrefixedKey(serverPort, RouteUtil.APIROUTE, + apiRouteInfo.getServiceName(), apiRouteInfo.getVersion(), + RouteUtil.ROUTE_PATH_LOADBALANCE); + + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + // 保存info信息 + jedis.hmset(serviceInfokey, serviceInfoMap); + + // 保存负载均衡信息 + for (int i = 0; i < serverList.length; i++) { + Map servermap = new HashMap(); + RouteServer server = serverList[i]; + + servermap.put("ip", server.getIp()); + servermap.put("port", server.getPort()); + servermap.put("weight", Integer.toString(server.getWeight())); + + jedis.hmset(serviceLBkey + ":server" + (i + 1), servermap); + } + // 保存生命周期信息 + +// ApiRouteLifeCycle lifeCycle = apiRouteInfo.getLifeCycle(); +// if (lifeCycle != null) { +// String serviceLifekey = +// RouteUtil.getPrefixedKey(serverPort, RouteUtil.APIROUTE, +// apiRouteInfo.getServiceName(), apiRouteInfo.getVersion(), +// RouteUtil.APIROUTE_PATH_LIFE); +// Map serviceLifeMap = new HashMap(); +// serviceLifeMap.put("path", lifeCycle.getInstallPath()); +// serviceLifeMap.put("start", lifeCycle.getStartScript()); +// serviceLifeMap.put("stop", lifeCycle.getStopScript()); +// jedis.hmset(serviceLifekey, serviceLifeMap); +// } + + + + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new ExtendedInternalServerErrorException("call redis throw exception:" + + e.getMessage()); + + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + return apiRouteInfo; + } + + + + /** + * @Title: deleteApiRoute + * @Description: TODO(删除单个服务信息) + * @param: @param type + * @param: @param serviceName + * @param: @param version + * @param: @param delKey + * @param: @return + * @return: void + */ + public synchronized void deleteApiRoute(String serviceName, String version, String delKey, + String serverPort) { + + if ("null".equals(version)) { + version = ""; + } + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new ExtendedNotSupportedException("version (" + version + + ") is not a valid format"); + } + } + + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + + // 获取info信息 + String routekey = + RouteUtil.getPrefixedKey(serverPort, RouteUtil.APIROUTE, serviceName, version, + delKey); + Set infoSet = jedis.keys(routekey); + + if (infoSet.isEmpty()) { + throw new ExtendedNotFoundException("delete ApiRoute FAIL:serviceName-" + + serviceName + ",version:" + version + " not fond "); + } + + String[] paths = new String[infoSet.size()]; + + // Set-->数组 + infoSet.toArray(paths); + + jedis.del(paths); + + + } catch (ExtendedNotFoundException e) { + throw e; + } catch (Exception e) { + LOGGER.error("delete ApiRoute throw exception", e); + throw new ExtendedInternalServerErrorException("delete ApiRoute throw exception:" + + e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + + } + + /** + * @Title: getAllApiDocs + * @Description: TODO(获取本地ext\initSwaggerJson目录的全部json文件目录) + * @param: @return + * @return: String[] + */ + public String[] getAllApiDocs() { + URL apiDocsPath = ApiRouteServiceWrapper.class.getResource("/ext/initSwaggerJson"); + if (apiDocsPath != null) { + String path = apiDocsPath.getPath(); + + try { + return readfile(path); + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + LOGGER.error("read ApiDocs Files throw FileNotFoundException", e); + throw new ExtendedInternalServerErrorException("read ApiDocs Files throw FileNotFoundException:" + + e.getMessage()); + } catch (IOException e) { + // TODO Auto-generated catch block + LOGGER.error("read ApiDocs Files throw IOexception", e); + throw new ExtendedInternalServerErrorException("read ApiDocs Files throw IOexception:" + + e.getMessage()); + } + + } + + + return null; + } + + /** + * 读取某个文件夹下的所有文件 + */ + public String[] readfile(String filepath) throws FileNotFoundException, IOException { + File file = new File(filepath); + if (file.isDirectory()) { + String[] filelist = file.list(); + return filelist; + } + return null; + } + + public String getApiGatewayPort() { + // return JedisUtil.serverIp+":"+JedisUtil.serverPort; + return System.getenv("APIGATEWAY_EXPOSE_PORT") == null ? String + .valueOf(JedisUtil.serverPort) : System.getenv("APIGATEWAY_EXPOSE_PORT"); + + } + + public DiscoverInfo getServiceDiscoverInfo() { + return RouteUtil.discoverInfo; + + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/CustomRouteServiceWrapper.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/CustomRouteServiceWrapper.java new file mode 100644 index 0000000..85d4d4e --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/CustomRouteServiceWrapper.java @@ -0,0 +1,474 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.openo.msb.api.CustomRouteInfo; +import org.openo.msb.api.RouteServer; +import org.openo.msb.api.exception.ExtendedInternalServerErrorException; +import org.openo.msb.api.exception.ExtendedNotFoundException; +import org.openo.msb.api.exception.ExtendedNotSupportedException; +import org.openo.msb.wrapper.util.JedisUtil; +import org.openo.msb.wrapper.util.RegExpTestUtil; +import org.openo.msb.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.Jedis; + +public class CustomRouteServiceWrapper { + + + private static final Logger LOGGER = LoggerFactory.getLogger(CustomRouteServiceWrapper.class); + + private static CustomRouteServiceWrapper instance = new CustomRouteServiceWrapper(); + + private CustomRouteServiceWrapper() {} + + public static CustomRouteServiceWrapper getInstance() { + return instance; + } + + + /** + * @Title: getAllCustomRouteService + * @Description: TODO(获取全部内容服务列表) + * @param: @return + * @return: CustomRouteInfo[] + */ + public CustomRouteInfo[] getAllCustomRouteInstances() { + + + Jedis jedis = null; + CustomRouteInfo[] customRouteList = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + + // 获取全部服务列表 + String routekey = + RouteUtil.getPrefixedKey("", RouteUtil.CUSTOMROUTE, "*", + RouteUtil.ROUTE_PATH_INFO); + Set routeSet = jedis.keys(routekey); + customRouteList = new CustomRouteInfo[routeSet.size()]; + + int i = 0; + for (String routePath : routeSet) { + String[] routePathArray = routePath.split(":"); + CustomRouteInfo customRoute = getCustomRouteInstance(routePathArray[3], jedis); + customRouteList[i] = customRoute; + i++; + } + + + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new ExtendedInternalServerErrorException("call redis throw exception:" + + e.getMessage()); + + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + return customRouteList; + } + + + + /** + * @Title: getCustomRouteInstance + * @Description: TODO(通过服务名获取单个内容服务对象信息) + * @param: @param serviceName + * @param: @return + * @return: CustomRouteInfo + */ + public CustomRouteInfo getCustomRouteInstance(String serviceName) { + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + CustomRouteInfo customRouteInfo; + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + + customRouteInfo = getCustomRouteInstance(serviceName, jedis); + + + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new ExtendedInternalServerErrorException("call redis throw exception:" + + e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + if (null == customRouteInfo) { + String errInfo = "customRouteInfo not found: serviceName-" + serviceName; + LOGGER.warn(errInfo); + throw new ExtendedNotFoundException(errInfo); + + } + + return customRouteInfo; + + } + + public CustomRouteInfo getCustomRouteInstance(String serviceName, Jedis jedis) throws Exception { + + + CustomRouteInfo customRouteInfo = null; + + + // 获取info信息 + String routekey = + RouteUtil.getPrefixedKey("", RouteUtil.CUSTOMROUTE, serviceName, + RouteUtil.ROUTE_PATH_INFO); + Map infomap = jedis.hgetAll(routekey); + if (!infomap.isEmpty()) { + customRouteInfo = new CustomRouteInfo(); + customRouteInfo.setServiceName(serviceName); + customRouteInfo.setUrl(infomap.get("url")); + customRouteInfo.setControl(infomap.get("control")); + customRouteInfo.setStatus(infomap.get("status")); + customRouteInfo.setVisualRange(infomap.get("visualRange")); + customRouteInfo.setUseOwnUpstream(infomap.get("useOwnUpstream")); + + + // 获取负载均衡信息 + String serviceLBkey = + RouteUtil.getPrefixedKey("", RouteUtil.CUSTOMROUTE, serviceName, + RouteUtil.ROUTE_PATH_LOADBALANCE); + Set serviceLBset = jedis.keys(serviceLBkey + ":*"); + int serverNum = serviceLBset.size(); + RouteServer[] CustomRouteServerList = new RouteServer[serverNum]; + int i = 0; + for (String serviceInfo : serviceLBset) { + Map serviceLBmap = jedis.hgetAll(serviceInfo); + RouteServer server = new RouteServer(); + server.setIp(serviceLBmap.get("ip")); + server.setPort(serviceLBmap.get("port")); + server.setWeight(Integer.parseInt(serviceLBmap.get("weight"))); + CustomRouteServerList[i] = server; + i++; + } + + customRouteInfo.setServers(CustomRouteServerList); + } + + + return customRouteInfo; + } + + /** + * @Title: updateCustomRouteInstance + * @Description: TODO(更新单个服务信息) + * @param: @param serviceName + * @param: @param CustomRouteInfo + * @param: @return + * @return: CustomRouteInfo + */ + public synchronized CustomRouteInfo updateCustomRouteInstance(String serviceName, + CustomRouteInfo customRouteInfo, String serverPort) { + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + try { + + if (serviceName.equals(customRouteInfo.getServiceName())) { + // 删除已存在负载均衡服务器信息 + deleteCustomRoute(serviceName, RouteUtil.ROUTE_PATH_LOADBALANCE + "*", serverPort); + } else { + // 如果已修改服务名,先删除此服务全部已有信息 + deleteCustomRoute(serviceName, "*", serverPort); + } + + + saveCustomRouteInstance(customRouteInfo, serverPort); + + + + } catch (ExtendedNotSupportedException e) { + throw e; + } catch (Exception e) { + LOGGER.error("updateCustomRoute throw exception", e); + throw new ExtendedInternalServerErrorException("update CustomRoute throw exception" + + e.getMessage()); + + } + + return customRouteInfo; + + } + + /** + * @Title updateCustomRouteStatus + * @Description TODO(更新单个服务状态) + * @param serviceName + * @param status + * @return + * @return RouteResult + */ + public synchronized CustomRouteInfo updateCustomRouteStatus(String serviceName, String status) { + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + if (!RouteUtil.contain(RouteUtil.statusRangeMatches, status)) { + throw new ExtendedNotSupportedException( + "save CustomRouteInfo Status FAIL:status is wrong,value range:(" + + RouteUtil.show(RouteUtil.statusRangeMatches) + ")"); + } + + CustomRouteInfo new_customRouteInfo = getCustomRouteInstance(serviceName); + + + + // 准备info信息 + String serviceInfokey = + RouteUtil.getPrefixedKey("", RouteUtil.CUSTOMROUTE, serviceName, + RouteUtil.ROUTE_PATH_INFO); + Map serviceInfoMap = new HashMap(); + serviceInfoMap.put("status", status); + + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + // 保存info信息 + jedis.hmset(serviceInfokey, serviceInfoMap); + new_customRouteInfo.setStatus(status); + + } catch (Exception e) { + + LOGGER.error("update CustomRoute status throw exception", e); + throw new ExtendedInternalServerErrorException( + "update CustomRoute status throw exception" + e.getMessage()); + + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + return new_customRouteInfo; + } + + /** + * @Title: saveCustomRouteInstance + * @Description: TODO(存储单个服务信息) + * @param: @param CustomRouteInfo + * @param: @return + * @return: CustomRouteInfo + */ + public synchronized CustomRouteInfo saveCustomRouteInstance(CustomRouteInfo customRouteInfo, + String serverPort) { + + if (StringUtils.isBlank(customRouteInfo.getServiceName()) + || customRouteInfo.getServers().length == 0) { + throw new ExtendedNotSupportedException( + "save CustomRouteInfo FAIL: Some required fields are empty"); + } + + + if (!RegExpTestUtil.urlRegExpTest(customRouteInfo.getServiceName())) { + throw new ExtendedNotSupportedException( + "save CustomRouteInfo FAIL: ServiceName is not a valid format(ServiceName must be begin with /)"); + + } + + if (StringUtils.isNotBlank(customRouteInfo.getUrl())){ + if (!RegExpTestUtil.urlRegExpTest(customRouteInfo.getUrl())) { + throw new ExtendedNotSupportedException( + "save CustomRouteInfo FAIL:url is not a valid format(url must be begin with /)"); + + } + } + + if (!RouteUtil.contain(RouteUtil.visualRangeRange, customRouteInfo.getVisualRange())) { + throw new ExtendedNotSupportedException( + "save CustomRouteInfo FAIL:VisualRange is wrong,value range:(" + + RouteUtil.show(RouteUtil.visualRangeMatches) + ")"); + } + + if (!RouteUtil.contain(RouteUtil.controlRangeMatches, customRouteInfo.getControl())) { + throw new ExtendedNotSupportedException( + "save CustomRouteInfo FAIL:control is wrong,value range:(" + + RouteUtil.show(RouteUtil.controlRangeMatches) + ")"); + } + + if (!RouteUtil.contain(RouteUtil.statusRangeMatches, customRouteInfo.getStatus())) { + throw new ExtendedNotSupportedException( + "save CustomRouteInfo FAIL:status is wrong,value range:(" + + RouteUtil.show(RouteUtil.statusRangeMatches) + ")"); + } + + if (!RouteUtil.contain(RouteUtil.useOwnUpstreamRangeMatches, customRouteInfo.getUseOwnUpstream())) { + throw new ExtendedNotSupportedException( + "save apiRouteInfo FAIL:useOwnUpstream is wrong,value range:(" + + RouteUtil.show(RouteUtil.useOwnUpstreamRangeMatches) + ")"); + } + + // 检查服务实例格式 + RouteServer[] serverList = customRouteInfo.getServers(); + for (int i = 0; i < serverList.length; i++) { + RouteServer server = serverList[i]; + if (!RegExpTestUtil.ipRegExpTest(server.getIp())) { + throw new ExtendedNotSupportedException("save CustomRouteInfo FAIL:IP(" + + server.getIp() + ")is not a valid ip address"); + } + + if (!RegExpTestUtil.portRegExpTest(server.getPort())) { + throw new ExtendedNotSupportedException("save CustomRouteInfo FAIL:Port(" + + server.getPort() + ")is not a valid Port address"); + } + } + + + // 准备info信息 + String serviceInfokey = + RouteUtil.getPrefixedKey(serverPort, RouteUtil.CUSTOMROUTE, + customRouteInfo.getServiceName().trim(), RouteUtil.ROUTE_PATH_INFO); + Map serviceInfoMap = new HashMap(); + serviceInfoMap.put("url", "/".equals(customRouteInfo.getUrl().trim()) + ? "" + : customRouteInfo.getUrl().trim()); + serviceInfoMap.put("control", customRouteInfo.getControl()); + serviceInfoMap.put("status", customRouteInfo.getStatus()); + serviceInfoMap.put("visualRange", customRouteInfo.getVisualRange()); + serviceInfoMap.put("useOwnUpstream", customRouteInfo.getUseOwnUpstream()); + + + + // 准备负载均衡信息 + String serviceLBkey = + RouteUtil.getPrefixedKey(serverPort, RouteUtil.CUSTOMROUTE, + customRouteInfo.getServiceName(), RouteUtil.ROUTE_PATH_LOADBALANCE); + + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + // 保存info信息 + jedis.hmset(serviceInfokey, serviceInfoMap); + + // 保存负载均衡信息 + + for (int i = 0; i < serverList.length; i++) { + Map servermap = new HashMap(); + RouteServer server = serverList[i]; + + servermap.put("ip", server.getIp()); + servermap.put("port", server.getPort()); + servermap.put("weight", Integer.toString(server.getWeight())); + + jedis.hmset(serviceLBkey + ":server" + (i + 1), servermap); + } + + + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new ExtendedInternalServerErrorException("call redis throw exception:" + + e.getMessage()); + + } finally { + JedisUtil.returnJedisInstance(jedis);; + } + + return customRouteInfo; + } + + + + /** + * @Title: deleteCustomRoute + * @Description: TODO(删除单个服务信息) + * @param: @param type + * @param: @param serviceName + * @param: @param delKey + * @param: @return + * @return: void + */ + public synchronized void deleteCustomRoute(String serviceName, String delKey, String serverPort) { + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + Jedis jedis = null; + + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + + // 获取info信息 + String routekey = + RouteUtil + .getPrefixedKey(serverPort, RouteUtil.CUSTOMROUTE, serviceName, delKey); + Set infoSet = jedis.keys(routekey); + + if (infoSet.isEmpty()) { + throw new ExtendedNotFoundException("delete CustomRoute FAIL:serviceName-" + + serviceName + " not fond "); + } + + + String[] paths = new String[infoSet.size()]; + + // Set-->数组 + infoSet.toArray(paths); + + jedis.del(paths); + + } catch (ExtendedNotFoundException e) { + throw e; + } catch (Exception e) { + + LOGGER.error("delete CustomRoute throw exception", e); + throw new ExtendedInternalServerErrorException("delete CustomRoute throw exception:" + + e.getMessage()); + + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/IuiRouteServiceWrapper.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/IuiRouteServiceWrapper.java new file mode 100644 index 0000000..d705172 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/IuiRouteServiceWrapper.java @@ -0,0 +1,453 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.openo.msb.api.IuiRouteInfo; +import org.openo.msb.api.RouteServer; +import org.openo.msb.api.exception.ExtendedInternalServerErrorException; +import org.openo.msb.api.exception.ExtendedNotFoundException; +import org.openo.msb.api.exception.ExtendedNotSupportedException; +import org.openo.msb.wrapper.util.JedisUtil; +import org.openo.msb.wrapper.util.RegExpTestUtil; +import org.openo.msb.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.Jedis; + +public class IuiRouteServiceWrapper { + + + private static final Logger LOGGER = LoggerFactory.getLogger(IuiRouteServiceWrapper.class); + + private static IuiRouteServiceWrapper instance = new IuiRouteServiceWrapper(); + + private IuiRouteServiceWrapper() {} + + public static IuiRouteServiceWrapper getInstance() { + return instance; + } + + + /** + * @Title: getAllIuiRouteService + * @Description: TODO(获取全部内容服务列表) + * @param: @return + * @return: IuiRouteInfo[] + */ + public IuiRouteInfo[] getAllIuiRouteInstances() { + + + Jedis jedis = null; + IuiRouteInfo[] iuiRouteList = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + + // 获取全部服务列表 + String routekey = + RouteUtil + .getPrefixedKey("", RouteUtil.IUIROUTE, "*", RouteUtil.ROUTE_PATH_INFO); + Set routeSet = jedis.keys(routekey); + iuiRouteList = new IuiRouteInfo[routeSet.size()]; + + int i = 0; + for (String routePath : routeSet) { + String[] routePathArray = routePath.split(":"); + IuiRouteInfo iuiRoute = getIuiRouteInstance(routePathArray[3], jedis); + iuiRouteList[i] = iuiRoute; + i++; + } + + + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new ExtendedInternalServerErrorException("call redis throw exception:" + + e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + return iuiRouteList; + } + + + + /** + * @Title: getIuiRouteInstance + * @Description: TODO(通过服务名获取单个内容服务对象信息) + * @param: @param serviceName + * @param: @return + * @return: IuiRouteInfo + */ + public IuiRouteInfo getIuiRouteInstance(String serviceName) { + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + IuiRouteInfo iuiRouteInfo = null; + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + + iuiRouteInfo = getIuiRouteInstance(serviceName, jedis); + + + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new ExtendedInternalServerErrorException("call redis throw exception:" + + e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + if (null == iuiRouteInfo) { + String errInfo = "iuiRouteInfo not found: serviceName-" + serviceName; + LOGGER.warn(errInfo); + throw new ExtendedNotFoundException(errInfo); + + } + + return iuiRouteInfo; + + } + + public IuiRouteInfo getIuiRouteInstance(String serviceName, Jedis jedis) throws Exception { + + + IuiRouteInfo iuiRouteInfo = null; + + + // 获取info信息 + String routekey = + RouteUtil.getPrefixedKey("", RouteUtil.IUIROUTE, serviceName, + RouteUtil.ROUTE_PATH_INFO); + Map infomap = jedis.hgetAll(routekey); + if (!infomap.isEmpty()) { + iuiRouteInfo = new IuiRouteInfo(); + iuiRouteInfo.setServiceName(serviceName); + iuiRouteInfo.setUrl(infomap.get("url")); + iuiRouteInfo.setControl(infomap.get("control")); + iuiRouteInfo.setStatus(infomap.get("status")); + iuiRouteInfo.setVisualRange(infomap.get("visualRange")); + iuiRouteInfo.setUseOwnUpstream(infomap.get("useOwnUpstream")); + + + // 获取负载均衡信息 + String serviceLBkey = + RouteUtil.getPrefixedKey("", RouteUtil.IUIROUTE, serviceName, + RouteUtil.ROUTE_PATH_LOADBALANCE); + Set serviceLBset = jedis.keys(serviceLBkey + ":*"); + int serverNum = serviceLBset.size(); + RouteServer[] iuiRouteServerList = new RouteServer[serverNum]; + int i = 0; + for (String serviceInfo : serviceLBset) { + Map serviceLBmap = jedis.hgetAll(serviceInfo); + RouteServer server = new RouteServer(); + server.setIp(serviceLBmap.get("ip")); + server.setPort(serviceLBmap.get("port")); + server.setWeight(Integer.parseInt(serviceLBmap.get("weight"))); + iuiRouteServerList[i] = server; + i++; + } + + iuiRouteInfo.setServers(iuiRouteServerList); + } + + + return iuiRouteInfo; + } + + /** + * @Title: updateIuiRouteInstance + * @Description: TODO(更新单个服务信息) + * @param: @param serviceName + * @param: @param IuiRouteInfo + * @param: @return + * @return: IuiRouteInfo + */ + public synchronized IuiRouteInfo updateIuiRouteInstance(String serviceName, + IuiRouteInfo iuiRouteInfo) { + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + try { + if (serviceName.equals(iuiRouteInfo.getServiceName())) { + // 删除已存在负载均衡服务器信息 + deleteIuiRoute(serviceName, RouteUtil.ROUTE_PATH_LOADBALANCE + "*"); + + } else { + // 如果已修改服务名,先删除此服务全部已有信息 + deleteIuiRoute(serviceName, "*"); + } + saveIuiRouteInstance(iuiRouteInfo); + + } catch (ExtendedNotSupportedException e) { + throw e; + } catch (Exception e) { + LOGGER.error("updateIuiRoute throw exception", e); + throw new ExtendedInternalServerErrorException("update IuiRouteInfo throw exception" + + e.getMessage()); + } + + return iuiRouteInfo; + + } + + /** + * @Title updateIuiRouteStatus + * @Description TODO(更新单个服务状态) + * @param serviceName + * @param status + * @return + * @return RouteResult + */ + public synchronized IuiRouteInfo updateIuiRouteStatus(String serviceName, String status) { + + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + if (!RouteUtil.contain(RouteUtil.statusRangeMatches, status)) { + throw new ExtendedNotSupportedException( + "save IuiRouteInfo Status FAIL:status is wrong,value range:(" + + RouteUtil.show(RouteUtil.statusRangeMatches) + ")"); + } + + IuiRouteInfo new_iuiRouteInfo = getIuiRouteInstance(serviceName); + + // 准备info信息 + String serviceInfokey = + RouteUtil.getPrefixedKey("", RouteUtil.IUIROUTE, serviceName, + RouteUtil.ROUTE_PATH_INFO); + Map serviceInfoMap = new HashMap(); + serviceInfoMap.put("status", status); + + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + // 保存info信息 + jedis.hmset(serviceInfokey, serviceInfoMap); + new_iuiRouteInfo.setStatus(status); + + } catch (Exception e) { + LOGGER.error("update IuiRoute status throw exception", e); + throw new ExtendedInternalServerErrorException( + "update IuiRouteInfo status throw exception" + e.getMessage()); + + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + return new_iuiRouteInfo; + } + + /** + * @Title: saveIuiRouteInstance + * @Description: TODO(存储单个服务信息) + * @param: @param IuiRouteInfo + * @param: @return + * @return: IuiRouteInfo + */ + public synchronized IuiRouteInfo saveIuiRouteInstance(IuiRouteInfo iuiRouteInfo) { + + if (StringUtils.isBlank(iuiRouteInfo.getServiceName()) + || iuiRouteInfo.getServers().length == 0) { + throw new ExtendedNotSupportedException( + "save iuiRouteInfo FAIL: Some required fields are empty"); + } + + if (StringUtils.isNotBlank(iuiRouteInfo.getUrl())){ + if (!RegExpTestUtil.urlRegExpTest(iuiRouteInfo.getUrl())) { + throw new ExtendedNotSupportedException( + "save iuiRouteInfo FAIL:url is not a valid format(url must be begin with /)"); + + } + } + + if (!RouteUtil.contain(RouteUtil.visualRangeRange, iuiRouteInfo.getVisualRange())) { + throw new ExtendedNotSupportedException( + "save iuiRouteInfo FAIL:VisualRange is wrong,value range:(" + + RouteUtil.show(RouteUtil.visualRangeMatches) + ")"); + } + + if (!RouteUtil.contain(RouteUtil.controlRangeMatches, iuiRouteInfo.getControl())) { + throw new ExtendedNotSupportedException( + "save iuiRouteInfo FAIL:control is wrong,value range:(" + + RouteUtil.show(RouteUtil.controlRangeMatches) + ")"); + } + + if (!RouteUtil.contain(RouteUtil.statusRangeMatches, iuiRouteInfo.getStatus())) { + throw new ExtendedNotSupportedException( + "save iuiRouteInfo FAIL:status is wrong,value range:(" + + RouteUtil.show(RouteUtil.statusRangeMatches) + ")"); + } + + if (!RouteUtil.contain(RouteUtil.useOwnUpstreamRangeMatches, iuiRouteInfo.getUseOwnUpstream())) { + throw new ExtendedNotSupportedException( + "save apiRouteInfo FAIL:useOwnUpstream is wrong,value range:(" + + RouteUtil.show(RouteUtil.useOwnUpstreamRangeMatches) + ")"); + } + + // 检查服务实例格式 + RouteServer[] serverList = iuiRouteInfo.getServers(); + for (int i = 0; i < serverList.length; i++) { + RouteServer server = serverList[i]; + if (!RegExpTestUtil.ipRegExpTest(server.getIp())) { + throw new ExtendedNotSupportedException("save iuiRouteInfo FAIL:IP(" + + server.getIp() + ")is not a valid ip address"); + } + + if (!RegExpTestUtil.portRegExpTest(server.getPort())) { + throw new ExtendedNotSupportedException("save iuiRouteInfo FAIL:Port(" + + server.getPort() + ")is not a valid Port address"); + } + } + + + // 准备info信息 + String serviceInfokey = + RouteUtil.getPrefixedKey("", RouteUtil.IUIROUTE, iuiRouteInfo.getServiceName().trim(), + RouteUtil.ROUTE_PATH_INFO); + Map serviceInfoMap = new HashMap(); + serviceInfoMap.put("url", "/".equals(iuiRouteInfo.getUrl().trim()) ? "" : iuiRouteInfo + .getUrl().trim()); + serviceInfoMap.put("control", iuiRouteInfo.getControl()); + serviceInfoMap.put("status", iuiRouteInfo.getStatus()); + serviceInfoMap.put("visualRange", iuiRouteInfo.getVisualRange()); + serviceInfoMap.put("useOwnUpstream", iuiRouteInfo.getUseOwnUpstream()); + + + // 准备负载均衡信息 + String serviceLBkey = + RouteUtil.getPrefixedKey("", RouteUtil.IUIROUTE, iuiRouteInfo.getServiceName(), + RouteUtil.ROUTE_PATH_LOADBALANCE); + + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + // 保存info信息 + jedis.hmset(serviceInfokey, serviceInfoMap); + + // 保存负载均衡信息 + for (int i = 0; i < serverList.length; i++) { + Map servermap = new HashMap(); + RouteServer server = serverList[i]; + + servermap.put("ip", server.getIp()); + servermap.put("port", server.getPort()); + servermap.put("weight", Integer.toString(server.getWeight())); + + jedis.hmset(serviceLBkey + ":server" + (i + 1), servermap); + } + + + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new ExtendedInternalServerErrorException("call redis throw exception:" + + e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis);; + } + + return iuiRouteInfo; + } + + + + /** + * @Title: deleteIuiRoute + * @Description: TODO(删除单个服务信息) + * @param: @param type + * @param: @param serviceName + * @param: @param delKey + * @param: @return + * @return: void + */ + public synchronized void deleteIuiRoute(String serviceName, String delKey) { + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new ExtendedInternalServerErrorException( + "fetch from jedis pool failed,null object!"); + } + + // 获取info信息 + String routekey = RouteUtil.getPrefixedKey("", RouteUtil.IUIROUTE, serviceName, delKey); + Set infoSet = jedis.keys(routekey); + + if (infoSet.isEmpty()) { + throw new ExtendedNotFoundException("delete IuiRoute FAIL:serviceName-" + + serviceName + " not fond "); + } + + String[] paths = new String[infoSet.size()]; + + // Set-->数组 + infoSet.toArray(paths); + + jedis.del(paths); + + + } catch (ExtendedNotFoundException e) { + throw e; + } catch (Exception e) { + LOGGER.error("delete IuiRoute throw exception", e); + throw new ExtendedInternalServerErrorException("delete IuiRoute throw exception:" + + e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/MetricsServiceWrapper.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/MetricsServiceWrapper.java new file mode 100644 index 0000000..656acb8 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/MetricsServiceWrapper.java @@ -0,0 +1,92 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper; + +import java.io.IOException; + +import org.apache.http.ParseException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.openo.msb.api.MetricsInfo; +import org.openo.msb.wrapper.util.MetricsUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class MetricsServiceWrapper { + + private static final Logger LOGGER = LoggerFactory + .getLogger(MetricsServiceWrapper.class); + + public static MetricsInfo getMetricsInfo() { + + String metricsUrl = MetricsUtil.adminContextPath; + String metricsJson = sendGetRequest(metricsUrl); + + metricsJson = metricsJson.replace("E_", "");//.replaceAll("(?![0-9])(\\.)(?![0-9])", "_").replace("-", "_") + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, + false); + MetricsInfo metricsInfo = new MetricsInfo(); + try { + metricsInfo = mapper.readValue(metricsJson, MetricsInfo.class); + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error("Jackson readValue to metricsInfo throw exception", e); + } + + return metricsInfo; + } + + public static String sendGetRequest(String url) { + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpGet httpGet = new HttpGet(url); + + try { + CloseableHttpResponse res = httpClient.execute(httpGet); + try { + if (res.getStatusLine().getStatusCode() == MetricsUtil.SC_OK) { + return EntityUtils.toString(res.getEntity()); + } + } finally { + res.close(); + } + } catch (ParseException e) { + LOGGER.error("HttpClient throw ParseException:" + url, e); + } catch (IOException e) { + LOGGER.error("HttpClient throw IOException:" + url, e); + } + finally{ + try { + httpClient.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + LOGGER.error("HttpClient Close throw IOException", e); + } + } + + return null; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/MicroServiceWrapper.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/MicroServiceWrapper.java new file mode 100644 index 0000000..4e5e6cb --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/MicroServiceWrapper.java @@ -0,0 +1,536 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper; + +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.openo.msb.api.MicroServiceFullInfo; +import org.openo.msb.api.MicroServiceInfo; +import org.openo.msb.api.Node; +import org.openo.msb.api.NodeInfo; +import org.openo.msb.api.exception.ExtendedInternalServerErrorException; +import org.openo.msb.api.exception.ExtendedNotFoundException; +import org.openo.msb.api.exception.ExtendedNotSupportedException; +import org.openo.msb.wrapper.util.MicroServiceDB; +import org.openo.msb.wrapper.util.RegExpTestUtil; +import org.openo.msb.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MicroServiceWrapper { + + private static final Logger LOGGER = LoggerFactory.getLogger(MicroServiceWrapper.class); + + private static MicroServiceWrapper instance = new MicroServiceWrapper(); + + + private MicroServiceWrapper() {} + + public static MicroServiceWrapper getInstance() { + return instance; + } + + + /** + * @Title: getAllMicroServiceInstances + * @Description: getAllMicroServiceInstances + * @param: @return + * @return: Response + * @throws Exception + */ + public MicroServiceFullInfo[] getAllMicroServiceInstances(){ + + try { + return MicroServiceDB.getInstance().getAllMicroServiceInstances(); + + } catch (Exception e) { + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + } + + /** + * @Title: getMicroServiceInstance + * @Description: (getMicroServiceInstance) + * @param: @param serviceName + * @param: @param version + * @param: @return + * @return: ApiRouteInfo + */ + public MicroServiceFullInfo getMicroServiceInstance(String serviceName, String version,String serverPort) { + if("null".equals(version)) { + version=""; + } + serviceName=serviceName.replace("*", "/"); + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException("serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new ExtendedNotSupportedException("version (" + version + + ") is not a valid format"); + } + } + + MicroServiceFullInfo microServiceInfo; + try { + microServiceInfo = + MicroServiceDB.getInstance().getMicroServiceInstance(serviceName, version,serverPort); + + } catch (Exception e) { + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + if (null == microServiceInfo) { + String errInfo = + "microservice not found: serviceName-" + serviceName + ",version-" + version; + LOGGER.warn(errInfo); + throw new ExtendedNotFoundException(errInfo); + + } + + return microServiceInfo; + } + + + + /** + * @Title: updateMicroServiceInstance + * @Description: updateMicroServiceInstance + * @param: serviceName + * @param: version + * @param: microServiceInfo + * @return: RouteResult + */ + public synchronized MicroServiceFullInfo updateMicroServiceInstance(String serviceName, + String version, MicroServiceInfo microServiceInfo) { + if("null".equals(version)) { + version=""; + } + serviceName=serviceName.replace("*", "/"); + + try { + + + MicroServiceFullInfo oldService= getMicroServiceInstance(serviceName,version,""); + + // Delete the original record + MicroServiceDB.getInstance().deleteMicroService(serviceName, version,""); + // Notify the listeners + MicroServiceDB.getInstance().noticeApiListener(oldService, "DELETE",""); + // Save the new record + MicroServiceDB.getInstance().saveMicroServiceInfo2Redis(microServiceInfo,""); + + MicroServiceDB.getInstance().noticeApiListener(microServiceInfo, "ADD",""); + MicroServiceFullInfo newMicroServiceInfo = + MicroServiceDB.getInstance().getMicroServiceInstance( + microServiceInfo.getServiceName(), microServiceInfo.getVersion(),""); + return newMicroServiceInfo; + } catch (Exception e) { + LOGGER.error("update MicroService throw exception", e); + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + + } + + public synchronized MicroServiceFullInfo updateMicroServiceNode(String serviceName, + String version, String ip,String port, int ttl) { + if("null".equals(version)) { + version=""; + } + serviceName=serviceName.replace("*", "/"); + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException( + "update MicroService Node FAIL:serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new ExtendedNotSupportedException( + "update MicroService Node FAIL:version is not a valid format"); + } + } + + if (!RegExpTestUtil.ipRegExpTest(ip)) { + throw new ExtendedNotSupportedException("update MicroService Node FAIL:ip(" + ip + + ")is not a valid IP address"); + } + + if (!RegExpTestUtil.portRegExpTest(port)) { + throw new ExtendedNotSupportedException("update MicroService Node FAIL:port(" + port + + ")is not a valid Port address"); + } + + try { + + MicroServiceDB.getInstance().updateMicroServiceNode2Redis(serviceName, version, ip,port,ttl); + + MicroServiceFullInfo newMicroServiceInfo = + MicroServiceDB.getInstance().getMicroServiceInstance(serviceName, version,""); + + return newMicroServiceInfo; + } catch (NullPointerException e) { + throw new ExtendedNotFoundException(e.getMessage()); + } catch (Exception e) { + LOGGER.error("update MicroServiceNode throw exception", e); + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + } + + /** + * @Title updateMicroServiceStatus + * @Description updateMicroServiceStatus + * @param serviceName + * @param version + * @param status + * @return + * @return RouteResult + */ + + public synchronized MicroServiceFullInfo updateMicroServiceStatus(String serviceName, String version, + String status) { + + if ("null".equals(version)) { + version = ""; + } + serviceName=serviceName.replace("*", "/"); + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException( + "update MicroService status FAIL:serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new ExtendedNotSupportedException( + "update MicroService status FAIL:version is not a valid format"); + } + } + + if(!"0".equals(status) && !"2".equals(status) && !"1".equals(status)){ + + throw new ExtendedNotSupportedException("update MicroService status FAIL:status is wrong"); + } + + + try { + + MicroServiceDB.getInstance().updateMicroServiceStatus(serviceName, version, status); + + MicroServiceFullInfo newMicroServiceInfo = + MicroServiceDB.getInstance().getMicroServiceInstance(serviceName, version,""); + + // Notify the listeners + MicroServiceDB.getInstance().noticeUpdateStatusListener(newMicroServiceInfo, status); + + + return newMicroServiceInfo; + } catch (NullPointerException e) { + throw new ExtendedNotFoundException(e.getMessage()); + } catch (Exception e) { + LOGGER.error("update MicroServiceNode throw exception", e); + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + + } + + + public synchronized MicroServiceFullInfo saveMicroServiceInstance( + MicroServiceInfo microServiceInfo, boolean createOrUpdate,String requestIP,String serverPort) { + // 保存数据格式判断 + + if (StringUtils.isBlank(microServiceInfo.getServiceName()) + || StringUtils.isBlank(microServiceInfo.getProtocol()) + || microServiceInfo.getNodes().size() == 0) { + throw new ExtendedNotSupportedException( + "register MicroServiceInfo FAIL: Some required fields are empty"); + } + + for (Node node : microServiceInfo.getNodes()) { + + if(node.getIp()==null || node.getIp().isEmpty()){ + node.setIp(requestIP); + } + else if (!RegExpTestUtil.ipRegExpTest(node.getIp())) { + throw new ExtendedNotSupportedException("register MicroServiceInfo FAIL:IP(" + + node.getIp() + ")is not a valid ip address"); + } + + if (!RegExpTestUtil.portRegExpTest(node.getPort())) { + throw new ExtendedNotSupportedException("register MicroServiceInfo FAIL:Port(" + + node.getPort() + ")is not a valid Port address"); + } + } + + if (StringUtils.isNotBlank(microServiceInfo.getVersion())) { + if (!RegExpTestUtil.versionRegExpTest(microServiceInfo.getVersion())) { + throw new ExtendedNotSupportedException( + "register MicroServiceInfo FAIL:version is not a valid format"); + + } + } + + if (StringUtils.isNotBlank(microServiceInfo.getUrl().trim())) { + if (!RegExpTestUtil.urlRegExpTest(microServiceInfo.getUrl())) { + throw new ExtendedNotSupportedException( + "register MicroServiceInfo FAIL:url is not a valid format(url must be begin with /)"); + + } + } + + + if (RouteUtil.PROTOCOL_LIST.indexOf(microServiceInfo.getProtocol().trim()) == -1) { + throw new ExtendedNotSupportedException( + "register MicroServiceInfo FAIL:Protocol is wrong,value range:(" + + RouteUtil.PROTOCOL_LIST + ")"); + } + + MicroServiceFullInfo existingMicroServiceInfo; + try { + //To determine whether a service already exists + existingMicroServiceInfo = + MicroServiceDB.getInstance().getMicroServiceInstance( + microServiceInfo.getServiceName().trim(), microServiceInfo.getVersion().trim(),serverPort); + + MicroServiceFullInfo newMicroServiceInfo ; + if (existingMicroServiceInfo != null) { + //a service already exists + + if (!existingMicroServiceInfo.getProtocol().equals(microServiceInfo.getProtocol())) { + throw new ExtendedNotSupportedException( + "MicroServiceInfo with different protocols and same serviceName is already existing"); + } + + if (createOrUpdate == false) { + //After the first remove added + MicroServiceDB.getInstance().deleteMicroService( + microServiceInfo.getServiceName(), microServiceInfo.getVersion(),serverPort); + + MicroServiceDB.getInstance().saveMicroServiceInfo2Redis(microServiceInfo,serverPort); + + } else { + //Add the original record and save directly + MicroServiceDB.getInstance().saveMicroServiceInfo2Redis(microServiceInfo,serverPort); + } + + newMicroServiceInfo = + MicroServiceDB.getInstance().getMicroServiceInstance( + microServiceInfo.getServiceName(), microServiceInfo.getVersion(),serverPort); + + //Notify the listeners + MicroServiceDB.getInstance().noticeUpdateApiListener(microServiceInfo.getServiceName(),microServiceInfo.getVersion(),newMicroServiceInfo,serverPort); + + } else { + //Save the new record + MicroServiceDB.getInstance().saveMicroServiceInfo2Redis(microServiceInfo,serverPort); + //Notify the listeners + MicroServiceDB.getInstance().noticeApiListener(microServiceInfo, "ADD",serverPort); + newMicroServiceInfo = + MicroServiceDB.getInstance().getMicroServiceInstance( + microServiceInfo.getServiceName(), microServiceInfo.getVersion(),serverPort); + } + + + + return newMicroServiceInfo; + + } catch (ExtendedNotSupportedException e) { + throw e; + } catch (Exception e) { + throw new ExtendedInternalServerErrorException(e.getMessage()); + } + + } + + + public synchronized void deleteMicroService(String serviceName, String version) { + if("null".equals(version)) { + version=""; + } + serviceName=serviceName.replace("*", "/"); + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException( + "delete MicroServiceInfo FAIL:serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new ExtendedNotSupportedException( + "delete MicroServiceInfo FAIL:version is not a valid format"); + + } + } + + try { + + MicroServiceFullInfo microServiceInfo = + MicroServiceDB.getInstance().getMicroServiceInstance(serviceName, version,""); + + if (microServiceInfo == null) { + LOGGER.warn("serviceName-"+ serviceName + ",version-" + version + " not fond "); + return; + } + + MicroServiceDB.getInstance().deleteMicroService(serviceName, version,""); + //Notify the listeners + MicroServiceDB.getInstance().noticeApiListener(microServiceInfo, "DELETE",""); + + } catch (Exception e) { + LOGGER.error("delete MicroServiceInfo throw exception", e); + throw new ExtendedInternalServerErrorException(e.getMessage()); + + } + + LOGGER.info("delete MicroServiceInfo success:serviceName-" + + serviceName + ",version-" + version ); + + } + + + public synchronized void deleteMicroService(String serviceName, String version,String serverPort) { + if("null".equals(version)) { + version=""; + } + serviceName=serviceName.replace("*", "/"); + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException( + "delete MicroServiceInfo FAIL:serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new ExtendedNotSupportedException( + "delete MicroServiceInfo FAIL:version is not a valid format"); + + } + } + + try { + + MicroServiceFullInfo microServiceInfo = + MicroServiceDB.getInstance().getMicroServiceInstance(serviceName, version,serverPort); + + if (microServiceInfo == null) { + throw new ExtendedNotFoundException("delete MicroServiceInfo FAIL:serviceName-" + + serviceName + ",version-" + version + " not fond "); + } + + MicroServiceDB.getInstance().deleteMicroService(serviceName, version,serverPort); + //Notify the listeners + MicroServiceDB.getInstance().noticeApiListener(microServiceInfo, "DELETE",serverPort); + } catch (ExtendedNotFoundException e) { + throw e; + } catch (Exception e) { + LOGGER.error("delete MicroServiceInfo throw exception", e); + throw new ExtendedInternalServerErrorException(e.getMessage()); + + } + + LOGGER.info("delete MicroServiceInfo success:serviceName-" + + serviceName + ",version-" + version ); + + } + + public synchronized void deleteMicroServiceInstance(String serviceName, String version, + String ip,String port) { + if("null".equals(version)) { + version=""; + } + serviceName=serviceName.replace("*", "/"); + + if (StringUtils.isBlank(serviceName)) { + throw new ExtendedNotSupportedException( + "delete MicroServiceInfo FAIL:serviceName can't be empty"); + } + + if (StringUtils.isNotBlank(version)) { + if (!RegExpTestUtil.versionRegExpTest(version)) { + throw new ExtendedNotSupportedException( + "delete MicroServiceInfo FAIL:version is not a valid format"); + } + } + + if (!RegExpTestUtil.ipRegExpTest(ip)) { + throw new ExtendedNotSupportedException("delete MicroServiceInfo FAIL:IP(" + ip + + ")is not a valid IP address"); + } + + if (!RegExpTestUtil.portRegExpTest(port)) { + throw new ExtendedNotSupportedException("delete MicroServiceInfo FAIL:Port(" + port + + ")is not a valid Port address"); + } + + + try { + MicroServiceFullInfo microServiceInfo = + MicroServiceDB.getInstance().getMicroServiceInstance(serviceName, version,""); + + if (microServiceInfo == null) { + throw new ExtendedNotFoundException("delete MicroServiceInfo FAIL:serviceName-" + + serviceName + ",version-" + version + " not fond "); + } + + Set nodes = microServiceInfo.getNodes(); + + boolean ifFindBNode = false; + + for (Node node : nodes) { + if (node.getIp().equals(ip) && node.getPort().equals(port)) { + ifFindBNode = true; + nodes.remove(node); + + if (nodes.isEmpty()) { + //delete MicroService + MicroServiceDB.getInstance().deleteMicroService(serviceName, version,""); + //Notify the listeners + MicroServiceDB.getInstance().noticeApiListener(microServiceInfo, "DELETE",""); + } else { + //delete Node + MicroServiceDB.getInstance().deleteNode(serviceName, version, ip,port); + MicroServiceDB.getInstance().noticeUpdateApiListener(serviceName, version,microServiceInfo,""); + } + + break; + } + } + + if (!ifFindBNode) { + throw new ExtendedNotFoundException("delete MicroServiceInfo FAIL: node-" + ip+":"+port + + " not fond "); + } + + + } catch (ExtendedNotFoundException e) { + throw e; + } catch (Exception e) { + LOGGER.error("deleteApiRoute throw exception", e); + throw new ExtendedInternalServerErrorException(e.getMessage()); + + } + + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/ServiceAccessWrapper.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/ServiceAccessWrapper.java new file mode 100644 index 0000000..35b02e7 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/ServiceAccessWrapper.java @@ -0,0 +1,172 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.openo.msb.api.ServiceAccessInfo; +import org.openo.msb.wrapper.util.JedisUtil; +import org.openo.msb.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.Jedis; + +public class ServiceAccessWrapper { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAccessWrapper.class); + + private static ServiceAccessWrapper instance = new ServiceAccessWrapper(); + + private ServiceAccessWrapper() {} + + public static ServiceAccessWrapper getInstance() { + return instance; + } + + public ServiceAccessInfo getApiServiceAccessAddr(String serviceName, String version, String host) { + + ServiceAccessInfo apiRouteAccessInfo = null; + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new Exception("fetch from jedis pool failed,null object!"); + } + + if ("null".equals(version)) { + version = ""; + } + + String routekey = + RouteUtil.getPrefixedKey("",RouteUtil.APIROUTE, serviceName, version, + RouteUtil.ROUTE_PATH_INFO); + Boolean isExist = jedis.exists(routekey); + if (isExist) { + apiRouteAccessInfo = new ServiceAccessInfo(); + apiRouteAccessInfo.setServiceName(serviceName); + apiRouteAccessInfo.setVersion(version); + String accessAddr = "http://" + host + "/api/" + serviceName + "/" + version; + apiRouteAccessInfo.setAccessAddr(accessAddr); + } + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + return apiRouteAccessInfo; + + } + + public List getApiRouteAccessAddr(String serviceType, String serviceName, + String version, String host) { + List serviceList = new ArrayList(); + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new Exception("fetch from jedis pool failed,null object!"); + } + + String keyPattern = this.getRedisSearchPattern(serviceType, serviceName, version); + Set infoKeys = jedis.keys(keyPattern); + Pattern pattern = this.getKeyPattern(); + for (Iterator iterator = infoKeys.iterator(); iterator.hasNext();) { + String infoKey = (String) iterator.next(); + Matcher matcher = pattern.matcher(infoKey); + if (matcher.matches()) { + ServiceAccessInfo serviceAccessInfo = new ServiceAccessInfo(); + serviceAccessInfo.setServiceType(matcher.group("servicetype")); + serviceAccessInfo.setServiceName(matcher.group("servicename")); + serviceAccessInfo.setVersion(matcher.group("version")); + this.buildServiceAccessAddr(serviceAccessInfo, host, infoKey, jedis); + serviceList.add(serviceAccessInfo); + } + } + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + return serviceList; + + } + + private void buildServiceAccessAddr(ServiceAccessInfo serviceAccessInfo, String host, + String infoKey, Jedis jedis) { + String serviceType = serviceAccessInfo.getServiceType(); + StringBuffer accessAddr = new StringBuffer(); + switch (serviceType) { + case RouteUtil.APIROUTE: + accessAddr.append("http://").append(host).append(":").append(JedisUtil.serverPort) + .append("/").append(serviceAccessInfo.getServiceType()).append("/") + .append(serviceAccessInfo.getServiceName()).append("/") + .append(serviceAccessInfo.getVersion()); + serviceAccessInfo.setAccessAddr(accessAddr.toString()); + break; + case RouteUtil.IUIROUTE: + accessAddr.append("http://").append(host).append(":").append(JedisUtil.serverPort) + .append("/").append(serviceAccessInfo.getServiceType()).append("/") + .append(serviceAccessInfo.getServiceName()); + serviceAccessInfo.setAccessAddr(accessAddr.toString()); + break; + case RouteUtil.CUSTOMROUTE: + accessAddr.append("http://").append(host).append(":").append(JedisUtil.serverPort) + .append(serviceAccessInfo.getServiceName()); + serviceAccessInfo.setAccessAddr(accessAddr.toString()); + break; + case RouteUtil.P2PROUTE: + accessAddr.append(jedis.hget(infoKey, "url")); + serviceAccessInfo.setAccessAddr(accessAddr.toString()); + break; + default: + serviceAccessInfo.setAccessAddr("not supported now"); + break; + } + } + + private String getRedisSearchPattern(String serviceType, String serviceName, String version) { + StringBuffer sb = new StringBuffer(); + sb.append(RouteUtil.ROUTE_PATH); + if (null != serviceType && !"".equals(serviceType)) { + sb.append(":").append(serviceType); + } else { + sb.append(":").append("*"); + } + sb.append(":").append(serviceName); + if (null != version && !"".equals(version)) { + sb.append(":"); + sb.append(version); + sb.append(":"); + } else { + sb.append(":*"); + } + sb.append(RouteUtil.ROUTE_PATH_INFO); + return sb.toString(); + } + + private Pattern getKeyPattern() { + String pStr = + "conductor:routing:(?api|iui|custom|p2p):(?[^:]+)(:(?[^:]*))?:info"; + return Pattern.compile(pStr); + } +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/CatalogClient.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/CatalogClient.java new file mode 100644 index 0000000..576bf9b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/CatalogClient.java @@ -0,0 +1,285 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul; + +import static org.openo.msb.wrapper.consul.util.ClientUtil.response; + +import java.util.List; +import java.util.Map; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; + +import org.openo.msb.wrapper.consul.async.ConsulResponseCallback; +import org.openo.msb.wrapper.consul.model.ConsulResponse; +import org.openo.msb.wrapper.consul.model.catalog.CatalogNode; +import org.openo.msb.wrapper.consul.model.catalog.CatalogService; +import org.openo.msb.wrapper.consul.model.health.Node; +import org.openo.msb.wrapper.consul.option.CatalogOptions; +import org.openo.msb.wrapper.consul.option.QueryOptions; + +/** + * HTTP Client for /v1/catalog/ endpoints or api/catalog/v1 by openresty + */ +public class CatalogClient { + + private static final GenericType> TYPE_STRING_LIST = new GenericType>() {}; + private static final GenericType> TYPE_NODE_LIST = new GenericType>() {}; + private static final GenericType>> TYPE_SERVICES_MAP = new GenericType>>() {}; + private static final GenericType> TYPE_CATALOG_SERVICE_LIST = new GenericType>() {}; + private static final GenericType TYPE_CATALOG_NODE = new GenericType() {}; + + private final WebTarget webTarget; + + /** + * Constructs an instance of this class. + * + * @param webTarget The {@link javax.ws.rs.client.WebTarget} to base requests from. + */ + CatalogClient(WebTarget webTarget) { + this.webTarget = webTarget; + } + + /** + * Retrieves all datacenters. + * + * GET /v1/catalog/datacenters + * + * @return A list of datacenter names. + */ + public List getDatacenters() { + return webTarget.path("datacenters").request() + .accept(MediaType.APPLICATION_JSON_TYPE).get(TYPE_STRING_LIST); + } + + /** + * Retrieves all nodes. + * + * GET /v1/catalog/nodes + * + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link org.openo.msb.wrapper.consul.model.health.Node} objects. + */ + public ConsulResponse> getNodes() { + return getNodes(null, QueryOptions.BLANK); + } + + /** + * Retrieves all nodes for a given datacenter. + * + * GET /v1/catalog/nodes?dc={datacenter} + * + * @param catalogOptions Catalog specific options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link org.openo.msb.wrapper.consul.model.health.Node} objects. + */ + public ConsulResponse> getNodes(CatalogOptions catalogOptions) { + return getNodes(catalogOptions, QueryOptions.BLANK); + } + + /** + * Retrieves all nodes with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/catalog/nodes + * + * @param queryOptions The Query Options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link org.openo.msb.wrapper.consul.model.health.Node} objects. + */ + public ConsulResponse> getNodes(QueryOptions queryOptions) { + return getNodes(null, queryOptions); + } + + /** + * Retrieves all nodes for a given datacenter with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/catalog/nodes?dc={datacenter} + * + * @param catalogOptions Catalog specific options to use. + * @param queryOptions The Query Options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link org.openo.msb.wrapper.consul.model.health.Node} objects. + */ + public ConsulResponse> getNodes(CatalogOptions catalogOptions, QueryOptions queryOptions) { + return response(webTarget.path("nodes"), catalogOptions, queryOptions, TYPE_NODE_LIST); + } + + /** + * Retrieves all services for a given datacenter. + * + * GET /v1/catalog/services?dc={datacenter} + * + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a map of service name to list of tags. + */ + public ConsulResponse>> getServices() { + return getServices(null, QueryOptions.BLANK); + } + + /** + * Retrieves all services for a given datacenter. + * + * GET /v1/catalog/services?dc={datacenter} + * + * @param catalogOptions Catalog specific options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a map of service name to list of tags. + */ + public ConsulResponse>> getServices(CatalogOptions catalogOptions) { + return getServices(catalogOptions, QueryOptions.BLANK); + } + + /** + * Retrieves all services for a given datacenter with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/catalog/services?dc={datacenter} + * + * @param queryOptions The Query Options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a map of service name to list of tags. + */ + public ConsulResponse>> getServices(QueryOptions queryOptions) { + return getServices(null, queryOptions); + } + + /** + * Retrieves all services for a given datacenter with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/catalog/services?dc={datacenter} + * + * @param catalogOptions Catalog specific options to use. + * @param queryOptions The Query Options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a map of service name to list of tags. + */ + public ConsulResponse>> getServices(CatalogOptions catalogOptions, QueryOptions queryOptions) { + return response(webTarget.path("services"), catalogOptions, queryOptions, TYPE_SERVICES_MAP); + } + + public void getService(QueryOptions queryOptions, ConsulResponseCallback>> callback) { + response(webTarget.path("services"), CatalogOptions.BLANK, + queryOptions, TYPE_SERVICES_MAP, callback); + } + + /** + * Retrieves a single service. + * + * GET /v1/catalog/service/{service} + * + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing + * {@link org.openo.msb.wrapper.consul.model.catalog.CatalogService} objects. + */ + public ConsulResponse> getService(String service) { + return getService(service, null, QueryOptions.BLANK); + } + + /** + * Retrieves a single service for a given datacenter. + * + * GET /v1/catalog/service/{service}?dc={datacenter} + * + * @param catalogOptions Catalog specific options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing + * {@link org.openo.msb.wrapper.consul.model.catalog.CatalogService} objects. + */ + public ConsulResponse> getService(String service, CatalogOptions catalogOptions) { + return getService(service, catalogOptions, QueryOptions.BLANK); + } + + /** + * Retrieves a single service with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/catalog/service/{service} + * + * @param queryOptions The Query Options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing + * {@link org.openo.msb.wrapper.consul.model.catalog.CatalogService} objects. + */ + public ConsulResponse> getService(String service, QueryOptions queryOptions) { + return getService(service, null, queryOptions); + } + + public void getService(String service, QueryOptions queryOptions, ConsulResponseCallback> callback) { + + response(webTarget.path("service").path(service), CatalogOptions.BLANK, + queryOptions, TYPE_CATALOG_SERVICE_LIST, callback); + } + + + + /** + * Retrieves a single service for a given datacenter with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/catalog/service/{service}?dc={datacenter} + * + * @param catalogOptions Catalog specific options to use. + * @param queryOptions The Query Options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing + * {@link org.openo.msb.wrapper.consul.model.catalog.CatalogService} objects. + */ + public ConsulResponse> getService(String service, CatalogOptions catalogOptions, + QueryOptions queryOptions) { + return response(webTarget.path("service").path(service), catalogOptions, queryOptions, + TYPE_CATALOG_SERVICE_LIST); + } + + /** + * Retrieves a single node. + * + * GET /v1/catalog/node/{node} + * + * @return A list of matching {@link org.openo.msb.wrapper.consul.model.catalog.CatalogService} objects. + */ + public ConsulResponse getNode(String node) { + return getNode(node, null, QueryOptions.BLANK); + } + + /** + * Retrieves a single node for a given datacenter. + * + * GET /v1/catalog/node/{node}?dc={datacenter} + * + * @param catalogOptions Catalog specific options to use. + * @return A list of matching {@link org.openo.msb.wrapper.consul.model.catalog.CatalogService} objects. + */ + public ConsulResponse getNode(String node, CatalogOptions catalogOptions) { + return getNode(node, catalogOptions, QueryOptions.BLANK); + } + + /** + * Retrieves a single node with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/catalog/node/{node} + * + * @param queryOptions The Query Options to use. + * @return A list of matching {@link org.openo.msb.wrapper.consul.model.catalog.CatalogService} objects. + */ + public ConsulResponse getNode(String node, QueryOptions queryOptions) { + return getNode(node, null, queryOptions); + } + + /** + * Retrieves a single node for a given datacenter with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/catalog/node/{node}?dc={datacenter} + * + * @param catalogOptions Catalog specific options to use. + * @param queryOptions The Query Options to use. + * @return A list of matching {@link org.openo.msb.wrapper.consul.model.catalog.CatalogService} objects. + */ + public ConsulResponse getNode(String node, CatalogOptions catalogOptions, QueryOptions queryOptions) { + return response(webTarget.path("node").path(node), catalogOptions, queryOptions, + TYPE_CATALOG_NODE); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/Consul.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/Consul.java new file mode 100644 index 0000000..2a55b7f --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/Consul.java @@ -0,0 +1,299 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul; + +import java.net.MalformedURLException; +import java.net.URL; + +import javax.net.ssl.SSLContext; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; + +import org.openo.msb.wrapper.consul.util.Jackson; +import org.openo.msb.wrapper.consul.util.ObjectMapperContextResolver; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.guava.GuavaModule; +import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; +import com.google.common.net.HostAndPort; + +/** + * Client for interacting with the Consul HTTP API. + * + * @author rfast + */ +public class Consul { + + /** + * Default Consul HTTP API host. + */ + public static final String DEFAULT_HTTP_HOST = "localhost"; + + /** + * Default Consul HTTP API port. + */ + public static final int DEFAULT_HTTP_PORT = 8500; + + + + + private final CatalogClient catalogClient; + + private final HealthClient healthClient; + + + /** + * Private constructor. + * + * @param url The full URL of a running Consul instance. + * @param builder JAX-RS client builder instance. + */ + private Consul(String url, ClientBuilder builder, ObjectMapper mapper) { + + if (!FluentIterable.from(builder.getConfiguration().getClasses()) + .filter(new Predicate>() { + @Override + public boolean apply(final Class clazz) { + return JacksonJaxbJsonProvider.class.isAssignableFrom(clazz); + } + }).first().isPresent()) { + builder.register(JacksonJaxbJsonProvider.class); + } + final Client client = builder + .register(new ObjectMapperContextResolver(mapper)) + .build(); + + + if(url.endsWith("8500")){ + this.catalogClient = new CatalogClient(client.target(url).path("v1").path("catalog")); + this.healthClient = new HealthClient(client.target(url).path("v1").path("health")); + } + else{ + this.catalogClient = new CatalogClient(client.target(url).path("api").path("catalog").path("v1")); + this.healthClient = new HealthClient(client.target(url).path("api").path("health").path("v1")); + } + + +// agentClient.ping(); + } + + /** + * Creates a new client given a complete URL. + * + * @deprecated Use {@link Consul.Builder} + * + * @param url The Consul API URL. + * @param builder The JAX-RS client builder instance. + * @return A new client. + */ + @Deprecated + public static Consul newClient(String url, ClientBuilder builder, ObjectMapper mapper) { + return new Consul(url, builder, mapper); + } + + /** + * Creates a new client given a host and a port. + * + * @deprecated Use {@link Consul.Builder} + * + * @param host The Consul API hostname or IP. + * @param port The Consul port. + * @param builder The JAX-RS client builder instance. + * @return A new client. + */ + @Deprecated + public static Consul newClient(String host, int port, ClientBuilder builder, ObjectMapper mapper) { + try { + return new Consul(new URL("http", host, port, "").toString(), builder, mapper); + } catch (MalformedURLException e) { + throw new ConsulException("Bad Consul URL", e); + } + } + + /** + * Creates a new client given a host and a port. + * + * @deprecated Use {@link Consul.Builder} + * + * @param host The Consul API hostname or IP. + * @param port The Consul port. + * @return A new client. + */ + @Deprecated + public static Consul newClient(String host, int port) { + return newClient(host, port, ClientBuilder.newBuilder(), Jackson.MAPPER); + } + + /** + * Creates a new client given a host and a port. + * + * @deprecated Use {@link Consul.Builder} + * + * @return A new client. + */ + @Deprecated + public static Consul newClient() { + return newClient(DEFAULT_HTTP_HOST, DEFAULT_HTTP_PORT); + } + + + /** + * Get the Catalog HTTP client. + *

+ * /v1/catalog + * + * @return The Catalog HTTP client. + */ + public CatalogClient catalogClient() { + return catalogClient; + } + + public HealthClient healthClient() { + return healthClient; + } + /** + * Creates a new {@link Builder} object. + * + * @return A new Consul builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link Consul} client objects. + */ + public static class Builder { + private URL url; + private Optional sslContext = Optional.absent(); + private ObjectMapper objectMapper = Jackson.MAPPER; + private ClientBuilder clientBuilder = ClientBuilder.newBuilder(); + + { + try { + url = new URL("http", "localhost", 8500, ""); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + /** + * Constructs a new builder. + */ + Builder() { + + } + + /** + * Sets the URL from a {@link URL} object. + * + * @param url The Consul agent URL. + * @return The builder. + */ + public Builder withUrl(URL url) { + this.url = url; + + return this; + } + + /** + * Sets the URL from a {@link HostAndPort} object. + * + * @param hostAndPort The Consul agent host and port. + * @return The builder. + */ + public Builder withHostAndPort(HostAndPort hostAndPort) { + try { + this.url = new URL("http", hostAndPort.getHostText(), hostAndPort.getPort(), ""); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + return this; + } + + /** + * Sets the URL from a string. + * + * @param url The Consul agent URL. + * @return The builder. + */ + public Builder withUrl(String url) { + try { + this.url = new URL(url); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + return this; + } + + /** + * Sets the {@link SSLContext} for the client. + * + * @param sslContext The SSL context for HTTPS agents. + * @return The builder. + */ + public Builder withSslContext(SSLContext sslContext) { + this.sslContext = Optional.of(sslContext); + + return this; + } + + /** + * Sets the {@link ObjectMapper} for the client. + * + * @param objectMapper The {@link ObjectMapper} to use. + * @return The builder. + */ + public Builder withObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + + objectMapper.registerModule(new GuavaModule()); + + return this; + } + + /** + * Sets the JAX-RS {@link ClientBuilder} to use. + * + * @param clientBuilder The JAX-RS builder. + * @return This builder. + */ + public Builder withClientBuilder(ClientBuilder clientBuilder) { + this.clientBuilder = clientBuilder; + + return this; + } + + /** + * Constructs a new {@link Consul} client. + * + * @return A new Consul client. + */ + public Consul build() { + if (this.sslContext.isPresent()) { + this.clientBuilder.sslContext(this.sslContext.get()); + } + + return new Consul(this.url.toExternalForm(), this.clientBuilder, this.objectMapper); + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/ConsulException.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/ConsulException.java new file mode 100644 index 0000000..ce08ac9 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/ConsulException.java @@ -0,0 +1,42 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul; + +/** + * Wraps an exception thrown whilst interacting with the Consul API. + */ +public class ConsulException extends RuntimeException { + + /** + * Constructs an instance of this class. + * + * @param message The exception message. + */ + public ConsulException(String message) { + super(message); + } + + /** + * Constructs an instance of this class. + * + * @param message The exception message. + * @param throwable The wrapped {@link java.lang.Throwable} object. + */ + public ConsulException(String message, Throwable throwable) { + super(message, throwable); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/HealthClient.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/HealthClient.java new file mode 100644 index 0000000..1d4c00a --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/HealthClient.java @@ -0,0 +1,247 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul; + +import static org.openo.msb.wrapper.consul.util.ClientUtil.response; + +import java.util.List; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; + +import org.openo.msb.wrapper.consul.async.ConsulResponseCallback; +import org.openo.msb.wrapper.consul.model.ConsulResponse; +import org.openo.msb.wrapper.consul.model.health.ServiceHealth; +import org.openo.msb.wrapper.consul.option.CatalogOptions; +import org.openo.msb.wrapper.consul.option.QueryOptions; + +/** + * HTTP Client for /v1/health/ endpoints. + */ +public class HealthClient { + + + private static final GenericType> TYPE_SERVICE_HEALTH_LIST = + new GenericType>() {}; + + private final WebTarget webTarget; + + /** + * Constructs an instance of this class. + * + * @param webTarget The {@link javax.ws.rs.client.WebTarget} to base requests from. + */ + HealthClient(WebTarget webTarget) { + this.webTarget = webTarget; + } + + + + /** + * Retrieves the healthchecks for all healthy service instances. + * + * GET /v1/health/service/{service}?passing + * + * @param service The service to query. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link com.orbitz.consul.model.health.HealthCheck} objects. + */ + public ConsulResponse> getHealthyServiceInstances(String service) { + return getHealthyServiceInstances(service, null, QueryOptions.BLANK); + } + + /** + * Retrieves the healthchecks for all healthy service instances in a given datacenter. + * + * GET /v1/health/service/{service}?dc={datacenter}&passing + * + * @param service The service to query. + * @param catalogOptions The catalog specific options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link com.orbitz.consul.model.health.HealthCheck} objects. + */ + public ConsulResponse> getHealthyServiceInstances(String service, CatalogOptions catalogOptions) { + return getHealthyServiceInstances(service, catalogOptions, QueryOptions.BLANK); + } + + /** + * Retrieves the healthchecks for all healthy service instances with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/health/service/{service}?passing + * + * @param service The service to query. + * @param queryOptions The Query Options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link com.orbitz.consul.model.health.HealthCheck} objects. + */ + public ConsulResponse> getHealthyServiceInstances(String service, QueryOptions queryOptions) { + return getHealthyServiceInstances(service, null, queryOptions); + } + + /** + * Retrieves the healthchecks for all healthy service instances in a given datacenter with + * {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/health/service/{service}?dc={datacenter}&passing + * + * @param service The service to query. + * @param catalogOptions The catalog specific options to use. + * @param queryOptions The Query Options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link com.orbitz.consul.model.health.HealthCheck} objects. + */ + public ConsulResponse> getHealthyServiceInstances(String service, CatalogOptions catalogOptions, + QueryOptions queryOptions) { + return response(webTarget.path("service").path(service).queryParam("passing", "true"), + catalogOptions, queryOptions, TYPE_SERVICE_HEALTH_LIST); + } + + /** + * Asynchronously retrieves the healthchecks for all healthy service instances in a given + * datacenter with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/health/service/{service}?dc={datacenter}&passing + * + * Experimental. + * + * @param service The service to query. + * @param catalogOptions The catalog specific options to use. + * @param queryOptions The Query Options to use. + * @param callback Callback implemented by callee to handle results. + */ + public void getHealthyServiceInstances(String service, CatalogOptions catalogOptions, + QueryOptions queryOptions, + ConsulResponseCallback> callback) { + response(webTarget.path("service").path(service).queryParam("passing", "true"), + catalogOptions, queryOptions, TYPE_SERVICE_HEALTH_LIST, callback); + } + + /** + * Asynchronously retrieves the healthchecks for all healthy service instances in a given + * datacenter with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/health/service/{service}?dc={datacenter}&passing + * + * Experimental. + * + * @param service The service to query. + * @param queryOptions The Query Options to use. + * @param callback Callback implemented by callee to handle results. + */ + public void getHealthyServiceInstances(String service, QueryOptions queryOptions, + ConsulResponseCallback> callback) { + response(webTarget.path("service").path(service).queryParam("passing", "true"), + CatalogOptions.BLANK, queryOptions, TYPE_SERVICE_HEALTH_LIST, callback); + } + + /** + * Retrieves the healthchecks for all nodes. + * + * GET /v1/health/service/{service} + * + * @param service The service to query. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link com.orbitz.consul.model.health.HealthCheck} objects. + */ + public ConsulResponse> getAllServiceInstances(String service) { + return getAllServiceInstances(service, null, QueryOptions.BLANK); + } + + /** + * Retrieves the healthchecks for all nodes in a given datacenter. + * + * GET /v1/health/service/{service}?dc={datacenter} + * + * @param service The service to query. + * @param catalogOptions The catalog specific options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link com.orbitz.consul.model.health.HealthCheck} objects. + */ + public ConsulResponse> getAllServiceInstances(String service, CatalogOptions catalogOptions) { + return getAllServiceInstances(service, catalogOptions, QueryOptions.BLANK); + } + + /** + * Retrieves the healthchecks for all nodes with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/health/service/{service} + * + * @param service The service to query. + * @param queryOptions The Query Options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link com.orbitz.consul.model.health.HealthCheck} objects. + */ + public ConsulResponse> getAllServiceInstances(String service, QueryOptions queryOptions) { + return getAllServiceInstances(service, null, queryOptions); + } + + /** + * Retrieves the healthchecks for all nodes in a given datacenter with + * {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/health/service/{service}?dc={datacenter} + * + * @param service The service to query. + * @param catalogOptions The catalog specific options to use. + * @param queryOptions The Query Options to use. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing a list of + * {@link com.orbitz.consul.model.health.HealthCheck} objects. + */ + public ConsulResponse> getAllServiceInstances(String service, CatalogOptions catalogOptions, + QueryOptions queryOptions) { + return response(webTarget.path("service").path(service), catalogOptions, queryOptions, + TYPE_SERVICE_HEALTH_LIST); + } + + /** + * Asynchronously retrieves the healthchecks for all nodes in a given + * datacenter with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/health/service/{service}?dc={datacenter} + * + * Experimental. + * + * @param service The service to query. + * @param catalogOptions The catalog specific options to use. + * @param queryOptions The Query Options to use. + * @param callback Callback implemented by callee to handle results. + */ + public void getAllServiceInstances(String service, CatalogOptions catalogOptions, + QueryOptions queryOptions, + ConsulResponseCallback> callback) { + response(webTarget.path("service").path(service), catalogOptions, queryOptions, + TYPE_SERVICE_HEALTH_LIST, callback); + } + + /** + * Asynchronously retrieves the healthchecks for all nodes in a given + * datacenter with {@link org.openo.msb.wrapper.consul.option.QueryOptions}. + * + * GET /v1/health/service/{service}?dc={datacenter} + * + * Experimental. + * + * @param service The service to query. + * @param queryOptions The Query Options to use. + * @param callback Callback implemented by callee to handle results. + */ + public void getAllServiceInstances(String service, QueryOptions queryOptions, + ConsulResponseCallback> callback) { + response(webTarget.path("service").path(service), CatalogOptions.BLANK, + queryOptions, TYPE_SERVICE_HEALTH_LIST, callback); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/async/ConsulResponseCallback.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/async/ConsulResponseCallback.java new file mode 100644 index 0000000..1db3cc5 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/async/ConsulResponseCallback.java @@ -0,0 +1,42 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.async; + +import org.openo.msb.wrapper.consul.model.ConsulResponse; + +/** + * For API calls that support long-polling, this callback is used to handle + * the result on success or failure for an async HTTP call. + * + * @param The Response type. + */ +public interface ConsulResponseCallback { + + /** + * Callback for a successful {@link org.openo.msb.wrapper.consul.model.ConsulResponse}. + * + * @param consulResponse The Consul response. + */ + void onComplete(ConsulResponse consulResponse); + + /** + * Callback for an unsuccessful request. + * + * @param throwable The exception thrown. + */ + void onFailure(Throwable throwable); +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/CatalogCache.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/CatalogCache.java new file mode 100644 index 0000000..8cafab4 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/CatalogCache.java @@ -0,0 +1,70 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.cache; + +import java.math.BigInteger; +import java.util.List; + +import org.openo.msb.wrapper.consul.CatalogClient; +import org.openo.msb.wrapper.consul.async.ConsulResponseCallback; +import org.openo.msb.wrapper.consul.model.catalog.CatalogService; + +import com.google.common.base.Function; + +public class CatalogCache extends ConsulCache{ + + private final String serviceName; + + private CatalogCache(Function keyConversion, + ConsulCache.CallbackConsumer callbackConsumer,String serviceName) { + super(keyConversion, callbackConsumer); + this.serviceName=serviceName; + // TODO Auto-generated constructor stub + } + + + public static CatalogCache newCache( + final CatalogClient catalogClient, + final String serviceName, + final int watchSeconds){ + Function keyExtractor = new Function() { + @Override + public String apply(CatalogService input) { + //return input.getKey().substring(rootPath.length() + 1); + return input.getServiceId(); + } + }; + + final CallbackConsumer callbackConsumer = new CallbackConsumer() { + @Override + public void consume(BigInteger index, ConsulResponseCallback> callback) { + catalogClient.getService(serviceName, watchParams(index, watchSeconds),callback); + } + }; + + + return new CatalogCache(keyExtractor, callbackConsumer,serviceName); + + + } + + public String getServiceName(){ + return this.serviceName; + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ConsulCache.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ConsulCache.java new file mode 100644 index 0000000..e7494fa --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ConsulCache.java @@ -0,0 +1,230 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.cache; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.collect.ImmutableMap; + +import org.openo.msb.wrapper.consul.async.ConsulResponseCallback; +import org.openo.msb.wrapper.consul.model.ConsulResponse; +import org.openo.msb.wrapper.consul.option.QueryOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigInteger; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static com.google.common.base.Preconditions.checkState; + +/** + * A cache structure that can provide an up-to-date read-only + * map backed by consul data + * + * @param + */ +public class ConsulCache { + + enum State {latent, starting, started, stopped } + + private final static Logger LOGGER = LoggerFactory.getLogger(ConsulCache.class); + + private final AtomicReference latestIndex = new AtomicReference(null); + private final AtomicReference> lastResponse = new AtomicReference>(ImmutableMap.of()); + private final AtomicReference state = new AtomicReference(State.latent); + private final CountDownLatch initLatch = new CountDownLatch(1); + private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + private final CopyOnWriteArrayList> listeners = new CopyOnWriteArrayList>(); + + private final Function keyConversion; + private final CallbackConsumer callBackConsumer; + private final ConsulResponseCallback> responseCallback; + + ConsulCache( + Function keyConversion, + CallbackConsumer callbackConsumer) { + this(keyConversion, callbackConsumer, 10, TimeUnit.SECONDS); + } + + ConsulCache( + Function keyConversion, + CallbackConsumer callbackConsumer, + final long backoffDelayQty, + final TimeUnit backoffDelayUnit) { + + this.keyConversion = keyConversion; + this.callBackConsumer = callbackConsumer; + + this.responseCallback = new ConsulResponseCallback>() { + @Override + public void onComplete(ConsulResponse> consulResponse) { + + if (!isRunning()) { + return; + } + updateIndex(consulResponse); + ImmutableMap full = convertToMap(consulResponse); + + boolean changed = !full.equals(lastResponse.get()); +// LOGGER.info("node changed:"+changed+"----"+full); + if (changed) { + // changes + lastResponse.set(full); + } + + if (changed) { + for (Listener l : listeners) { + l.notify(full); + } + } + + if (state.compareAndSet(State.starting, State.started)) { + initLatch.countDown(); + } + runCallback(); + } + + @Override + public void onFailure(Throwable throwable) { + + if (!isRunning()) { + return; + } + LOGGER.error(String.format("Error getting response from consul. will retry in %d %s", backoffDelayQty, backoffDelayUnit), throwable); + + executorService.schedule(new Runnable() { + @Override + public void run() { + runCallback(); + } + }, backoffDelayQty, backoffDelayUnit); + } + }; + } + + public void start() throws Exception { + checkState(state.compareAndSet(State.latent, State.starting),"Cannot transition from state %s to %s", state.get(), State.starting); + runCallback(); + } + + public void stop() throws Exception { + State previous = state.getAndSet(State.stopped); + if (previous != State.stopped) { + executorService.shutdownNow(); + } + } + + private void runCallback() { + if (isRunning()) { + callBackConsumer.consume(latestIndex.get(), responseCallback); + } + } + + private boolean isRunning() { + return state.get() == State.started || state.get() == State.starting; + } + + public boolean awaitInitialized(long timeout, TimeUnit unit) throws InterruptedException { + return initLatch.await(timeout, unit); + } + + public ImmutableMap getMap() { + return lastResponse.get(); + } + + @VisibleForTesting + ImmutableMap convertToMap(final ConsulResponse> response) { + if (response == null || response.getResponse() == null || response.getResponse().isEmpty()) { + return ImmutableMap.of(); + } + + final ImmutableMap.Builder builder = ImmutableMap.builder(); + final Set keySet = new HashSet<>(); + for (final V v : response.getResponse()) { + final K key = keyConversion.apply(v); + if (key != null) { + if (!keySet.contains(key)) { + builder.put(key, v); + } else { + System.out.println(key.toString()); + LOGGER.warn("Duplicate service encountered. May differ by tags. Try using more specific tags? " + key.toString()); + } + } + keySet.add(key); + } + return builder.build(); + } + + private void updateIndex(ConsulResponse> consulResponse) { + if (consulResponse != null && consulResponse.getIndex() != null) { + this.latestIndex.set(consulResponse.getIndex()); + } + } + + protected static QueryOptions watchParams(BigInteger index, int blockSeconds) { + if (index == null) { + return QueryOptions.BLANK; + } else { + return QueryOptions.blockSeconds(blockSeconds, index).build(); + } + } + + /** + * passed in by creators to vary the content of the cached values + * + * @param + */ + protected interface CallbackConsumer { + void consume(BigInteger index, ConsulResponseCallback> callback); + } + + /** + * Implementers can register a listener to receive + * a new map when it changes + * + * @param + */ + public interface Listener { + void notify(Map newValues); + } + + public boolean addListener(Listener listener) { + boolean added = listeners.add(listener); + if (state.get() == State.started) { + listener.notify(lastResponse.get()); + } + return added; + } + + public boolean removeListener(Listener listener) { + return listeners.remove(listener); + } + + @VisibleForTesting + protected State getState() { + return state.get(); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ConsulCache4Map.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ConsulCache4Map.java new file mode 100644 index 0000000..84fd8ec --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ConsulCache4Map.java @@ -0,0 +1,258 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.cache; + + +import static com.google.common.base.Preconditions.checkState; + +import java.math.BigInteger; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.openo.msb.wrapper.consul.async.ConsulResponseCallback; +import org.openo.msb.wrapper.consul.model.ConsulResponse; +import org.openo.msb.wrapper.consul.model.catalog.CatalogService; +import org.openo.msb.wrapper.consul.model.catalog.ServiceInfo; +import org.openo.msb.wrapper.consul.option.QueryOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; + +/** + * A cache structure that can provide an up-to-date read-only + * map backed by consul data + * + * @param + */ +public class ConsulCache4Map { + + enum State {latent, starting, started, stopped } + + private final static Logger LOGGER = LoggerFactory.getLogger(ConsulCache4Map.class); + + private final AtomicReference latestIndex = new AtomicReference(null); + private final AtomicReference> lastResponse = new AtomicReference>(ImmutableList.of()); + private final AtomicReference state = new AtomicReference(State.latent); + private final CountDownLatch initLatch = new CountDownLatch(1); + private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + private final CopyOnWriteArrayList> listeners = new CopyOnWriteArrayList>(); + + private final CallbackConsumer callBackConsumer; + private final ConsulResponseCallback>> responseCallback; + + ConsulCache4Map(CallbackConsumer callbackConsumer) { + this( callbackConsumer, 10, TimeUnit.SECONDS); + } + + ConsulCache4Map( + CallbackConsumer callbackConsumer, + final long backoffDelayQty, + final TimeUnit backoffDelayUnit) { + + this.callBackConsumer = callbackConsumer; + + this.responseCallback = new ConsulResponseCallback>>() { + @Override + public void onComplete(ConsulResponse>> consulResponse) { + + if (!isRunning()) { + return; + } + updateIndex(consulResponse); + ImmutableList full = convertToList(consulResponse); + List oldList=lastResponse.get(); + boolean changed = !full.equals(lastResponse.get()); +// LOGGER.info("service changed:"+changed+"----"+full); + if (changed) { + // changes + lastResponse.set(full); + } + + if (changed) { + for (Listener l : listeners) { + l.notify(oldList,full); + } + } + + if (state.compareAndSet(State.starting, State.started)) { + initLatch.countDown(); + } + runCallback(); + } + + @Override + public void onFailure(Throwable throwable) { + + if (!isRunning()) { + return; + } + LOGGER.error(String.format("Error getting response from consul. will retry in %d %s", backoffDelayQty, backoffDelayUnit), throwable); + + executorService.schedule(new Runnable() { + @Override + public void run() { + runCallback(); + } + }, backoffDelayQty, backoffDelayUnit); + } + }; + } + + public void start() throws Exception { + checkState(state.compareAndSet(State.latent, State.starting),"Cannot transition from state %s to %s", state.get(), State.starting); + runCallback(); + } + + public void stop() throws Exception { + State previous = state.getAndSet(State.stopped); + if (previous != State.stopped) { + executorService.shutdownNow(); + } + } + + private void runCallback() { + if (isRunning()) { + callBackConsumer.consume(latestIndex.get(), responseCallback); + } + } + + private boolean isRunning() { + return state.get() == State.started || state.get() == State.starting; + } + + public boolean awaitInitialized(long timeout, TimeUnit unit) throws InterruptedException { + return initLatch.await(timeout, unit); + } + + public ImmutableList getMap() { + return lastResponse.get(); + } + + @VisibleForTesting + ImmutableList convertToList(final ConsulResponse>> response) { + if (response == null || response.getResponse() == null || response.getResponse().isEmpty()) { + return ImmutableList.of(); + } + + final ImmutableList.Builder builder = ImmutableList.builder(); + final Set keySet = new HashSet<>(); + + for(Map.Entry> entry : response.getResponse().entrySet()) { + + String key = entry.getKey(); + + if (key != null && !"consul".equals(key)) { + if (!keySet.contains(key)) { + ServiceInfo serviceInfo=new ServiceInfo(); + serviceInfo.setServiceName(key); + + List value=entry.getValue(); + for(String tag:value){ + + if(tag.startsWith("version")){ + String version; + if(tag.split(":").length==2) + { + version = tag.split(":")[1]; + } + else{ + version=""; + } + + serviceInfo.setVersion(version); + break; + } + } + + builder.add(serviceInfo); + } else { + System.out.println(key.toString()); + LOGGER.warn("Duplicate service encountered. May differ by tags. Try using more specific tags? " + key.toString()); + } + } + keySet.add(key); + + } + + + return builder.build(); + } + + private void updateIndex(ConsulResponse>> consulResponse) { + if (consulResponse != null && consulResponse.getIndex() != null) { + this.latestIndex.set(consulResponse.getIndex()); + } + } + + protected static QueryOptions watchParams(BigInteger index, int blockSeconds) { + if (index == null) { + return QueryOptions.BLANK; + } else { + return QueryOptions.blockSeconds(blockSeconds, index).build(); + } + } + + /** + * passed in by creators to vary the content of the cached values + * + * @param + */ + protected interface CallbackConsumer { + void consume(BigInteger index, ConsulResponseCallback>> callback); + } + + /** + * Implementers can register a listener to receive + * a new map when it changes + * + * @param + */ + public interface Listener { + void notify(List oldValues,List newValues); + } + + public boolean addListener(Listener listener) { + boolean added = listeners.add(listener); + if (state.get() == State.started) { + listener.notify(lastResponse.get(),lastResponse.get()); + } + return added; + } + + public boolean removeListener(Listener listener) { + return listeners.remove(listener); + } + + @VisibleForTesting + protected State getState() { + return state.get(); + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/HealthCache.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/HealthCache.java new file mode 100644 index 0000000..0134eeb --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/HealthCache.java @@ -0,0 +1,70 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.cache; + +import java.math.BigInteger; +import java.util.List; + +import org.openo.msb.wrapper.consul.HealthClient; +import org.openo.msb.wrapper.consul.async.ConsulResponseCallback; +import org.openo.msb.wrapper.consul.model.health.ServiceHealth; + +import com.google.common.base.Function; + +public class HealthCache extends ConsulCache{ + + private final String serviceName; + + private HealthCache(Function keyConversion, + ConsulCache.CallbackConsumer callbackConsumer,String serviceName) { + super(keyConversion, callbackConsumer); + this.serviceName=serviceName; + // TODO Auto-generated constructor stub + } + + + public static HealthCache newCache( + final HealthClient healthClient, + final String serviceName, + final int watchSeconds){ + Function keyExtractor = new Function() { + @Override + public String apply(ServiceHealth input) { + //return input.getKey().substring(rootPath.length() + 1); + return input.getService().getId(); + } + }; + + final CallbackConsumer callbackConsumer = new CallbackConsumer() { + @Override + public void consume(BigInteger index, ConsulResponseCallback> callback) { + healthClient.getHealthyServiceInstances(serviceName, watchParams(index, watchSeconds),callback); + } + }; + + + return new HealthCache(keyExtractor, callbackConsumer,serviceName); + + + } + + public String getServiceName(){ + return this.serviceName; + } + + +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ServiceCache.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ServiceCache.java new file mode 100644 index 0000000..80cdb7b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/cache/ServiceCache.java @@ -0,0 +1,50 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.cache; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +import org.openo.msb.wrapper.consul.CatalogClient; +import org.openo.msb.wrapper.consul.async.ConsulResponseCallback; + +public class ServiceCache extends ConsulCache4Map>> { + private ServiceCache( ConsulCache4Map.CallbackConsumer>> callbackConsumer) { + super(callbackConsumer); + // TODO Auto-generated constructor stub + } + + + public static ServiceCache newCache( + final CatalogClient catalogClient, + final int watchSeconds){ + + + final CallbackConsumer>> callbackConsumer = new CallbackConsumer>>() { + @Override + public void consume(BigInteger index, ConsulResponseCallback>> callback) { + catalogClient.getService(watchParams(index, watchSeconds),callback); + } + }; + + + return new ServiceCache(callbackConsumer); + + + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/ConsulResponse.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/ConsulResponse.java new file mode 100644 index 0000000..7d3b2e2 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/ConsulResponse.java @@ -0,0 +1,80 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model; + +import com.google.common.base.Objects; + +import java.math.BigInteger; + +public class ConsulResponse { + + private final T response; + private final long lastContact; + private final boolean knownLeader; + private final BigInteger index; + + public ConsulResponse(T response, long lastContact, boolean knownLeader, BigInteger index) { + this.response = response; + this.lastContact = lastContact; + this.knownLeader = knownLeader; + this.index = index; + } + + public T getResponse() { + return response; + } + + public long getLastContact() { + return lastContact; + } + + public boolean isKnownLeader() { + return knownLeader; + } + + public BigInteger getIndex() { + return index; + } + + @Override + public String toString() { + return "ConsulResponse{" + + "response=" + response + + ", lastContact=" + lastContact + + ", knownLeader=" + knownLeader + + ", index=" + index + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ConsulResponse that = (ConsulResponse) o; + + return Objects.equal(this.response, that.response) && + Objects.equal(this.lastContact, that.lastContact) && + Objects.equal(this.knownLeader, that.knownLeader) && + Objects.equal(this.index, that.index); + } + + @Override + public int hashCode() { + return Objects.hashCode(response, lastContact, knownLeader, index); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/CatalogNode.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/CatalogNode.java new file mode 100644 index 0000000..1221c08 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/CatalogNode.java @@ -0,0 +1,41 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model.catalog; + +import java.util.Map; + +import org.openo.msb.wrapper.consul.model.health.Node; +import org.openo.msb.wrapper.consul.model.health.Service; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + + +@JsonSerialize(as = ImmutableCatalogNode.class) +@JsonDeserialize(as = ImmutableCatalogNode.class) +@JsonIgnoreProperties(ignoreUnknown = true) +public abstract class CatalogNode { + + @JsonProperty("Node") + public abstract Node getNode(); + + @JsonProperty("Services") + public abstract Map getServices(); + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/CatalogService.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/CatalogService.java new file mode 100644 index 0000000..5951fa7 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/CatalogService.java @@ -0,0 +1,52 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model.catalog; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + + +@JsonSerialize(as = ImmutableCatalogService.class) +@JsonDeserialize(as = ImmutableCatalogService.class) +@JsonIgnoreProperties(ignoreUnknown = true) +public abstract class CatalogService { + + @JsonProperty("Node") + public abstract String getNode(); + + @JsonProperty("Address") + public abstract String getAddress(); + + @JsonProperty("ServiceName") + public abstract String getServiceName(); + + @JsonProperty("ServiceID") + public abstract String getServiceId(); + + @JsonProperty("ServiceAddress") + public abstract String getServiceAddress(); + + @JsonProperty("ServicePort") + public abstract int getServicePort(); + + @JsonProperty("ServiceTags") + public abstract List getServiceTags(); +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ImmutableCatalogNode.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ImmutableCatalogNode.java new file mode 100644 index 0000000..f504168 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ImmutableCatalogNode.java @@ -0,0 +1,306 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model.catalog; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; + +import java.util.List; +import java.util.Map; + +import javax.annotation.Generated; + +import org.openo.msb.wrapper.consul.model.health.Node; +import org.openo.msb.wrapper.consul.model.health.Service; + +/** + * Immutable implementation of {@link CatalogNode}. + *

+ * Use the builder to create immutable instances: + * {@code ImmutableCatalogNode.builder()}. + */ +@SuppressWarnings("all") +@Generated({"Immutables.generator", "CatalogNode"}) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class ImmutableCatalogNode extends CatalogNode { + private final Node node; + private final ImmutableMap services; + + private ImmutableCatalogNode( + Node node, + ImmutableMap services) { + this.node = node; + this.services = services; + } + + /** + * @return The value of the {@code node} attribute + */ + @JsonProperty(value = "Node") + @Override + public Node getNode() { + return node; + } + + /** + * @return The value of the {@code services} attribute + */ + @JsonProperty(value = "Services") + @Override + public ImmutableMap getServices() { + return services; + } + + /** + * Copy the current immutable object by setting a value for the {@link CatalogNode#getNode() node} attribute. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param value A new value for node + * @return A modified copy of the {@code this} object + */ + public final ImmutableCatalogNode withNode(Node value) { + if (this.node == value) return this; + return new ImmutableCatalogNode(Preconditions.checkNotNull(value, "node"), this.services); + } + + /** + * Copy the current immutable object by replacing the {@link CatalogNode#getServices() services} map with the specified map. + * Nulls are not permitted as keys or values. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param entries The entries to be added to the services map + * @return A modified copy of {@code this} object + */ + public final ImmutableCatalogNode withServices(Map entries) { + if (this.services == entries) return this; + ImmutableMap value = ImmutableMap.copyOf(entries); + return new ImmutableCatalogNode(this.node, value); + } + + /** + * This instance is equal to all instances of {@code ImmutableCatalogNode} that have equal attribute values. + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if (this == another) return true; + return another instanceof ImmutableCatalogNode + && equalTo((ImmutableCatalogNode) another); + } + + private boolean equalTo(ImmutableCatalogNode another) { + return node.equals(another.node) + && services.equals(another.services); + } + + /** + * Computes a hash code from attributes: {@code node}, {@code services}. + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 31; + h = h * 17 + node.hashCode(); + h = h * 17 + services.hashCode(); + return h; + } + + /** + * Prints the immutable value {@code CatalogNode...} with all non-generated + * and non-auxiliary attribute values. + * @return A string representation of the value + */ + @Override + public String toString() { + return MoreObjects.toStringHelper("CatalogNode") + .add("node", node) + .add("services", services) + .toString(); + } + + /** + * Utility type used to correctly read immutable object from JSON representation. + * @deprecated Do not use this type directly, it exists only for the Jackson-binding infrastructure + */ + @Deprecated + @JsonDeserialize + static final class Json extends CatalogNode { + Node node; + Map services; + @JsonProperty(value = "Node") + public void setNode(Node node) { + this.node = node; + } + @JsonProperty(value = "Services") + public void setServices(Map services) { + this.services = services; + } + @Override + public Node getNode() { throw new UnsupportedOperationException(); } + @Override + public Map getServices() { throw new UnsupportedOperationException(); } + } + + /** + * @param json A JSON-bindable data structure + * @return An immutable value type + * @deprecated Do not use this method directly, it exists only for the Jackson-binding infrastructure + */ + @Deprecated + @JsonCreator + static ImmutableCatalogNode fromJson(Json json) { + ImmutableCatalogNode.Builder builder = ImmutableCatalogNode.builder(); + if (json.node != null) { + builder.node(json.node); + } + if (json.services != null) { + builder.putAllServices(json.services); + } + return builder.build(); + } + + /** + * Creates an immutable copy of a {@link CatalogNode} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * @param instance The instance to copy + * @return A copied immutable CatalogNode instance + */ + public static ImmutableCatalogNode copyOf(CatalogNode instance) { + if (instance instanceof ImmutableCatalogNode) { + return (ImmutableCatalogNode) instance; + } + return ImmutableCatalogNode.builder() + .from(instance) + .build(); + } + + /** + * Creates a builder for {@link ImmutableCatalogNode ImmutableCatalogNode}. + * @return A new ImmutableCatalogNode builder + */ + public static ImmutableCatalogNode.Builder builder() { + return new ImmutableCatalogNode.Builder(); + } + + /** + * Builds instances of type {@link ImmutableCatalogNode ImmutableCatalogNode}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static final class Builder { + private static final long INIT_BIT_NODE = 0x1L; + private long initBits = 0x1; + + private Node node; + private ImmutableMap.Builder servicesBuilder = ImmutableMap.builder(); + + private Builder() { + } + + /** + * Fill a builder with attribute values from the provided {@code CatalogNode} instance. + * Regular attribute values will be replaced with those from the given instance. + * Absent optional values will not replace present values. + * Collection elements and entries will be added, not replaced. + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(CatalogNode instance) { + Preconditions.checkNotNull(instance, "instance"); + node(instance.getNode()); + putAllServices(instance.getServices()); + return this; + } + + /** + * Initializes the value for the {@link CatalogNode#getNode() node} attribute. + * @param node The value for node + * @return {@code this} builder for use in a chained invocation + */ + public final Builder node(Node node) { + this.node = Preconditions.checkNotNull(node, "node"); + initBits &= ~INIT_BIT_NODE; + return this; + } + + /** + * Put one entry to the {@link CatalogNode#getServices() services} map. + * @param key The key in the services map + * @param value The associated value in the services map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putServices(String key, Service value) { + servicesBuilder.put(key, value); + return this; + } + + /** + * Put one entry to the {@link CatalogNode#getServices() services} map. Nulls are not permitted + * @param entry The key and value entry + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putServices(Map.Entry entry) { + servicesBuilder.put(entry); + return this; + } + + /** + * Sets or replaces all mappings from the specified map as entries for the {@link CatalogNode#getServices() services} map. Nulls are not permitted + * @param entries The entries that will be added to the services map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder services(Map entries) { + servicesBuilder = ImmutableMap.builder(); + return putAllServices(entries); + } + + /** + * Put all mappings from the specified map as entries to {@link CatalogNode#getServices() services} map. Nulls are not permitted + * @param entries The entries that will be added to the services map + * @return {@code this} builder for use in a chained invocation + */ + public final Builder putAllServices(Map entries) { + servicesBuilder.putAll(entries); + return this; + } + + /** + * Builds a new {@link ImmutableCatalogNode ImmutableCatalogNode}. + * @return An immutable instance of CatalogNode + * @throws java.lang.IllegalStateException if any required attributes are missing + */ + public ImmutableCatalogNode build() throws IllegalStateException { + if (initBits != 0) { + throw new IllegalStateException(formatRequiredAttributesMessage()); + } + return new ImmutableCatalogNode(node, servicesBuilder.build()); + } + + private String formatRequiredAttributesMessage() { + List attributes = Lists.newArrayList(); + if ((initBits & INIT_BIT_NODE) != 0) attributes.add("node"); + return "Cannot build CatalogNode, some of required attributes are not set " + attributes; + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ImmutableCatalogService.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ImmutableCatalogService.java new file mode 100644 index 0000000..33d0e03 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ImmutableCatalogService.java @@ -0,0 +1,626 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model.catalog; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import java.util.List; +import javax.annotation.Generated; + +/** + * Immutable implementation of {@link CatalogService}. + *

+ * Use the builder to create immutable instances: + * {@code ImmutableCatalogService.builder()}. + */ +@SuppressWarnings("all") +@Generated({"Immutables.generator", "CatalogService"}) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class ImmutableCatalogService extends CatalogService { + private final String node; + private final String address; + private final String serviceName; + private final String serviceId; + private final String serviceAddress; + private final int servicePort; + private final ImmutableList serviceTags; + + private ImmutableCatalogService( + String node, + String address, + String serviceName, + String serviceId, + String serviceAddress, + int servicePort, + ImmutableList serviceTags) { + this.node = node; + this.address = address; + this.serviceName = serviceName; + this.serviceId = serviceId; + this.serviceAddress = serviceAddress; + this.servicePort = servicePort; + this.serviceTags = serviceTags; + } + + /** + * @return The value of the {@code node} attribute + */ + @JsonProperty(value = "Node") + @Override + public String getNode() { + return node; + } + + /** + * @return The value of the {@code address} attribute + */ + @JsonProperty(value = "Address") + @Override + public String getAddress() { + return address; + } + + /** + * @return The value of the {@code serviceName} attribute + */ + @JsonProperty(value = "ServiceName") + @Override + public String getServiceName() { + return serviceName; + } + + /** + * @return The value of the {@code serviceId} attribute + */ + @JsonProperty(value = "ServiceID") + @Override + public String getServiceId() { + return serviceId; + } + + /** + * @return The value of the {@code serviceAddress} attribute + */ + @JsonProperty(value = "ServiceAddress") + @Override + public String getServiceAddress() { + return serviceAddress; + } + + /** + * @return The value of the {@code servicePort} attribute + */ + @JsonProperty(value = "ServicePort") + @Override + public int getServicePort() { + return servicePort; + } + + /** + * @return The value of the {@code serviceTags} attribute + */ + @JsonProperty(value = "ServiceTags") + @Override + public ImmutableList getServiceTags() { + return serviceTags; + } + + /** + * Copy the current immutable object by setting a value for the {@link CatalogService#getNode() node} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for node + * @return A modified copy of the {@code this} object + */ + public final ImmutableCatalogService withNode(String value) { + if (this.node.equals(value)) return this; + return new ImmutableCatalogService( + Preconditions.checkNotNull(value, "node"), + this.address, + this.serviceName, + this.serviceId, + this.serviceAddress, + this.servicePort, + this.serviceTags); + } + + /** + * Copy the current immutable object by setting a value for the {@link CatalogService#getAddress() address} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for address + * @return A modified copy of the {@code this} object + */ + public final ImmutableCatalogService withAddress(String value) { + if (this.address.equals(value)) return this; + return new ImmutableCatalogService( + this.node, + Preconditions.checkNotNull(value, "address"), + this.serviceName, + this.serviceId, + this.serviceAddress, + this.servicePort, + this.serviceTags); + } + + /** + * Copy the current immutable object by setting a value for the {@link CatalogService#getServiceName() serviceName} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for serviceName + * @return A modified copy of the {@code this} object + */ + public final ImmutableCatalogService withServiceName(String value) { + if (this.serviceName.equals(value)) return this; + return new ImmutableCatalogService( + this.node, + this.address, + Preconditions.checkNotNull(value, "serviceName"), + this.serviceId, + this.serviceAddress, + this.servicePort, + this.serviceTags); + } + + /** + * Copy the current immutable object by setting a value for the {@link CatalogService#getServiceId() serviceId} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for serviceId + * @return A modified copy of the {@code this} object + */ + public final ImmutableCatalogService withServiceId(String value) { + if (this.serviceId.equals(value)) return this; + return new ImmutableCatalogService( + this.node, + this.address, + this.serviceName, + Preconditions.checkNotNull(value, "serviceId"), + this.serviceAddress, + this.servicePort, + this.serviceTags); + } + + /** + * Copy the current immutable object by setting a value for the {@link CatalogService#getServiceAddress() serviceAddress} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for serviceAddress + * @return A modified copy of the {@code this} object + */ + public final ImmutableCatalogService withServiceAddress(String value) { + if (this.serviceAddress.equals(value)) return this; + return new ImmutableCatalogService( + this.node, + this.address, + this.serviceName, + this.serviceId, + Preconditions.checkNotNull(value, "serviceAddress"), + this.servicePort, + this.serviceTags); + } + + /** + * Copy the current immutable object by setting a value for the {@link CatalogService#getServicePort() servicePort} attribute. + * A value equality check is used to prevent copying of the same value by returning {@code this}. + * @param value A new value for servicePort + * @return A modified copy of the {@code this} object + */ + public final ImmutableCatalogService withServicePort(int value) { + if (this.servicePort == value) return this; + return new ImmutableCatalogService( + this.node, + this.address, + this.serviceName, + this.serviceId, + this.serviceAddress, + value, + this.serviceTags); + } + + /** + * Copy the current immutable object with elements that replace the content of {@link CatalogService#getServiceTags() serviceTags}. + * @param elements The elements to set + * @return A modified copy of {@code this} object + */ + public final ImmutableCatalogService withServiceTags(String... elements) { + ImmutableList newValue = ImmutableList.copyOf(elements); + return new ImmutableCatalogService( + this.node, + this.address, + this.serviceName, + this.serviceId, + this.serviceAddress, + this.servicePort, + newValue); + } + + /** + * Copy the current immutable object with elements that replace the content of {@link CatalogService#getServiceTags() serviceTags}. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param elements An iterable of serviceTags elements to set + * @return A modified copy of {@code this} object + */ + public final ImmutableCatalogService withServiceTags(Iterable elements) { + if (this.serviceTags == elements) return this; + ImmutableList newValue = ImmutableList.copyOf(elements); + return new ImmutableCatalogService( + this.node, + this.address, + this.serviceName, + this.serviceId, + this.serviceAddress, + this.servicePort, + newValue); + } + + /** + * This instance is equal to all instances of {@code ImmutableCatalogService} that have equal attribute values. + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if (this == another) return true; + return another instanceof ImmutableCatalogService + && equalTo((ImmutableCatalogService) another); + } + + private boolean equalTo(ImmutableCatalogService another) { + return node.equals(another.node) + && address.equals(another.address) + && serviceName.equals(another.serviceName) + && serviceId.equals(another.serviceId) + && serviceAddress.equals(another.serviceAddress) + && servicePort == another.servicePort + && serviceTags.equals(another.serviceTags); + } + + /** + * Computes a hash code from attributes: {@code node}, {@code address}, {@code serviceName}, {@code serviceId}, {@code serviceAddress}, {@code servicePort}, {@code serviceTags}. + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 31; + h = h * 17 + node.hashCode(); + h = h * 17 + address.hashCode(); + h = h * 17 + serviceName.hashCode(); + h = h * 17 + serviceId.hashCode(); + h = h * 17 + serviceAddress.hashCode(); + h = h * 17 + servicePort; + h = h * 17 + serviceTags.hashCode(); + return h; + } + + /** + * Prints the immutable value {@code CatalogService...} with all non-generated + * and non-auxiliary attribute values. + * @return A string representation of the value + */ + @Override + public String toString() { + return MoreObjects.toStringHelper("CatalogService") + .add("node", node) + .add("address", address) + .add("serviceName", serviceName) + .add("serviceId", serviceId) + .add("serviceAddress", serviceAddress) + .add("servicePort", servicePort) + .add("serviceTags", serviceTags) + .toString(); + } + + /** + * Utility type used to correctly read immutable object from JSON representation. + * @deprecated Do not use this type directly, it exists only for the Jackson-binding infrastructure + */ + @Deprecated + @JsonDeserialize + static final class Json extends CatalogService { + String node; + String address; + String serviceName; + String serviceId; + String serviceAddress; + Integer servicePort; + List serviceTags = ImmutableList.of(); + @JsonProperty(value = "Node") + public void setNode(String node) { + this.node = node; + } + @JsonProperty(value = "Address") + public void setAddress(String address) { + this.address = address; + } + @JsonProperty(value = "ServiceName") + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + @JsonProperty(value = "ServiceID") + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + @JsonProperty(value = "ServiceAddress") + public void setServiceAddress(String serviceAddress) { + this.serviceAddress = serviceAddress; + } + @JsonProperty(value = "ServicePort") + public void setServicePort(int servicePort) { + this.servicePort = servicePort; + } + @JsonProperty(value = "ServiceTags") + public void setServiceTags(List serviceTags) { + this.serviceTags = serviceTags; + } + @Override + public String getNode() { throw new UnsupportedOperationException(); } + @Override + public String getAddress() { throw new UnsupportedOperationException(); } + @Override + public String getServiceName() { throw new UnsupportedOperationException(); } + @Override + public String getServiceId() { throw new UnsupportedOperationException(); } + @Override + public String getServiceAddress() { throw new UnsupportedOperationException(); } + @Override + public int getServicePort() { throw new UnsupportedOperationException(); } + @Override + public List getServiceTags() { throw new UnsupportedOperationException(); } + } + + /** + * @param json A JSON-bindable data structure + * @return An immutable value type + * @deprecated Do not use this method directly, it exists only for the Jackson-binding infrastructure + */ + @Deprecated + @JsonCreator + static ImmutableCatalogService fromJson(Json json) { + ImmutableCatalogService.Builder builder = ImmutableCatalogService.builder(); + if (json.node != null) { + builder.node(json.node); + } + if (json.address != null) { + builder.address(json.address); + } + if (json.serviceName != null) { + builder.serviceName(json.serviceName); + } + if (json.serviceId != null) { + builder.serviceId(json.serviceId); + } + if (json.serviceAddress != null) { + builder.serviceAddress(json.serviceAddress); + } + if (json.servicePort != null) { + builder.servicePort(json.servicePort); + } + if (json.serviceTags != null) { + builder.addAllServiceTags(json.serviceTags); + } + return builder.build(); + } + + /** + * Creates an immutable copy of a {@link CatalogService} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * @param instance The instance to copy + * @return A copied immutable CatalogService instance + */ + public static ImmutableCatalogService copyOf(CatalogService instance) { + if (instance instanceof ImmutableCatalogService) { + return (ImmutableCatalogService) instance; + } + return ImmutableCatalogService.builder() + .from(instance) + .build(); + } + + /** + * Creates a builder for {@link ImmutableCatalogService ImmutableCatalogService}. + * @return A new ImmutableCatalogService builder + */ + public static ImmutableCatalogService.Builder builder() { + return new ImmutableCatalogService.Builder(); + } + + /** + * Builds instances of type {@link ImmutableCatalogService ImmutableCatalogService}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static final class Builder { + private static final long INIT_BIT_NODE = 0x1L; + private static final long INIT_BIT_ADDRESS = 0x2L; + private static final long INIT_BIT_SERVICE_NAME = 0x4L; + private static final long INIT_BIT_SERVICE_ID = 0x8L; + private static final long INIT_BIT_SERVICE_ADDRESS = 0x10L; + private static final long INIT_BIT_SERVICE_PORT = 0x20L; + private long initBits = 0x3f; + + private String node; + private String address; + private String serviceName; + private String serviceId; + private String serviceAddress; + private int servicePort; + private ImmutableList.Builder serviceTagsBuilder = ImmutableList.builder(); + + private Builder() { + } + + /** + * Fill a builder with attribute values from the provided {@code CatalogService} instance. + * Regular attribute values will be replaced with those from the given instance. + * Absent optional values will not replace present values. + * Collection elements and entries will be added, not replaced. + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(CatalogService instance) { + Preconditions.checkNotNull(instance, "instance"); + node(instance.getNode()); + address(instance.getAddress()); + serviceName(instance.getServiceName()); + serviceId(instance.getServiceId()); + serviceAddress(instance.getServiceAddress()); + servicePort(instance.getServicePort()); + addAllServiceTags(instance.getServiceTags()); + return this; + } + + /** + * Initializes the value for the {@link CatalogService#getNode() node} attribute. + * @param node The value for node + * @return {@code this} builder for use in a chained invocation + */ + public final Builder node(String node) { + this.node = Preconditions.checkNotNull(node, "node"); + initBits &= ~INIT_BIT_NODE; + return this; + } + + /** + * Initializes the value for the {@link CatalogService#getAddress() address} attribute. + * @param address The value for address + * @return {@code this} builder for use in a chained invocation + */ + public final Builder address(String address) { + this.address = Preconditions.checkNotNull(address, "address"); + initBits &= ~INIT_BIT_ADDRESS; + return this; + } + + /** + * Initializes the value for the {@link CatalogService#getServiceName() serviceName} attribute. + * @param serviceName The value for serviceName + * @return {@code this} builder for use in a chained invocation + */ + public final Builder serviceName(String serviceName) { + this.serviceName = Preconditions.checkNotNull(serviceName, "serviceName"); + initBits &= ~INIT_BIT_SERVICE_NAME; + return this; + } + + /** + * Initializes the value for the {@link CatalogService#getServiceId() serviceId} attribute. + * @param serviceId The value for serviceId + * @return {@code this} builder for use in a chained invocation + */ + public final Builder serviceId(String serviceId) { + this.serviceId = Preconditions.checkNotNull(serviceId, "serviceId"); + initBits &= ~INIT_BIT_SERVICE_ID; + return this; + } + + /** + * Initializes the value for the {@link CatalogService#getServiceAddress() serviceAddress} attribute. + * @param serviceAddress The value for serviceAddress + * @return {@code this} builder for use in a chained invocation + */ + public final Builder serviceAddress(String serviceAddress) { + this.serviceAddress = Preconditions.checkNotNull(serviceAddress, "serviceAddress"); + initBits &= ~INIT_BIT_SERVICE_ADDRESS; + return this; + } + + /** + * Initializes the value for the {@link CatalogService#getServicePort() servicePort} attribute. + * @param servicePort The value for servicePort + * @return {@code this} builder for use in a chained invocation + */ + public final Builder servicePort(int servicePort) { + this.servicePort = servicePort; + initBits &= ~INIT_BIT_SERVICE_PORT; + return this; + } + + /** + * Adds one element to {@link CatalogService#getServiceTags() serviceTags} list. + * @param element A serviceTags element + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addServiceTags(String element) { + serviceTagsBuilder.add(element); + return this; + } + + /** + * Adds elements to {@link CatalogService#getServiceTags() serviceTags} list. + * @param elements An array of serviceTags elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addServiceTags(String... elements) { + serviceTagsBuilder.add(elements); + return this; + } + + /** + * Sets or replaces all elements for {@link CatalogService#getServiceTags() serviceTags} list. + * @param elements An iterable of serviceTags elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder serviceTags(Iterable elements) { + serviceTagsBuilder = ImmutableList.builder(); + return addAllServiceTags(elements); + } + + /** + * Adds elements to {@link CatalogService#getServiceTags() serviceTags} list. + * @param elements An iterable of serviceTags elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addAllServiceTags(Iterable elements) { + serviceTagsBuilder.addAll(elements); + return this; + } + + /** + * Builds a new {@link ImmutableCatalogService ImmutableCatalogService}. + * @return An immutable instance of CatalogService + * @throws java.lang.IllegalStateException if any required attributes are missing + */ + public ImmutableCatalogService build() throws IllegalStateException { + if (initBits != 0) { + throw new IllegalStateException(formatRequiredAttributesMessage()); + } + return new ImmutableCatalogService( + node, + address, + serviceName, + serviceId, + serviceAddress, + servicePort, + serviceTagsBuilder.build()); + } + + private String formatRequiredAttributesMessage() { + List attributes = Lists.newArrayList(); + if ((initBits & INIT_BIT_NODE) != 0) attributes.add("node"); + if ((initBits & INIT_BIT_ADDRESS) != 0) attributes.add("address"); + if ((initBits & INIT_BIT_SERVICE_NAME) != 0) attributes.add("serviceName"); + if ((initBits & INIT_BIT_SERVICE_ID) != 0) attributes.add("serviceId"); + if ((initBits & INIT_BIT_SERVICE_ADDRESS) != 0) attributes.add("serviceAddress"); + if ((initBits & INIT_BIT_SERVICE_PORT) != 0) attributes.add("servicePort"); + return "Cannot build CatalogService, some of required attributes are not set " + attributes; + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ServiceInfo.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ServiceInfo.java new file mode 100644 index 0000000..657af12 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/catalog/ServiceInfo.java @@ -0,0 +1,63 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model.catalog; + +import com.google.common.base.Objects; + +public class ServiceInfo { + + private String serviceName; + + private String version=""; + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + @Override + public boolean equals(Object other) + { + if(this == other) + return true; + if(other instanceof ServiceInfo) + { + ServiceInfo that = (ServiceInfo)other; + return Objects.equal(serviceName, that.serviceName) && Objects.equal(version, that.version); + } else + { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hashCode(serviceName, version); + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ImmutableNode.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ImmutableNode.java new file mode 100644 index 0000000..8499ac3 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ImmutableNode.java @@ -0,0 +1,266 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model.health; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import java.util.List; +import javax.annotation.Generated; + +/** + * Immutable implementation of {@link Node}. + *

+ * Use the builder to create immutable instances: + * {@code ImmutableNode.builder()}. + */ +@SuppressWarnings("all") +@Generated({"Immutables.generator", "Node"}) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class ImmutableNode extends Node { + private final String node; + private final String address; + + private ImmutableNode(String node, String address) { + this.node = node; + this.address = address; + } + + /** + * @return The value of the {@code node} attribute + */ + @JsonProperty(value = "Node") + @Override + public String getNode() { + return node; + } + + /** + * @return The value of the {@code address} attribute + */ + @JsonProperty(value = "Address") + @Override + public String getAddress() { + return address; + } + + /** + * Copy the current immutable object by setting a value for the {@link Node#getNode() node} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for node + * @return A modified copy of the {@code this} object + */ + public final ImmutableNode withNode(String value) { + if (this.node.equals(value)) return this; + return new ImmutableNode(Preconditions.checkNotNull(value, "node"), this.address); + } + + /** + * Copy the current immutable object by setting a value for the {@link Node#getAddress() address} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for address + * @return A modified copy of the {@code this} object + */ + public final ImmutableNode withAddress(String value) { + if (this.address.equals(value)) return this; + return new ImmutableNode(this.node, Preconditions.checkNotNull(value, "address")); + } + + /** + * This instance is equal to all instances of {@code ImmutableNode} that have equal attribute values. + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if (this == another) return true; + return another instanceof ImmutableNode + && equalTo((ImmutableNode) another); + } + + private boolean equalTo(ImmutableNode another) { + return node.equals(another.node) + && address.equals(another.address); + } + + /** + * Computes a hash code from attributes: {@code node}, {@code address}. + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 31; + h = h * 17 + node.hashCode(); + h = h * 17 + address.hashCode(); + return h; + } + + /** + * Prints the immutable value {@code Node...} with all non-generated + * and non-auxiliary attribute values. + * @return A string representation of the value + */ + @Override + public String toString() { + return MoreObjects.toStringHelper("Node") + .add("node", node) + .add("address", address) + .toString(); + } + + /** + * Utility type used to correctly read immutable object from JSON representation. + * @deprecated Do not use this type directly, it exists only for the Jackson-binding infrastructure + */ + @Deprecated + @JsonDeserialize + static final class Json extends Node { + String node; + String address; + @JsonProperty(value = "Node") + public void setNode(String node) { + this.node = node; + } + @JsonProperty(value = "Address") + public void setAddress(String address) { + this.address = address; + } + @Override + public String getNode() { throw new UnsupportedOperationException(); } + @Override + public String getAddress() { throw new UnsupportedOperationException(); } + } + + /** + * @param json A JSON-bindable data structure + * @return An immutable value type + * @deprecated Do not use this method directly, it exists only for the Jackson-binding infrastructure + */ + @Deprecated + @JsonCreator + static ImmutableNode fromJson(Json json) { + ImmutableNode.Builder builder = ImmutableNode.builder(); + if (json.node != null) { + builder.node(json.node); + } + if (json.address != null) { + builder.address(json.address); + } + return builder.build(); + } + + /** + * Creates an immutable copy of a {@link Node} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * @param instance The instance to copy + * @return A copied immutable Node instance + */ + public static ImmutableNode copyOf(Node instance) { + if (instance instanceof ImmutableNode) { + return (ImmutableNode) instance; + } + return ImmutableNode.builder() + .from(instance) + .build(); + } + + /** + * Creates a builder for {@link ImmutableNode ImmutableNode}. + * @return A new ImmutableNode builder + */ + public static ImmutableNode.Builder builder() { + return new ImmutableNode.Builder(); + } + + /** + * Builds instances of type {@link ImmutableNode ImmutableNode}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static final class Builder { + private static final long INIT_BIT_NODE = 0x1L; + private static final long INIT_BIT_ADDRESS = 0x2L; + private long initBits = 0x3; + + private String node; + private String address; + + private Builder() { + } + + /** + * Fill a builder with attribute values from the provided {@code Node} instance. + * Regular attribute values will be replaced with those from the given instance. + * Absent optional values will not replace present values. + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(Node instance) { + Preconditions.checkNotNull(instance, "instance"); + node(instance.getNode()); + address(instance.getAddress()); + return this; + } + + /** + * Initializes the value for the {@link Node#getNode() node} attribute. + * @param node The value for node + * @return {@code this} builder for use in a chained invocation + */ + public final Builder node(String node) { + this.node = Preconditions.checkNotNull(node, "node"); + initBits &= ~INIT_BIT_NODE; + return this; + } + + /** + * Initializes the value for the {@link Node#getAddress() address} attribute. + * @param address The value for address + * @return {@code this} builder for use in a chained invocation + */ + public final Builder address(String address) { + this.address = Preconditions.checkNotNull(address, "address"); + initBits &= ~INIT_BIT_ADDRESS; + return this; + } + + /** + * Builds a new {@link ImmutableNode ImmutableNode}. + * @return An immutable instance of Node + * @throws java.lang.IllegalStateException if any required attributes are missing + */ + public ImmutableNode build() throws IllegalStateException { + if (initBits != 0) { + throw new IllegalStateException(formatRequiredAttributesMessage()); + } + return new ImmutableNode(node, address); + } + + private String formatRequiredAttributesMessage() { + List attributes = Lists.newArrayList(); + if ((initBits & INIT_BIT_NODE) != 0) attributes.add("node"); + if ((initBits & INIT_BIT_ADDRESS) != 0) attributes.add("address"); + return "Cannot build Node, some of required attributes are not set " + attributes; + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ImmutableService.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ImmutableService.java new file mode 100644 index 0000000..70eabf2 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ImmutableService.java @@ -0,0 +1,478 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model.health; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import java.util.List; +import javax.annotation.Generated; + +/** + * Immutable implementation of {@link Service}. + *

+ * Use the builder to create immutable instances: + * {@code ImmutableService.builder()}. + */ +@SuppressWarnings("all") +@Generated({"Immutables.generator", "Service"}) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class ImmutableService extends Service { + private final String id; + private final String service; + private final ImmutableList tags; + private final String address; + private final int port; + + private ImmutableService( + String id, + String service, + ImmutableList tags, + String address, + int port) { + this.id = id; + this.service = service; + this.tags = tags; + this.address = address; + this.port = port; + } + + /** + * @return The value of the {@code id} attribute + */ + @JsonProperty(value = "ID") + @Override + public String getId() { + return id; + } + + /** + * @return The value of the {@code service} attribute + */ + @JsonProperty(value = "Service") + @Override + public String getService() { + return service; + } + + /** + * @return The value of the {@code tags} attribute + */ + @JsonProperty(value = "Tags") + @JsonDeserialize(as = ImmutableList.class, contentAs = String.class) + @Override + public ImmutableList getTags() { + return tags; + } + + /** + * @return The value of the {@code address} attribute + */ + @JsonProperty(value = "Address") + @Override + public String getAddress() { + return address; + } + + /** + * @return The value of the {@code port} attribute + */ + @JsonProperty(value = "Port") + @Override + public int getPort() { + return port; + } + + /** + * Copy the current immutable object by setting a value for the {@link Service#getId() id} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for id + * @return A modified copy of the {@code this} object + */ + public final ImmutableService withId(String value) { + if (this.id.equals(value)) return this; + return new ImmutableService( + Preconditions.checkNotNull(value, "id"), + this.service, + this.tags, + this.address, + this.port); + } + + /** + * Copy the current immutable object by setting a value for the {@link Service#getService() service} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for service + * @return A modified copy of the {@code this} object + */ + public final ImmutableService withService(String value) { + if (this.service.equals(value)) return this; + return new ImmutableService( + this.id, + Preconditions.checkNotNull(value, "service"), + this.tags, + this.address, + this.port); + } + + /** + * Copy the current immutable object with elements that replace the content of {@link Service#getTags() tags}. + * @param elements The elements to set + * @return A modified copy of {@code this} object + */ + public final ImmutableService withTags(String... elements) { + ImmutableList newValue = ImmutableList.copyOf(elements); + return new ImmutableService(this.id, this.service, newValue, this.address, this.port); + } + + /** + * Copy the current immutable object with elements that replace the content of {@link Service#getTags() tags}. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param elements An iterable of tags elements to set + * @return A modified copy of {@code this} object + */ + public final ImmutableService withTags(Iterable elements) { + if (this.tags == elements) return this; + ImmutableList newValue = ImmutableList.copyOf(elements); + return new ImmutableService(this.id, this.service, newValue, this.address, this.port); + } + + /** + * Copy the current immutable object by setting a value for the {@link Service#getAddress() address} attribute. + * An equals check used to prevent copying of the same value by returning {@code this}. + * @param value A new value for address + * @return A modified copy of the {@code this} object + */ + public final ImmutableService withAddress(String value) { + if (this.address.equals(value)) return this; + return new ImmutableService( + this.id, + this.service, + this.tags, + Preconditions.checkNotNull(value, "address"), + this.port); + } + + /** + * Copy the current immutable object by setting a value for the {@link Service#getPort() port} attribute. + * A value equality check is used to prevent copying of the same value by returning {@code this}. + * @param value A new value for port + * @return A modified copy of the {@code this} object + */ + public final ImmutableService withPort(int value) { + if (this.port == value) return this; + return new ImmutableService(this.id, this.service, this.tags, this.address, value); + } + + /** + * This instance is equal to all instances of {@code ImmutableService} that have equal attribute values. + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if (this == another) return true; + return another instanceof ImmutableService + && equalTo((ImmutableService) another); + } + + private boolean equalTo(ImmutableService another) { + return id.equals(another.id) + && service.equals(another.service) + && tags.equals(another.tags) + && address.equals(another.address) + && port == another.port; + } + + /** + * Computes a hash code from attributes: {@code id}, {@code service}, {@code tags}, {@code address}, {@code port}. + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 31; + h = h * 17 + id.hashCode(); + h = h * 17 + service.hashCode(); + h = h * 17 + tags.hashCode(); + h = h * 17 + address.hashCode(); + h = h * 17 + port; + return h; + } + + /** + * Prints the immutable value {@code Service...} with all non-generated + * and non-auxiliary attribute values. + * @return A string representation of the value + */ + @Override + public String toString() { + return MoreObjects.toStringHelper("Service") + .add("id", id) + .add("service", service) + .add("tags", tags) + .add("address", address) + .add("port", port) + .toString(); + } + + /** + * Utility type used to correctly read immutable object from JSON representation. + * @deprecated Do not use this type directly, it exists only for the Jackson-binding infrastructure + */ + @Deprecated + @JsonDeserialize + static final class Json extends Service { + String id; + String service; + List tags = ImmutableList.of(); + String address; + Integer port; + @JsonProperty(value = "ID") + public void setId(String id) { + this.id = id; + } + @JsonProperty(value = "Service") + public void setService(String service) { + this.service = service; + } + @JsonProperty(value = "Tags") + @JsonDeserialize(as = ImmutableList.class, contentAs = String.class) + public void setTags(List tags) { + this.tags = tags; + } + @JsonProperty(value = "Address") + public void setAddress(String address) { + this.address = address; + } + @JsonProperty(value = "Port") + public void setPort(int port) { + this.port = port; + } + @Override + public String getId() { throw new UnsupportedOperationException(); } + @Override + public String getService() { throw new UnsupportedOperationException(); } + @Override + public List getTags() { throw new UnsupportedOperationException(); } + @Override + public String getAddress() { throw new UnsupportedOperationException(); } + @Override + public int getPort() { throw new UnsupportedOperationException(); } + } + + /** + * @param json A JSON-bindable data structure + * @return An immutable value type + * @deprecated Do not use this method directly, it exists only for the Jackson-binding infrastructure + */ + @Deprecated + @JsonCreator + static ImmutableService fromJson(Json json) { + ImmutableService.Builder builder = ImmutableService.builder(); + if (json.id != null) { + builder.id(json.id); + } + if (json.service != null) { + builder.service(json.service); + } + if (json.tags != null) { + builder.addAllTags(json.tags); + } + if (json.address != null) { + builder.address(json.address); + } + if (json.port != null) { + builder.port(json.port); + } + return builder.build(); + } + + /** + * Creates an immutable copy of a {@link Service} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * @param instance The instance to copy + * @return A copied immutable Service instance + */ + public static ImmutableService copyOf(Service instance) { + if (instance instanceof ImmutableService) { + return (ImmutableService) instance; + } + return ImmutableService.builder() + .from(instance) + .build(); + } + + /** + * Creates a builder for {@link ImmutableService ImmutableService}. + * @return A new ImmutableService builder + */ + public static ImmutableService.Builder builder() { + return new ImmutableService.Builder(); + } + + /** + * Builds instances of type {@link ImmutableService ImmutableService}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static final class Builder { + private static final long INIT_BIT_ID = 0x1L; + private static final long INIT_BIT_SERVICE = 0x2L; + private static final long INIT_BIT_ADDRESS = 0x4L; + private static final long INIT_BIT_PORT = 0x8L; + private long initBits = 0xf; + + private String id; + private String service; + private ImmutableList.Builder tagsBuilder = ImmutableList.builder(); + private String address; + private int port; + + private Builder() { + } + + /** + * Fill a builder with attribute values from the provided {@code Service} instance. + * Regular attribute values will be replaced with those from the given instance. + * Absent optional values will not replace present values. + * Collection elements and entries will be added, not replaced. + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(Service instance) { + Preconditions.checkNotNull(instance, "instance"); + id(instance.getId()); + service(instance.getService()); + addAllTags(instance.getTags()); + address(instance.getAddress()); + port(instance.getPort()); + return this; + } + + /** + * Initializes the value for the {@link Service#getId() id} attribute. + * @param id The value for id + * @return {@code this} builder for use in a chained invocation + */ + public final Builder id(String id) { + this.id = Preconditions.checkNotNull(id, "id"); + initBits &= ~INIT_BIT_ID; + return this; + } + + /** + * Initializes the value for the {@link Service#getService() service} attribute. + * @param service The value for service + * @return {@code this} builder for use in a chained invocation + */ + public final Builder service(String service) { + this.service = Preconditions.checkNotNull(service, "service"); + initBits &= ~INIT_BIT_SERVICE; + return this; + } + + /** + * Adds one element to {@link Service#getTags() tags} list. + * @param element A tags element + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addTags(String element) { + tagsBuilder.add(element); + return this; + } + + /** + * Adds elements to {@link Service#getTags() tags} list. + * @param elements An array of tags elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addTags(String... elements) { + tagsBuilder.add(elements); + return this; + } + + /** + * Sets or replaces all elements for {@link Service#getTags() tags} list. + * @param elements An iterable of tags elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder tags(Iterable elements) { + tagsBuilder = ImmutableList.builder(); + return addAllTags(elements); + } + + /** + * Adds elements to {@link Service#getTags() tags} list. + * @param elements An iterable of tags elements + * @return {@code this} builder for use in a chained invocation + */ + public final Builder addAllTags(Iterable elements) { + tagsBuilder.addAll(elements); + return this; + } + + /** + * Initializes the value for the {@link Service#getAddress() address} attribute. + * @param address The value for address + * @return {@code this} builder for use in a chained invocation + */ + public final Builder address(String address) { + this.address = Preconditions.checkNotNull(address, "address"); + initBits &= ~INIT_BIT_ADDRESS; + return this; + } + + /** + * Initializes the value for the {@link Service#getPort() port} attribute. + * @param port The value for port + * @return {@code this} builder for use in a chained invocation + */ + public final Builder port(int port) { + this.port = port; + initBits &= ~INIT_BIT_PORT; + return this; + } + + /** + * Builds a new {@link ImmutableService ImmutableService}. + * @return An immutable instance of Service + * @throws java.lang.IllegalStateException if any required attributes are missing + */ + public ImmutableService build() throws IllegalStateException { + if (initBits != 0) { + throw new IllegalStateException(formatRequiredAttributesMessage()); + } + return new ImmutableService(id, service, tagsBuilder.build(), address, port); + } + + private String formatRequiredAttributesMessage() { + List attributes = Lists.newArrayList(); + if ((initBits & INIT_BIT_ID) != 0) attributes.add("id"); + if ((initBits & INIT_BIT_SERVICE) != 0) attributes.add("service"); + if ((initBits & INIT_BIT_ADDRESS) != 0) attributes.add("address"); + if ((initBits & INIT_BIT_PORT) != 0) attributes.add("port"); + return "Cannot build Service, some of required attributes are not set " + attributes; + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/Node.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/Node.java new file mode 100644 index 0000000..83e6632 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/Node.java @@ -0,0 +1,35 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model.health; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + + +@JsonSerialize(as = ImmutableNode.class) +@JsonDeserialize(as = ImmutableNode.class) +@JsonIgnoreProperties(ignoreUnknown = true) +public abstract class Node { + + @JsonProperty("Node") + public abstract String getNode(); + + @JsonProperty("Address") + public abstract String getAddress(); +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/Service.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/Service.java new file mode 100644 index 0000000..379e9f1 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/Service.java @@ -0,0 +1,48 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model.health; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.collect.ImmutableList; + + +@JsonSerialize(as = ImmutableService.class) +@JsonDeserialize(as = ImmutableService.class) +@JsonIgnoreProperties(ignoreUnknown = true) +public abstract class Service { + + @JsonProperty("ID") + public abstract String getId(); + + @JsonProperty("Service") + public abstract String getService(); + + @JsonProperty("Tags") + @JsonDeserialize(as = ImmutableList.class, contentAs = String.class) + public abstract List getTags(); + + @JsonProperty("Address") + public abstract String getAddress(); + + @JsonProperty("Port") + public abstract int getPort(); +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ServiceHealth.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ServiceHealth.java new file mode 100644 index 0000000..d353b55 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/model/health/ServiceHealth.java @@ -0,0 +1,73 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.model.health; + +import org.openo.msb.wrapper.consul.model.catalog.ServiceInfo; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; + + +@JsonIgnoreProperties(ignoreUnknown = true) +public class ServiceHealth { + + @JsonProperty("Node") + public Node node; + + @JsonProperty("Service") + public Service service; + + public Node getNode() { + return node; + } + + public void setNode(Node node) { + this.node = node; + } + + public Service getService() { + return service; + } + + public void setService(Service service) { + this.service = service; + } + + @Override + public boolean equals(Object other) + { + if(this == other) + return true; + if(other instanceof ServiceHealth) + { + ServiceHealth that = (ServiceHealth)other; + return Objects.equal(node, that.node) && Objects.equal(service, that.service); + } else + { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hashCode(node, service); + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/CatalogOptions.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/CatalogOptions.java new file mode 100644 index 0000000..34972fe --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/CatalogOptions.java @@ -0,0 +1,39 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.option; + +import static org.openo.msb.wrapper.consul.option.Options.optionallyAdd; + +import javax.ws.rs.client.WebTarget; + +import com.google.common.base.Optional; + + +public abstract class CatalogOptions implements ParamAdder { + + public abstract Optional getDatacenter(); + public abstract Optional getTag(); + + public static final CatalogOptions BLANK = ImmutableCatalogOptions.builder().build(); + + @Override + public final WebTarget apply(final WebTarget input) { + WebTarget added = optionallyAdd(input, "dc", getDatacenter()); + added = optionallyAdd(added, "tag", getTag()); + return added; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ConsistencyMode.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ConsistencyMode.java new file mode 100644 index 0000000..52afa00 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ConsistencyMode.java @@ -0,0 +1,21 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.option; + +public enum ConsistencyMode { + DEFAULT, STALE, CONSISTENT +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ImmutableCatalogOptions.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ImmutableCatalogOptions.java new file mode 100644 index 0000000..35eda18 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ImmutableCatalogOptions.java @@ -0,0 +1,251 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.option; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import javax.annotation.Generated; + +/** + * Immutable implementation of {@link CatalogOptions}. + *

+ * Use the builder to create immutable instances: + * {@code ImmutableCatalogOptions.builder()}. + */ +@SuppressWarnings("all") +@Generated({"Immutables.generator", "CatalogOptions"}) +public final class ImmutableCatalogOptions extends CatalogOptions { + private final Optional datacenter; + private final Optional tag; + + private ImmutableCatalogOptions( + Optional datacenter, + Optional tag) { + this.datacenter = datacenter; + this.tag = tag; + } + + /** + * @return The value of the {@code datacenter} attribute + */ + @Override + public Optional getDatacenter() { + return datacenter; + } + + /** + * @return The value of the {@code tag} attribute + */ + @Override + public Optional getTag() { + return tag; + } + + /** + * Copy the current immutable object by setting a present value for the optional {@link CatalogOptions#getDatacenter() datacenter} attribute. + * @param value The value for datacenter + * @return A modified copy of {@code this} object + */ + public final ImmutableCatalogOptions withDatacenter(String value) { + Optional newValue = Optional.of(value); + return new ImmutableCatalogOptions(newValue, this.tag); + } + + /** + * Copy the current immutable object by setting an optional value for the {@link CatalogOptions#getDatacenter() datacenter} attribute. + * A shallow reference equality check on the optional value is used to prevent copying of the same value by returning {@code this}. + * @param optional A value for datacenter + * @return A modified copy of {@code this} object + */ + public final ImmutableCatalogOptions withDatacenter(Optional optional) { + Optional value = Preconditions.checkNotNull(optional, "datacenter"); + if (this.datacenter == value) return this; + return new ImmutableCatalogOptions(value, this.tag); + } + + /** + * Copy the current immutable object by setting a present value for the optional {@link CatalogOptions#getTag() tag} attribute. + * @param value The value for tag + * @return A modified copy of {@code this} object + */ + public final ImmutableCatalogOptions withTag(String value) { + Optional newValue = Optional.of(value); + return new ImmutableCatalogOptions(this.datacenter, newValue); + } + + /** + * Copy the current immutable object by setting an optional value for the {@link CatalogOptions#getTag() tag} attribute. + * A shallow reference equality check on the optional value is used to prevent copying of the same value by returning {@code this}. + * @param optional A value for tag + * @return A modified copy of {@code this} object + */ + public final ImmutableCatalogOptions withTag(Optional optional) { + Optional value = Preconditions.checkNotNull(optional, "tag"); + if (this.tag == value) return this; + return new ImmutableCatalogOptions(this.datacenter, value); + } + + /** + * This instance is equal to all instances of {@code ImmutableCatalogOptions} that have equal attribute values. + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if (this == another) return true; + return another instanceof ImmutableCatalogOptions + && equalTo((ImmutableCatalogOptions) another); + } + + private boolean equalTo(ImmutableCatalogOptions another) { + return datacenter.equals(another.datacenter) + && tag.equals(another.tag); + } + + /** + * Computes a hash code from attributes: {@code datacenter}, {@code tag}. + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 31; + h = h * 17 + datacenter.hashCode(); + h = h * 17 + tag.hashCode(); + return h; + } + + /** + * Prints the immutable value {@code CatalogOptions...} with all non-generated + * and non-auxiliary attribute values. + * @return A string representation of the value + */ + @Override + public String toString() { + return MoreObjects.toStringHelper("CatalogOptions") + .add("datacenter", datacenter) + .add("tag", tag) + .toString(); + } + + /** + * Creates an immutable copy of a {@link CatalogOptions} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * @param instance The instance to copy + * @return A copied immutable CatalogOptions instance + */ + public static ImmutableCatalogOptions copyOf(CatalogOptions instance) { + if (instance instanceof ImmutableCatalogOptions) { + return (ImmutableCatalogOptions) instance; + } + return ImmutableCatalogOptions.builder() + .from(instance) + .build(); + } + + /** + * Creates a builder for {@link ImmutableCatalogOptions ImmutableCatalogOptions}. + * @return A new ImmutableCatalogOptions builder + */ + public static ImmutableCatalogOptions.Builder builder() { + return new ImmutableCatalogOptions.Builder(); + } + + /** + * Builds instances of type {@link ImmutableCatalogOptions ImmutableCatalogOptions}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static final class Builder { + private Optional datacenter = Optional.absent(); + private Optional tag = Optional.absent(); + + private Builder() { + } + + /** + * Fill a builder with attribute values from the provided {@code CatalogOptions} instance. + * Regular attribute values will be replaced with those from the given instance. + * Absent optional values will not replace present values. + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(CatalogOptions instance) { + Preconditions.checkNotNull(instance, "instance"); + Optional datacenterOptional = instance.getDatacenter(); + if (datacenterOptional.isPresent()) { + datacenter(datacenterOptional); + } + Optional tagOptional = instance.getTag(); + if (tagOptional.isPresent()) { + tag(tagOptional); + } + return this; + } + + /** + * Initializes the optional value {@link CatalogOptions#getDatacenter() datacenter} to datacenter. + * @param datacenter The value for datacenter + * @return {@code this} builder for chained invocation + */ + public final Builder datacenter(String datacenter) { + this.datacenter = Optional.of(datacenter); + return this; + } + + /** + * Initializes the optional value {@link CatalogOptions#getDatacenter() datacenter} to datacenter. + * @param datacenter The value for datacenter + * @return {@code this} builder for use in a chained invocation + */ + public final Builder datacenter(Optional datacenter) { + this.datacenter = Preconditions.checkNotNull(datacenter, "datacenter"); + return this; + } + + /** + * Initializes the optional value {@link CatalogOptions#getTag() tag} to tag. + * @param tag The value for tag + * @return {@code this} builder for chained invocation + */ + public final Builder tag(String tag) { + this.tag = Optional.of(tag); + return this; + } + + /** + * Initializes the optional value {@link CatalogOptions#getTag() tag} to tag. + * @param tag The value for tag + * @return {@code this} builder for use in a chained invocation + */ + public final Builder tag(Optional tag) { + this.tag = Preconditions.checkNotNull(tag, "tag"); + return this; + } + + /** + * Builds a new {@link ImmutableCatalogOptions ImmutableCatalogOptions}. + * @return An immutable instance of CatalogOptions + * @throws java.lang.IllegalStateException if any required attributes are missing + */ + public ImmutableCatalogOptions build() throws IllegalStateException { + return new ImmutableCatalogOptions(datacenter, tag); + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ImmutableQueryOptions.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ImmutableQueryOptions.java new file mode 100644 index 0000000..87a0f8a --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ImmutableQueryOptions.java @@ -0,0 +1,531 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.option; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.primitives.Booleans; +import java.math.BigInteger; +import java.util.ArrayList; +import javax.annotation.Generated; + +/** + * Immutable implementation of {@link QueryOptions}. + *

+ * Use the builder to create immutable instances: + * {@code ImmutableQueryOptions.builder()}. + */ +@SuppressWarnings("all") +@Generated({"Immutables.generator", "QueryOptions"}) +public final class ImmutableQueryOptions extends QueryOptions { + private final Optional wait; + private final Optional token; + private final Optional index; + private final Optional near; + private final ConsistencyMode consistencyMode; + private final boolean isBlocking; + private final boolean hasToken; + + private ImmutableQueryOptions(ImmutableQueryOptions.Builder builder) { + this.wait = builder.wait; + this.token = builder.token; + this.index = builder.index; + this.near = builder.near; + if (builder.consistencyMode != null) { + initShim.consistencyMode(builder.consistencyMode); + } + this.consistencyMode = initShim.getConsistencyMode(); + this.isBlocking = initShim.isBlocking(); + this.hasToken = initShim.hasToken(); + this.initShim = null; + } + + private ImmutableQueryOptions( + Optional wait, + Optional token, + Optional index, + Optional near, + ConsistencyMode consistencyMode) { + this.wait = wait; + this.token = token; + this.index = index; + this.near = near; + this.consistencyMode = consistencyMode; + initShim.consistencyMode(consistencyMode); + this.isBlocking = initShim.isBlocking(); + this.hasToken = initShim.hasToken(); + this.initShim = null; + } + + private static final int STAGE_INITIALIZING = -1; + private static final int STAGE_UNINITIALIZED = 0; + private static final int STAGE_INITIALIZED = 1; + private volatile InitShim initShim = new InitShim(); + + private final class InitShim { + private ConsistencyMode consistencyMode; + private byte consistencyModeStage; + + ConsistencyMode getConsistencyMode() { + if (consistencyModeStage == STAGE_INITIALIZING) throw new IllegalStateException(formatInitCycleMessage()); + if (consistencyModeStage == STAGE_UNINITIALIZED) { + consistencyModeStage = STAGE_INITIALIZING; + this.consistencyMode = Preconditions.checkNotNull(ImmutableQueryOptions.super.getConsistencyMode(), "consistencyMode"); + consistencyModeStage = STAGE_INITIALIZED; + } + return consistencyMode; + } + + ConsistencyMode consistencyMode(ConsistencyMode value) { + this.consistencyMode = value; + consistencyModeStage = STAGE_INITIALIZED; + return value; + } + private boolean isBlocking; + private byte isBlockingStage; + + boolean isBlocking() { + if (isBlockingStage == STAGE_INITIALIZING) throw new IllegalStateException(formatInitCycleMessage()); + if (isBlockingStage == STAGE_UNINITIALIZED) { + isBlockingStage = STAGE_INITIALIZING; + this.isBlocking = ImmutableQueryOptions.super.isBlocking(); + isBlockingStage = STAGE_INITIALIZED; + } + return isBlocking; + } + private boolean hasToken; + private byte hasTokenStage; + + boolean hasToken() { + if (hasTokenStage == STAGE_INITIALIZING) throw new IllegalStateException(formatInitCycleMessage()); + if (hasTokenStage == STAGE_UNINITIALIZED) { + hasTokenStage = STAGE_INITIALIZING; + this.hasToken = ImmutableQueryOptions.super.hasToken(); + hasTokenStage = STAGE_INITIALIZED; + } + return hasToken; + } + + private String formatInitCycleMessage() { + ArrayList attributes = Lists.newArrayList(); + if (consistencyModeStage == STAGE_INITIALIZING) attributes.add("consistencyMode"); + if (isBlockingStage == STAGE_INITIALIZING) attributes.add("isBlocking"); + if (hasTokenStage == STAGE_INITIALIZING) attributes.add("hasToken"); + return "Cannot build QueryOptions, attribute initializers form cycle" + attributes; + } + } + + /** + * @return The value of the {@code wait} attribute + */ + @Override + public Optional getWait() { + return wait; + } + + /** + * @return The value of the {@code token} attribute + */ + @Override + public Optional getToken() { + return token; + } + + /** + * @return The value of the {@code index} attribute + */ + @Override + public Optional getIndex() { + return index; + } + + /** + * @return The value of the {@code near} attribute + */ + @Override + public Optional getNear() { + return near; + } + + /** + * @return The value of the {@code consistencyMode} attribute + */ + @Override + public ConsistencyMode getConsistencyMode() { + return initShim != null + ? initShim.getConsistencyMode() + : consistencyMode; + } + + /** + * @return The computed-at-construction value of the {@code isBlocking} attribute + */ + @Override + public boolean isBlocking() { + return initShim != null + ? initShim.isBlocking() + : isBlocking; + } + + /** + * @return The computed-at-construction value of the {@code hasToken} attribute + */ + @Override + public boolean hasToken() { + return initShim != null + ? initShim.hasToken() + : hasToken; + } + + /** + * Copy the current immutable object by setting a present value for the optional {@link QueryOptions#getWait() wait} attribute. + * @param value The value for wait + * @return A modified copy of {@code this} object + */ + public final ImmutableQueryOptions withWait(String value) { + Optional newValue = Optional.of(value); + return validate(new ImmutableQueryOptions(newValue, this.token, this.index, this.near, this.consistencyMode)); + } + + /** + * Copy the current immutable object by setting an optional value for the {@link QueryOptions#getWait() wait} attribute. + * A shallow reference equality check on the optional value is used to prevent copying of the same value by returning {@code this}. + * @param optional A value for wait + * @return A modified copy of {@code this} object + */ + public final ImmutableQueryOptions withWait(Optional optional) { + Optional value = Preconditions.checkNotNull(optional, "wait"); + if (this.wait == value) return this; + return validate(new ImmutableQueryOptions(value, this.token, this.index, this.near, this.consistencyMode)); + } + + /** + * Copy the current immutable object by setting a present value for the optional {@link QueryOptions#getToken() token} attribute. + * @param value The value for token + * @return A modified copy of {@code this} object + */ + public final ImmutableQueryOptions withToken(String value) { + Optional newValue = Optional.of(value); + return validate(new ImmutableQueryOptions(this.wait, newValue, this.index, this.near, this.consistencyMode)); + } + + /** + * Copy the current immutable object by setting an optional value for the {@link QueryOptions#getToken() token} attribute. + * A shallow reference equality check on the optional value is used to prevent copying of the same value by returning {@code this}. + * @param optional A value for token + * @return A modified copy of {@code this} object + */ + public final ImmutableQueryOptions withToken(Optional optional) { + Optional value = Preconditions.checkNotNull(optional, "token"); + if (this.token == value) return this; + return validate(new ImmutableQueryOptions(this.wait, value, this.index, this.near, this.consistencyMode)); + } + + /** + * Copy the current immutable object by setting a present value for the optional {@link QueryOptions#getIndex() index} attribute. + * @param value The value for index + * @return A modified copy of {@code this} object + */ + public final ImmutableQueryOptions withIndex(BigInteger value) { + Optional newValue = Optional.of(value); + return validate(new ImmutableQueryOptions(this.wait, this.token, newValue, this.near, this.consistencyMode)); + } + + /** + * Copy the current immutable object by setting an optional value for the {@link QueryOptions#getIndex() index} attribute. + * A shallow reference equality check on the optional value is used to prevent copying of the same value by returning {@code this}. + * @param optional A value for index + * @return A modified copy of {@code this} object + */ + public final ImmutableQueryOptions withIndex(Optional optional) { + Optional value = Preconditions.checkNotNull(optional, "index"); + if (this.index == value) return this; + return validate(new ImmutableQueryOptions(this.wait, this.token, value, this.near, this.consistencyMode)); + } + + /** + * Copy the current immutable object by setting a present value for the optional {@link QueryOptions#getNear() near} attribute. + * @param value The value for near + * @return A modified copy of {@code this} object + */ + public final ImmutableQueryOptions withNear(String value) { + Optional newValue = Optional.of(value); + return validate(new ImmutableQueryOptions(this.wait, this.token, this.index, newValue, this.consistencyMode)); + } + + /** + * Copy the current immutable object by setting an optional value for the {@link QueryOptions#getNear() near} attribute. + * A shallow reference equality check on the optional value is used to prevent copying of the same value by returning {@code this}. + * @param optional A value for near + * @return A modified copy of {@code this} object + */ + public final ImmutableQueryOptions withNear(Optional optional) { + Optional value = Preconditions.checkNotNull(optional, "near"); + if (this.near == value) return this; + return validate(new ImmutableQueryOptions(this.wait, this.token, this.index, value, this.consistencyMode)); + } + + /** + * Copy the current immutable object by setting a value for the {@link QueryOptions#getConsistencyMode() consistencyMode} attribute. + * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. + * @param value A new value for consistencyMode + * @return A modified copy of the {@code this} object + */ + public final ImmutableQueryOptions withConsistencyMode(ConsistencyMode value) { + if (this.consistencyMode == value) return this; + return validate(new ImmutableQueryOptions( + this.wait, + this.token, + this.index, + this.near, + Preconditions.checkNotNull(value, "consistencyMode"))); + } + + /** + * This instance is equal to all instances of {@code ImmutableQueryOptions} that have equal attribute values. + * @return {@code true} if {@code this} is equal to {@code another} instance + */ + @Override + public boolean equals(Object another) { + if (this == another) return true; + return another instanceof ImmutableQueryOptions + && equalTo((ImmutableQueryOptions) another); + } + + private boolean equalTo(ImmutableQueryOptions another) { + return wait.equals(another.wait) + && token.equals(another.token) + && index.equals(another.index) + && near.equals(another.near) + && consistencyMode.equals(another.consistencyMode) + && isBlocking == another.isBlocking + && hasToken == another.hasToken; + } + + /** + * Computes a hash code from attributes: {@code wait}, {@code token}, {@code index}, {@code near}, {@code consistencyMode}, {@code isBlocking}, {@code hasToken}. + * @return hashCode value + */ + @Override + public int hashCode() { + int h = 31; + h = h * 17 + wait.hashCode(); + h = h * 17 + token.hashCode(); + h = h * 17 + index.hashCode(); + h = h * 17 + near.hashCode(); + h = h * 17 + consistencyMode.hashCode(); + h = h * 17 + Booleans.hashCode(isBlocking); + h = h * 17 + Booleans.hashCode(hasToken); + return h; + } + + /** + * Prints the immutable value {@code QueryOptions...} with all non-generated + * and non-auxiliary attribute values. + * @return A string representation of the value + */ + @Override + public String toString() { + return MoreObjects.toStringHelper("QueryOptions") + .add("wait", wait) + .add("token", token) + .add("index", index) + .add("near", near) + .add("consistencyMode", consistencyMode) + .add("isBlocking", isBlocking) + .add("hasToken", hasToken) + .toString(); + } + + private static ImmutableQueryOptions validate(ImmutableQueryOptions instance) { + instance.validate(); + return instance; + } + + /** + * Creates an immutable copy of a {@link QueryOptions} value. + * Uses accessors to get values to initialize the new immutable instance. + * If an instance is already immutable, it is returned as is. + * @param instance The instance to copy + * @return A copied immutable QueryOptions instance + */ + public static ImmutableQueryOptions copyOf(QueryOptions instance) { + if (instance instanceof ImmutableQueryOptions) { + return (ImmutableQueryOptions) instance; + } + return ImmutableQueryOptions.builder() + .from(instance) + .build(); + } + + /** + * Creates a builder for {@link ImmutableQueryOptions ImmutableQueryOptions}. + * @return A new ImmutableQueryOptions builder + */ + public static ImmutableQueryOptions.Builder builder() { + return new ImmutableQueryOptions.Builder(); + } + + /** + * Builds instances of type {@link ImmutableQueryOptions ImmutableQueryOptions}. + * Initialize attributes and then invoke the {@link #build()} method to create an + * immutable instance. + *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, + * but instead used immediately to create instances. + */ + public static final class Builder { + private Optional wait = Optional.absent(); + private Optional token = Optional.absent(); + private Optional index = Optional.absent(); + private Optional near = Optional.absent(); + private ConsistencyMode consistencyMode; + + private Builder() { + } + + /** + * Fill a builder with attribute values from the provided {@code QueryOptions} instance. + * Regular attribute values will be replaced with those from the given instance. + * Absent optional values will not replace present values. + * @param instance The instance from which to copy values + * @return {@code this} builder for use in a chained invocation + */ + public final Builder from(QueryOptions instance) { + Preconditions.checkNotNull(instance, "instance"); + Optional waitOptional = instance.getWait(); + if (waitOptional.isPresent()) { + wait(waitOptional); + } + Optional tokenOptional = instance.getToken(); + if (tokenOptional.isPresent()) { + token(tokenOptional); + } + Optional indexOptional = instance.getIndex(); + if (indexOptional.isPresent()) { + index(indexOptional); + } + Optional nearOptional = instance.getNear(); + if (nearOptional.isPresent()) { + near(nearOptional); + } + consistencyMode(instance.getConsistencyMode()); + return this; + } + + /** + * Initializes the optional value {@link QueryOptions#getWait() wait} to wait. + * @param wait The value for wait + * @return {@code this} builder for chained invocation + */ + public final Builder wait(String wait) { + this.wait = Optional.of(wait); + return this; + } + + /** + * Initializes the optional value {@link QueryOptions#getWait() wait} to wait. + * @param wait The value for wait + * @return {@code this} builder for use in a chained invocation + */ + public final Builder wait(Optional wait) { + this.wait = Preconditions.checkNotNull(wait, "wait"); + return this; + } + + /** + * Initializes the optional value {@link QueryOptions#getToken() token} to token. + * @param token The value for token + * @return {@code this} builder for chained invocation + */ + public final Builder token(String token) { + this.token = Optional.of(token); + return this; + } + + /** + * Initializes the optional value {@link QueryOptions#getToken() token} to token. + * @param token The value for token + * @return {@code this} builder for use in a chained invocation + */ + public final Builder token(Optional token) { + this.token = Preconditions.checkNotNull(token, "token"); + return this; + } + + /** + * Initializes the optional value {@link QueryOptions#getIndex() index} to index. + * @param index The value for index + * @return {@code this} builder for chained invocation + */ + public final Builder index(BigInteger index) { + this.index = Optional.of(index); + return this; + } + + /** + * Initializes the optional value {@link QueryOptions#getIndex() index} to index. + * @param index The value for index + * @return {@code this} builder for use in a chained invocation + */ + public final Builder index(Optional index) { + this.index = Preconditions.checkNotNull(index, "index"); + return this; + } + + /** + * Initializes the optional value {@link QueryOptions#getNear() near} to near. + * @param near The value for near + * @return {@code this} builder for chained invocation + */ + public final Builder near(String near) { + this.near = Optional.of(near); + return this; + } + + /** + * Initializes the optional value {@link QueryOptions#getNear() near} to near. + * @param near The value for near + * @return {@code this} builder for use in a chained invocation + */ + public final Builder near(Optional near) { + this.near = Preconditions.checkNotNull(near, "near"); + return this; + } + + /** + * Initializes the value for the {@link QueryOptions#getConsistencyMode() consistencyMode} attribute. + *

If not set, this attribute will have a default value as returned by the initializer of {@link QueryOptions#getConsistencyMode() consistencyMode}. + * @param consistencyMode The value for consistencyMode + * @return {@code this} builder for use in a chained invocation + */ + public final Builder consistencyMode(ConsistencyMode consistencyMode) { + this.consistencyMode = Preconditions.checkNotNull(consistencyMode, "consistencyMode"); + return this; + } + + /** + * Builds a new {@link ImmutableQueryOptions ImmutableQueryOptions}. + * @return An immutable instance of QueryOptions + * @throws java.lang.IllegalStateException if any required attributes are missing + */ + public ImmutableQueryOptions build() throws IllegalStateException { + return ImmutableQueryOptions.validate(new ImmutableQueryOptions(this)); + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/Options.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/Options.java new file mode 100644 index 0000000..bccdd5c --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/Options.java @@ -0,0 +1,29 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.option; + +import com.google.common.base.Optional; + +import javax.ws.rs.client.WebTarget; + +public class Options { + private Options(){}; + + static WebTarget optionallyAdd(WebTarget input, String key, Optional val) { + return val.isPresent() ? input.queryParam(key, val.get()) : input; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ParamAdder.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ParamAdder.java new file mode 100644 index 0000000..56f97f6 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/ParamAdder.java @@ -0,0 +1,23 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.option; + +import com.google.common.base.Function; + +import javax.ws.rs.client.WebTarget; + +public interface ParamAdder extends Function {} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/QueryOptions.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/QueryOptions.java new file mode 100644 index 0000000..63d201e --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/option/QueryOptions.java @@ -0,0 +1,100 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.option; + +import static com.google.common.base.Preconditions.checkArgument; +import static org.openo.msb.wrapper.consul.option.Options.optionallyAdd; + +import java.math.BigInteger; + +import javax.ws.rs.client.WebTarget; + +import com.google.common.base.Optional; + +/** + * Container for common query options used by the Consul API. + */ + +public abstract class QueryOptions implements ParamAdder { + + public static final QueryOptions BLANK = ImmutableQueryOptions.builder().build(); + + public abstract Optional getWait(); + public abstract Optional getToken(); + public abstract Optional getIndex(); + public abstract Optional getNear(); + + + public ConsistencyMode getConsistencyMode() { + return ConsistencyMode.DEFAULT; + } + + + public boolean isBlocking() { + return getWait().isPresent(); + } + + + public boolean hasToken() { + return getToken().isPresent(); + } + + + void validate() { + if (isBlocking()) { + checkArgument(getIndex().isPresent(), "If wait is specified, index must also be specified"); + } + } + + public static ImmutableQueryOptions.Builder blockSeconds(int seconds, BigInteger index) { + return blockBuilder("s", seconds, index); + } + + public static ImmutableQueryOptions.Builder blockMinutes(int minutes, BigInteger index) { + return blockBuilder("m", minutes, index); + } + + private static ImmutableQueryOptions.Builder blockBuilder(String identifier, int qty, BigInteger index) { + return ImmutableQueryOptions.builder() + .wait(String.format("%s%s", qty, identifier)) + .index(index); + } + + @Override + public WebTarget apply(WebTarget input) { + + WebTarget added = input; + switch (getConsistencyMode()) { + case CONSISTENT: + added = added.queryParam("consistent", ""); + break; + case STALE: + added = added.queryParam("stale", ""); + break; + } + + if (isBlocking()) { + added = added.queryParam("wait", getWait().get()) + .queryParam("index", String.valueOf(getIndex().get())); + } + + added = optionallyAdd(added, "token", getToken()); + added = optionallyAdd(added, "near", getToken()); + + return added; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/Base64EncodingDeserializer.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/Base64EncodingDeserializer.java new file mode 100644 index 0000000..58af732 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/Base64EncodingDeserializer.java @@ -0,0 +1,45 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.util; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.google.common.base.Optional; +import com.google.common.io.BaseEncoding; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; + +/** + * For use with JSON fields that Consul Base 64 encodes. + */ +public class Base64EncodingDeserializer extends JsonDeserializer> { + + /** + * {@inheritDoc} + */ + @Override + public Optional deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getValueAsString(); + + if (StringUtils.isNotEmpty(value)) { + return Optional.of(new String(BaseEncoding.base64().decode(value))); + } + return Optional.absent(); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ClientUtil.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ClientUtil.java new file mode 100644 index 0000000..ab184c1 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ClientUtil.java @@ -0,0 +1,246 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.util; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import javax.ws.rs.ServerErrorException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.client.InvocationCallback; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.openo.msb.wrapper.consul.ConsulException; +import org.openo.msb.wrapper.consul.async.ConsulResponseCallback; +import org.openo.msb.wrapper.consul.model.ConsulResponse; +import org.openo.msb.wrapper.consul.option.CatalogOptions; +import org.openo.msb.wrapper.consul.option.ParamAdder; +import org.openo.msb.wrapper.consul.option.QueryOptions; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +/** + * A collection of stateless utility methods for use in constructing + * requests and responses to the Consul HTTP API. + */ +public class ClientUtil { + + /** + * Applies all key/values from the params map to query string parameters. + * + * @param webTarget The JAX-RS target to apply the query parameters. + * @param params Map of parameters. + * @return The new target with the parameters applied. + */ + public static WebTarget queryParams(WebTarget webTarget, Map params) { + WebTarget target = webTarget; + + if(params != null) { + for(Map.Entry entry : params.entrySet()) { + target = target.queryParam(entry.getKey(), entry.getValue()); + } + } + + return target; + } + + /** + * Given a {@link org.openo.msb.wrapper.consul.option.ParamAdder} object, adds the + * appropriate query string parameters to the request being built. + * + * @param webTarget The base {@link javax.ws.rs.client.WebTarget}. + * @param paramAdder will add specific params to the target. + * @return A {@link javax.ws.rs.client.WebTarget} with all appropriate query + * string parameters. + */ + public static WebTarget addParams(WebTarget webTarget, ParamAdder paramAdder) { + return paramAdder == null ? webTarget : paramAdder.apply(webTarget); + } + + /** + * Generates a {@link org.openo.msb.wrapper.consul.model.ConsulResponse} for a specific datacenter, + * set of {@link org.openo.msb.wrapper.consul.option.QueryOptions}, and a result type. + * + * @param target The base {@link javax.ws.rs.client.WebTarget}. + * @param catalogOptions Catalog specific options to use. + * @param queryOptions The Query Options to use. + * @param type The generic type to marshall the resulting data to. + * @param The result type. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse}. + */ + public static ConsulResponse response(WebTarget target, CatalogOptions catalogOptions, + QueryOptions queryOptions, + GenericType type) { + target = addParams(target, catalogOptions); + target = addParams(target, queryOptions); + + return response(target, type); + } + + /** + * Generates a {@link org.openo.msb.wrapper.consul.model.ConsulResponse} for a specific datacenter, + * set of {@link org.openo.msb.wrapper.consul.option.QueryOptions}, and a result type. + * + * @param target The base {@link javax.ws.rs.client.WebTarget}. + * @param catalogOptions Catalog specific options to use. + * @param queryOptions The Query Options to use. + * @param type The generic type to marshall the resulting data to. + * @param The result type. + */ + public static void response(WebTarget target, CatalogOptions catalogOptions, + QueryOptions queryOptions, + GenericType type, + ConsulResponseCallback callback) { + + target = addParams(target, catalogOptions); + target = addParams(target, queryOptions); + + response(target, type, callback); + } + + /** + * Given a {@link javax.ws.rs.client.WebTarget} object and a type to marshall + * the result JSON into, complete the HTTP GET request. + * + * @param webTarget The JAX-RS target. + * @param responseType The class to marshall the JSON into. + * @param The class to marshall the JSON into. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing the result. + */ + public static ConsulResponse response(WebTarget webTarget, GenericType responseType) { + Response response = webTarget.request().accept(MediaType.APPLICATION_JSON_TYPE).get(); + + return consulResponse(responseType, response); + } + + /** + * Given a {@link javax.ws.rs.client.WebTarget} object and a type to marshall + * the result JSON into, complete the HTTP GET request. + * + * @param webTarget The JAX-RS target. + * @param responseType The class to marshall the JSON into. + * @param callback The callback object to handle the result on a different thread. + * @param The class to marshall the JSON into. + */ + public static void response(WebTarget webTarget, final GenericType responseType, + final ConsulResponseCallback callback) { + webTarget.request().accept(MediaType.APPLICATION_JSON_TYPE).async().get(new InvocationCallback() { + + @Override + public void completed(Response response) { + try { + callback.onComplete(consulResponse(responseType, response)); + } catch (Exception ex) { + callback.onFailure(ex); + } + } + + @Override + public void failed(Throwable throwable) { + callback.onFailure(throwable); + } + }); + } + + /** + * Extracts Consul specific headers and adds them to a {@link org.openo.msb.wrapper.consul.model.ConsulResponse} + * object, which also contains the returned JSON entity. + * + * @param responseType The class to marshall the JSON to. + * @param response The HTTP response. + * @param The class to marshall the JSON to. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} object. + */ + private static ConsulResponse consulResponse(GenericType responseType, Response response) { + handleErrors(response); + + String indexHeaderValue = response.getHeaderString("X-Consul-Index"); + String lastContactHeaderValue = response.getHeaderString("X-Consul-Lastcontact"); + String knownLeaderHeaderValue = response.getHeaderString("X-Consul-Knownleader"); + + BigInteger index = new BigInteger(indexHeaderValue); + long lastContact = lastContactHeaderValue == null ? -1 : Long.valueOf(lastContactHeaderValue); + boolean knownLeader = knownLeaderHeaderValue == null ? false : Boolean.valueOf(knownLeaderHeaderValue); + + ConsulResponse consulResponse = new ConsulResponse(readResponse(response, responseType), lastContact, knownLeader, index); + + response.close(); + + return consulResponse; + } + + /** + * Converts a {@link Response} object to the generic type provided, or an empty + * representation if appropriate + * + * @param response response + * @param responseType response type + * @param + * @return the re + */ + private static T readResponse(Response response, GenericType responseType) { + if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) { + // would be nice I knew a better way to do this + if (responseType.getRawType() == List.class) { + return (T) ImmutableList.of(); + } else if (responseType.getRawType() == Optional.class) { + return (T) Optional.absent(); + } else if(responseType.getRawType() == Map.class) { + return (T) ImmutableMap.of(); + } else { + // Not sure if this case will be reached, but if it is it'll be nice to know + throw new IllegalStateException("Cannot determine empty representation for " + responseType.getRawType()); + } + } + return response.readEntity(responseType); + } + + /** + * Since Consul returns plain text when an error occurs, check for + * unsuccessful HTTP status code, and throw an exception with the text + * from Consul as the message. + * + * @param response The HTTP response. + */ + public static void handleErrors(Response response) { + + if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL + || response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) { + // not an error + return; + } + + try { + final String message = response.hasEntity() ? response.readEntity(String.class) : null; + if (response.getStatusInfo().getFamily() == Response.Status.Family.SERVER_ERROR) { + throw new ServerErrorException(message, response); + } else { + throw new WebApplicationException(message, response); + } + } catch (Exception e) { + throw new ConsulException(e.getLocalizedMessage(), e); + } finally { + response.close(); + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/Jackson.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/Jackson.java new file mode 100644 index 0000000..64feeac --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/Jackson.java @@ -0,0 +1,34 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.guava.GuavaModule; + +public class Jackson { + + public static final ObjectMapper MAPPER = newObjectMapper(); + + private static ObjectMapper newObjectMapper() { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new GuavaModule()); + return mapper; + } + + private Jackson() {} + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ObjectMapperContextResolver.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ObjectMapperContextResolver.java new file mode 100644 index 0000000..c438967 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ObjectMapperContextResolver.java @@ -0,0 +1,35 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.util; + +import javax.ws.rs.ext.ContextResolver; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class ObjectMapperContextResolver implements ContextResolver { + + private final ObjectMapper objectMapper; + public ObjectMapperContextResolver(final ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public ObjectMapper getContext(final Class type) { + return objectMapper; + } + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/SecondsDeserializer.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/SecondsDeserializer.java new file mode 100644 index 0000000..17438f9 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/SecondsDeserializer.java @@ -0,0 +1,43 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.util; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; + +/** + * Deserializes Consul time values with "s" suffix to {@link Long} objects. + */ +public class SecondsDeserializer extends JsonDeserializer { + + @Override + public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + String value = p.getValueAsString(); + + if (StringUtils.isNotEmpty(value)) { + value = value.replaceAll("[a-zA-Z]", ""); + return Long.valueOf(value); + } else { + return null; + } + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/SecondsSerializer.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/SecondsSerializer.java new file mode 100644 index 0000000..1420e18 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/SecondsSerializer.java @@ -0,0 +1,34 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.util; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +/** + * Serializes a time field (e.g. TTL) as seconds. + */ +public class SecondsSerializer extends JsonSerializer { + + @Override + public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(String.format("%ss", value)); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/UnsignedLongDeserializer.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/UnsignedLongDeserializer.java new file mode 100644 index 0000000..802c048 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/UnsignedLongDeserializer.java @@ -0,0 +1,37 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.consul.util; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.google.common.primitives.UnsignedLongs; + +import java.io.IOException; + +/** + * @author sgardner + * @since 2015.02.28 + */ +public class UnsignedLongDeserializer extends JsonDeserializer { + @Override + public Long deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { + String sValue = jp.getValueAsString(); + return UnsignedLongs.decode(sValue); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/serviceListener/IMicroServiceChangeListener.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/serviceListener/IMicroServiceChangeListener.java new file mode 100644 index 0000000..098f71d --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/serviceListener/IMicroServiceChangeListener.java @@ -0,0 +1,33 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.serviceListener; + +import org.openo.msb.api.MicroServiceInfo; +import org.openo.msb.api.Service; + + +public interface IMicroServiceChangeListener { + public void onSave(Service microServiceInfo,String serverPort); + + public void onChange(String serviceName,String version,Service microServiceInfo,String serverPort); + + public void onStatusChange(String serviceName,String url,String version,String protocol,String status); + + public void onDelete(String serviceName, String url,String version,String protocol,String serverPort); + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/serviceListener/MicroServiceChangeListener.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/serviceListener/MicroServiceChangeListener.java new file mode 100644 index 0000000..d41a0d0 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/serviceListener/MicroServiceChangeListener.java @@ -0,0 +1,306 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.serviceListener; + +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.openo.msb.api.ApiRouteInfo; +import org.openo.msb.api.CustomRouteInfo; +import org.openo.msb.api.IuiRouteInfo; +import org.openo.msb.api.Node; +import org.openo.msb.api.RouteServer; +import org.openo.msb.api.Service; +import org.openo.msb.wrapper.ApiRouteServiceWrapper; +import org.openo.msb.wrapper.CustomRouteServiceWrapper; +import org.openo.msb.wrapper.IuiRouteServiceWrapper; +import org.openo.msb.wrapper.util.RegExpTestUtil; +import org.openo.msb.wrapper.util.RouteUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class MicroServiceChangeListener implements IMicroServiceChangeListener { + + private static final Logger LOGGER = LoggerFactory.getLogger(MicroServiceChangeListener.class); + + @Override + public void onSave(Service microServiceInfo,String serverPort) { + + if("UI".equals(microServiceInfo.getProtocol())){ + IuiRouteInfo iuiRouteInfo = this.buildIuiRouteInfo(microServiceInfo); + if(null != iuiRouteInfo){ + IuiRouteServiceWrapper.getInstance().saveIuiRouteInstance(iuiRouteInfo); + } + } + else{ + + if(ifApiRouteUrl(microServiceInfo.getUrl())){ + ApiRouteInfo apiRouteInfo = this.buildApiRouteInfo(microServiceInfo); + if(null != apiRouteInfo){ + ApiRouteServiceWrapper.getInstance().saveApiRouteInstance(apiRouteInfo,serverPort); + } + } + else{ + CustomRouteInfo customRouteInfo = this.buildCustomRouteInfo(microServiceInfo); + if(null != customRouteInfo){ + CustomRouteServiceWrapper.getInstance().saveCustomRouteInstance(customRouteInfo,serverPort); + } + } + } + + + } + + @Override + public void onChange(String serviceName, String version, Service microServiceInfo,String serverPort) { + + if("UI".equals(microServiceInfo.getProtocol())){ + if(serviceName.startsWith("iui_")||serviceName.startsWith("IUI_")){ + serviceName=serviceName.substring(4); + } + IuiRouteInfo iuiRouteInfo = this.buildIuiRouteInfo(microServiceInfo); + if(null != iuiRouteInfo){ + IuiRouteServiceWrapper.getInstance().updateIuiRouteInstance(serviceName, iuiRouteInfo); + } + } + else{ + + if(ifApiRouteUrl(microServiceInfo.getUrl())){ + ApiRouteInfo apiRouteInfo = this.buildApiRouteInfo(microServiceInfo); + if(null != apiRouteInfo){ + ApiRouteServiceWrapper.getInstance().updateApiRouteInstance(serviceName, version, apiRouteInfo,serverPort); + } + } + else{ + if(!serviceName.startsWith("/")){ + serviceName="/"+serviceName; + } + CustomRouteInfo customRouteInfo = this.buildCustomRouteInfo(microServiceInfo); + if(null != customRouteInfo){ + CustomRouteServiceWrapper.getInstance().updateCustomRouteInstance(serviceName,customRouteInfo,serverPort); + } + } + } + + } + + @Override + public void onStatusChange(String serviceName,String url,String version,String protocol,String status) { + if("UI".equals(protocol)){ + + if(serviceName.startsWith("iui_")||serviceName.startsWith("IUI_")){ + serviceName=serviceName.substring(4); + } + IuiRouteServiceWrapper.getInstance().updateIuiRouteStatus(serviceName, status); + + } + else{ + if(ifApiRouteUrl(url)){ + ApiRouteServiceWrapper.getInstance().updateApiRouteStatus(serviceName, version, status); + } + else{ + if(!serviceName.startsWith("/")){ + serviceName="/"+serviceName; + } + CustomRouteServiceWrapper.getInstance().updateCustomRouteStatus(serviceName, status); + } + } + + + + } + + @Override + public void onDelete(String serviceName,String url, String version,String protocol,String serverPort) { + + if("UI".equals(protocol)){ + if(serviceName.startsWith("iui_")||serviceName.startsWith("IUI_")){ + serviceName=serviceName.substring(4); + } + IuiRouteServiceWrapper.getInstance().deleteIuiRoute(serviceName, "*"); + + } + else{ + if(ifApiRouteUrl(url)){ + ApiRouteServiceWrapper.getInstance().deleteApiRoute(serviceName, version, "*",serverPort); + } + else{ + + + if(!serviceName.startsWith("/")){ + serviceName="/"+serviceName; + } + + CustomRouteServiceWrapper.getInstance().deleteCustomRoute(serviceName, "*",serverPort); + } + } + } + + + /** + * @Title ifApiRouteUrl + * @Description TODO(According to judge whether the API registration URL format) + * @param url + * @return + * @return boolean + */ + private boolean ifApiRouteUrl(String url){ + return RegExpTestUtil.apiRouteUrlRegExpTest(url); + } + + + /** + * From MicroServiceInfo to ApiRouteInfo + * @param microServiceInfo + * @return + */ + private ApiRouteInfo buildApiRouteInfo(Service microServiceInfo){ + + ApiRouteInfo apiRouteInfo = new ApiRouteInfo(); + apiRouteInfo.setUrl(microServiceInfo.getUrl()); + + Set nodes=microServiceInfo.getNodes(); + RouteServer[] routeServers=new RouteServer[nodes.size()]; + + + int i=0; + for(Node node:nodes){ + RouteServer routeServer = new RouteServer(node.getIp(),node.getPort()); + routeServers[i]=routeServer; + i++; + } + + + apiRouteInfo.setServers(routeServers); + String[] rangs=StringUtils.split(microServiceInfo.getVisualRange(), "|"); + if(RouteUtil.contain(rangs, "0")){ + apiRouteInfo.setVisualRange("0"); + } + else{ + apiRouteInfo.setVisualRange("1"); + } + + + if("ip_hash".equals(microServiceInfo.getLb_policy())){ + apiRouteInfo.setUseOwnUpstream("1"); + } + + + + apiRouteInfo.setServiceName(microServiceInfo.getServiceName()); + apiRouteInfo.setVersion(microServiceInfo.getVersion()); + //TODO:set json and metrics defaultValue + String version="".equals(microServiceInfo.getVersion())?"":"/"+microServiceInfo.getVersion(); + apiRouteInfo.setApiJson(microServiceInfo.getUrl()+"/swagger.json"); + apiRouteInfo.setMetricsUrl("/admin/metrics"); + return apiRouteInfo; + } + + + /** + * From MicroServiceInfo to CustomRouteInfo + * @param microServiceInfo + * @return + */ + private CustomRouteInfo buildCustomRouteInfo(Service microServiceInfo){ + + CustomRouteInfo customRouteInfo = new CustomRouteInfo(); + customRouteInfo.setUrl(microServiceInfo.getUrl()); + + Set nodes=microServiceInfo.getNodes(); + RouteServer[] routeServers=new RouteServer[nodes.size()]; + + + int i=0; + for(Node node:nodes){ + RouteServer routeServer = new RouteServer(node.getIp(),node.getPort()); + routeServers[i]=routeServer; + i++; + } + + customRouteInfo.setServers(routeServers); + String[] rangs=StringUtils.split(microServiceInfo.getVisualRange(), "|"); + if(RouteUtil.contain(rangs, "0")){ + customRouteInfo.setVisualRange("0"); + } + else{ + customRouteInfo.setVisualRange("1"); + } + + if("ip_hash".equals(microServiceInfo.getLb_policy())){ + customRouteInfo.setUseOwnUpstream("1"); + } + + String serviceName; + if(!microServiceInfo.getServiceName().startsWith("/")){ + serviceName="/"+microServiceInfo.getServiceName(); + } + else{ + serviceName=microServiceInfo.getServiceName(); + } + customRouteInfo.setServiceName(serviceName); + + return customRouteInfo; + } + + + /** + * From MicroServiceInfo to IuiRouteInfo + * @param microServiceInfo + * @return + */ + private IuiRouteInfo buildIuiRouteInfo(Service microServiceInfo){ + + IuiRouteInfo iuiRouteInfo = new IuiRouteInfo(); + iuiRouteInfo.setUrl(microServiceInfo.getUrl()); + + Set nodes=microServiceInfo.getNodes(); + RouteServer[] routeServers=new RouteServer[nodes.size()]; + + + int i=0; + for(Node node:nodes){ + RouteServer routeServer = new RouteServer(node.getIp(),node.getPort()); + routeServers[i]=routeServer; + i++; + } + + iuiRouteInfo.setServers(routeServers); + String[] rangs=StringUtils.split(microServiceInfo.getVisualRange(), "|"); + if(RouteUtil.contain(rangs, "0")){ + iuiRouteInfo.setVisualRange("0"); + } + else{ + iuiRouteInfo.setVisualRange("1"); + } + + if("ip_hash".equals(microServiceInfo.getLb_policy())){ + iuiRouteInfo.setUseOwnUpstream("1"); + } + + String serviceName=microServiceInfo.getServiceName(); + if(serviceName.startsWith("iui_")||serviceName.startsWith("IUI_")){ + serviceName=serviceName.substring(4); + } + + + iuiRouteInfo.setServiceName(serviceName); + + return iuiRouteInfo; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/FileUtil.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/FileUtil.java new file mode 100644 index 0000000..25c0888 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/FileUtil.java @@ -0,0 +1,66 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.util; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; + +public final class FileUtil { + + /** + * Read all the files under a folder + */ + public static File[] readFileFolder(String filepath) throws FileNotFoundException, IOException { + File file = new File(filepath); + if (file.isDirectory()) { + File[] filelist = file.listFiles(); + return filelist; + } + + return null; + } + + public static String readFile(String Path) throws IOException{ + BufferedReader reader = null; + String fileContent = ""; + try { + FileInputStream fileInputStream = new FileInputStream(Path); + InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8"); + reader = new BufferedReader(inputStreamReader); + String tempString = null; + while ((tempString = reader.readLine()) != null) { + fileContent += tempString; + } + reader.close(); + } catch (IOException e) { + throw e; + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + throw e; + } + } + } + return fileContent; + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/JacksonJsonUtil.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/JacksonJsonUtil.java new file mode 100644 index 0000000..531b0d4 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/JacksonJsonUtil.java @@ -0,0 +1,120 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.util; + +import java.util.List; + +import org.openo.msb.api.ApiRouteInfo; +import org.openo.msb.api.RouteServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + + +public class JacksonJsonUtil { + + private static final Logger logger = LoggerFactory.getLogger(JacksonJsonUtil.class); + + private static ObjectMapper mapper; + + + public static synchronized ObjectMapper getMapperInstance() { + if (mapper == null) { + mapper = new ObjectMapper(); + } + return mapper; + } + + /** + * from java object to json + * @param obj + * @return json + * @throws Exception + */ + public static String beanToJson(Object obj) throws Exception { + String json=null; + try { + ObjectMapper objectMapper = getMapperInstance(); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + json =objectMapper.writeValueAsString(obj); + } catch (Exception e) { + logger.error("Class beanToJson faild"); + throw new Exception("Class beanToJson faild"); + } + return json; + } + + + + /** + * from json to java object + * @param json + * @param cls + * @return + * @throws Exception + */ + public static Object jsonToBean(String json, Class cls) throws Exception { + Object vo =null; + try { + ObjectMapper objectMapper = getMapperInstance(); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + vo = objectMapper.readValue(json, cls); + + } catch (Exception e) { + logger.error(cls+" JsonTobean faild"); + throw new Exception(cls+" JsonTobean faild"); + } + return vo; + } + + /** + * from json to java List + * @param json + * @return + * @throws Exception + */ + public static List jsonToListBean(String json) throws Exception { + List vo =null; + try { + + ObjectMapper objectMapper = getMapperInstance(); + + + vo = objectMapper.readValue(json, new TypeReference>() {}); + + } catch (Exception e) { + throw new Exception( "JSON_TO_BEAN_FAILD"); + } + return vo; + } + + public static void main(String[] args) { + RouteServer server=new RouteServer("127.0.0.1","80"); + try { + String json=beanToJson(server); + System.out.println(json); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/JedisUtil.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/JedisUtil.java new file mode 100644 index 0000000..7075249 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/JedisUtil.java @@ -0,0 +1,221 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.util; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + + + +public final class JedisUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(JedisUtil.class); + private static String host = "127.0.0.1"; + private static int port = 6379; + private static int connectionTimeout = 2000; + private static int DEFAULT_DB_INDEX = 0; + + private static JedisPool jedisPool = null; + + public static String serverIp="127.0.0.1"; + + public static int serverPort=10080; + + public static String propertiesName="redis.properties"; + + public static String propertiesPath=""; + + + +public static void main(String[] args) { + +} + + private JedisUtil() { + // private constructor + + } + + private static void initialPool() { + try { + JedisPoolConfig config = new JedisPoolConfig(); + +// String pathtest=JedisUtil.class.getResource("").getPath(); +// String path ="/"+ pathtest.substring(0, pathtest.indexOf("assembly")).replace("file:/", "") +"assembly/"+defaultWorkspace; + + File propertiesFile = new File(propertiesPath); + + if (propertiesFile.exists()) { + + + BufferedInputStream inputStream =new BufferedInputStream(new FileInputStream(propertiesPath)); + ResourceBundle bundle =new PropertyResourceBundle(inputStream); + + if (bundle == null) { + throw new IllegalArgumentException( + "[redis.properties] is not found!"); + } + + + // 设置连接池基本信息 + String strHost = bundle.getString("redis.host"); + if(StringUtils.isNotEmpty(strHost)){ + host = strHost; + } + String strPort = bundle.getString("redis.port"); + if(StringUtils.isNotEmpty(strPort)){ + port = Integer.valueOf(strPort); + } + + + String strTimeout = bundle.getString("redis.connectionTimeout"); + if (StringUtils.isNotEmpty(strTimeout) ){ + connectionTimeout = Integer.valueOf(strTimeout); + } + +// serverIp=bundle.getString("server.ip"); + serverPort=Integer.valueOf(bundle.getString("server.port")); + + String strDbIndex = bundle.getString("redis.db_index"); + if (StringUtils.isNotEmpty(strDbIndex)) { + DEFAULT_DB_INDEX = Integer.valueOf(strDbIndex); + } + + String strMaxTotal = bundle.getString("redis.pool.maxTotal"); + if (StringUtils.isNotEmpty(strMaxTotal)) { + config.setMaxTotal(Integer.valueOf(strMaxTotal)); + } + + String strMaxIdle = bundle.getString("redis.pool.maxIdle"); + if (StringUtils.isNotEmpty(strMaxIdle)) { + config.setMaxIdle(Integer.valueOf(strMaxIdle)); + } + + String strMaxWaitMillis = bundle.getString("redis.pool.maxWaitMillis"); + if (StringUtils.isNotEmpty(strMaxWaitMillis)) { + config.setMaxWaitMillis(Long.valueOf(strMaxWaitMillis)); + } + + String strTestOnBorrow = bundle + .getString("redis.pool.testOnBorrow"); + if (StringUtils.isNotEmpty(strTestOnBorrow)) { + config.setTestOnBorrow(Boolean.valueOf(strTestOnBorrow)); + } + + String strTestOnReturn = bundle + .getString("redis.pool.testOnReturn"); + if (StringUtils.isNotEmpty(strTestOnReturn)) { + config.setTestOnReturn(Boolean.valueOf(strTestOnReturn)); + } + + } + + LOGGER.info("Redis server info: " + host + ":" + port); + LOGGER.info("nginx server info: " + serverIp + ":" + serverPort); + + +// ResourceBundle bundle = ResourceBundle.getBundle("conf.redis"); + + jedisPool = new JedisPool(config, host, port, connectionTimeout); + } catch (Exception e) { + LOGGER.error("Initiate Jedis pool failed!", e); + } + } + /** + * From the connection pool to obtain jedis instance, use the default database index number 0 + * @return + */ + public synchronized static Jedis borrowJedisInstance() { + if (jedisPool == null) { + initialPool(); + } + try { + if (jedisPool != null) { + Jedis resource = jedisPool.getResource(); + resource.select(DEFAULT_DB_INDEX); + return resource; + } else { + return null; + } + } catch (Exception e) { + LOGGER.error("Get Jedis from pool failed!", e); + return null; + } + } + /** + * From the connection pool to obtain jedis instance, using the specified database index number + * @return + */ + public synchronized static Jedis borrowJedisInstance(final int dbIndex) { + if (jedisPool == null) { + initialPool(); + } + try { + if (jedisPool != null) { + Jedis resource = jedisPool.getResource(); + resource.select(dbIndex); + return resource; + } else { + return null; + } + } catch (Exception e) { + LOGGER.error("Get Jedis from pool failed!", e); + return null; + } + } + + /** + * returned to the pool jedis instance + * @param jedis + */ + public static void returnJedisInstance(final Jedis jedis) { + if (jedis != null) { + jedis.close(); + } + } + + + /** + * @Title getJedis + * @Description TODO(From the connection pool to obtain jedis instance) + * @throws Exception + * @return Jedis + */ + public static Jedis getJedis() throws Exception{ + + + Jedis jedis = borrowJedisInstance(); + if (jedis == null) { + throw new Exception("fetch from jedis pool failed,null object!"); + + } + + return jedis; + + } + +} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MetricsUtil.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MetricsUtil.java new file mode 100644 index 0000000..b07737b --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MetricsUtil.java @@ -0,0 +1,24 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.util; + +public class MetricsUtil { + + public static String adminContextPath = "http://127.0.0.1:8086/admin/metrics"; + + public static final int SC_OK = 200; +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MicroServiceDB.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MicroServiceDB.java new file mode 100644 index 0000000..ed22745 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MicroServiceDB.java @@ -0,0 +1,423 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.util; + +import java.sql.Date; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.openo.msb.api.MicroServiceFullInfo; +import org.openo.msb.api.MicroServiceInfo; +import org.openo.msb.api.Node; +import org.openo.msb.api.NodeInfo; +import org.openo.msb.api.Service; +import org.openo.msb.wrapper.serviceListener.IMicroServiceChangeListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.Jedis; + +public class MicroServiceDB { + + private static final Logger LOGGER = LoggerFactory.getLogger(MicroServiceDB.class); + + private static MicroServiceDB instance = new MicroServiceDB(); + + private List serviceListenerlist = + new ArrayList(); + + private MicroServiceDB() {} + + public static MicroServiceDB getInstance() { + return instance; + } + + + public void addServiceChangeListener(IMicroServiceChangeListener listener) { + synchronized (serviceListenerlist) { + serviceListenerlist.add(listener); + } + } + + + public void removeServiceChangeListener(IMicroServiceChangeListener listener) { + synchronized (serviceListenerlist) { + serviceListenerlist.remove(listener); + } + } + + + public MicroServiceFullInfo[] getAllMicroServiceInstances() throws Exception { + Jedis jedis = null; + MicroServiceFullInfo[] microServiceList; + try { + jedis = JedisUtil.getJedis(); + + String routekey = + MicroServiceUtil.getPrefixedKey("","*", MicroServiceUtil.SUFFIX_PATH_INFO); + Set serviceSet = jedis.keys(routekey); + microServiceList = new MicroServiceFullInfo[serviceSet.size()]; + + Pattern redisKeyPattern = MicroServiceUtil.getRedisKeyPattern(); + int i = 0; + for (String servicePath : serviceSet) { + Matcher matcher = redisKeyPattern.matcher(servicePath); + if (matcher.matches()) { + microServiceList[i] = getMicroServiceByJedis(jedis, matcher.group("servicename"),matcher.group("version"), ""); + i++; + } + } + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new Exception("call redis throw exception:"+e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + return microServiceList; + } + + public void saveMicroServiceInfo2Redis(MicroServiceInfo microServiceInfo,String serverPort) throws Exception { + // 1.1 set info + String serviceInfokey = + MicroServiceUtil.getServiceInfoKey(serverPort,microServiceInfo.getServiceName(), + microServiceInfo.getVersion()); + Map serviceInfoMap = new HashMap(); + serviceInfoMap.put("url", microServiceInfo.getUrl()); + serviceInfoMap.put("protocol", microServiceInfo.getProtocol()); + serviceInfoMap.put("visualRange",microServiceInfo.getVisualRange()); + serviceInfoMap.put("lb_policy",microServiceInfo.getLb_policy()); + serviceInfoMap.put("status", "0"); + + + + // 1.2 set lb info + String serviceLBkey = + MicroServiceUtil.getPrefixedKey(serverPort,microServiceInfo.getServiceName(), + microServiceInfo.getVersion(), MicroServiceUtil.ROUTE_PATH_LOADBALANCE); + + + Jedis jedis = null; + try { + jedis = JedisUtil.getJedis(); + // 2.1 save info + jedis.hmset(serviceInfokey, serviceInfoMap); + + + for(Node node:microServiceInfo.getNodes()){ + + String key=serviceLBkey+":"+node.getIp()+"-"+node.getPort(); + + Map nodeMap = new HashMap(); + + nodeMap.put("ip", node.getIp()); + nodeMap.put("port", node.getPort()); + nodeMap.put("ttl", Integer.toString(node.getTtl())); + long expiration_time=System.currentTimeMillis()+node.getTtl()*1000; + nodeMap.put("expiration", Long.toString(expiration_time)); + + if(jedis.keys(key).isEmpty()){ + nodeMap.put("created_at", Long.toString(System.currentTimeMillis())); + } +// else{ +// Map nodeLBmap = jedis.hgetAll(key); +// nodeMap.put("created_at", nodeLBmap.get("created_at")); +// } + nodeMap.put("updated_at", Long.toString(System.currentTimeMillis())); + + jedis.hmset(key, nodeMap); + } + +// jedis.sadd(serviceLBkey, nodeArray); + + } catch (Exception e) { + LOGGER.error("save to redis throw exception", e); + throw new Exception("save to redis throw exception:"+e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + + + } + + public void updateMicroServiceStatus(String serviceName, String version,String status) throws Exception{ + + + String serviceInfokey = MicroServiceUtil.getServiceInfoKey("",serviceName, version); + Map serviceInfoMap = new HashMap(); + serviceInfoMap.put("status", status); + + + Jedis jedis = null; + try { + jedis = JedisUtil.borrowJedisInstance(); + if (jedis == null) { + throw new Exception("fetch from jedis pool failed,null object!"); + } + jedis.hmset(serviceInfokey, serviceInfoMap); + } + catch (Exception e) { + LOGGER.error("update MicroService status throw exception", e); + throw new Exception("update MicroService status throw exception:"+e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + } + + + public void updateMicroServiceNode2Redis(String serviceName, String version,String ip,String port,int ttl) throws Exception { + String serviceLBkey = + MicroServiceUtil.getPrefixedKey("",serviceName,version, MicroServiceUtil.ROUTE_PATH_LOADBALANCE); + + + Jedis jedis = null; + try { + jedis = JedisUtil.getJedis(); + + + String nodeKey=serviceLBkey+":"+ip+"-"+port; + Map nodeLBmap = jedis.hgetAll(nodeKey); + + if(nodeLBmap.isEmpty()){ + throw new NullPointerException(" MicroService Node not fond "); + } + + + nodeLBmap.put("ttl", Integer.toString(ttl)); + long expiration_time=System.currentTimeMillis()+ttl*1000; + nodeLBmap.put("expiration", Long.toString(expiration_time)); + nodeLBmap.put("updated_at", Long.toString(System.currentTimeMillis())); + + jedis.hmset(nodeKey, nodeLBmap); + + + } + catch (NullPointerException e){ + throw e; + } + catch (Exception e) { + LOGGER.error("update MicroService Node throw exception", e); + throw new Exception("update MicroService Node throw exception:"+e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + } + + + public void noticeUpdateApiListener(String serviceName,String version,Service microServiceInfo,String serverPort) { + if (isNeedNotify(microServiceInfo)) { + for (IMicroServiceChangeListener serviceListener : serviceListenerlist) { + serviceListener.onChange(serviceName,version, microServiceInfo,serverPort); + } + } + + } + + public void noticeUpdateStatusListener(Service microServiceInfo,String status) { + + for (IMicroServiceChangeListener serviceListener : serviceListenerlist) { + serviceListener.onStatusChange(microServiceInfo.getServiceName(),microServiceInfo.getUrl(), + microServiceInfo.getVersion(),microServiceInfo.getProtocol(),status); + } + } + + + + public void noticeApiListener(Service microServiceInfo, String type,String serverPort) { + if (isNeedNotify(microServiceInfo)) { + + if ("ADD".equals(type)) { + for (IMicroServiceChangeListener serviceListener : serviceListenerlist) { + serviceListener.onSave(microServiceInfo,serverPort); + } + } else if ("DELETE".equals(type)) { + for (IMicroServiceChangeListener serviceListener : serviceListenerlist) { + serviceListener.onDelete(microServiceInfo.getServiceName(),microServiceInfo.getUrl(), + microServiceInfo.getVersion(),microServiceInfo.getProtocol(),serverPort); + } + } + + } + } + + + public MicroServiceFullInfo getMicroServiceInstance(String serviceName, String version,String serverPort) + throws Exception { + if (null == version || "null".equals(version)) { + version = ""; + } + + Jedis jedis = null; + MicroServiceFullInfo microServiceInfo = null; + + try { + jedis = JedisUtil.getJedis(); + + microServiceInfo= getMicroServiceByJedis(jedis,serviceName,version, serverPort); + + + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new Exception("call redis throw exception:"+e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + return microServiceInfo; + + } + + private MicroServiceFullInfo getMicroServiceByJedis(Jedis jedis,String serviceName, String version,String serverPort){ + MicroServiceFullInfo microServiceInfo = null; + String serviceInfoKey = MicroServiceUtil.getServiceInfoKey(serverPort,serviceName, version); + Map infomap = jedis.hgetAll(serviceInfoKey); + if (!infomap.isEmpty()) { + microServiceInfo = new MicroServiceFullInfo(); + microServiceInfo.setServiceName(serviceName); + microServiceInfo.setVersion(version); + microServiceInfo.setUrl(infomap.get("url")); + microServiceInfo.setProtocol(infomap.get("protocol")); + microServiceInfo.setVisualRange(infomap.get("visualRange")); + microServiceInfo.setStatus(infomap.get("status")); + microServiceInfo.setLb_policy(infomap.get("lb_policy")); + + String nodeLBkey = + MicroServiceUtil.getPrefixedKey(serverPort,microServiceInfo.getServiceName(), + microServiceInfo.getVersion(), + MicroServiceUtil.ROUTE_PATH_LOADBALANCE); + + Set nodeKeys=jedis.keys(nodeLBkey+":*"); + + Set nodes=new HashSet(); + for(String nodeKey:nodeKeys){ + Map nodeLBmap = jedis.hgetAll(nodeKey); + NodeInfo nodeInfo=new NodeInfo(); + nodeInfo.setNodeId(serviceName+"_"+nodeLBmap.get("ip")+"_"+nodeLBmap.get("port")); + nodeInfo.setIp(nodeLBmap.get("ip")); + nodeInfo.setPort(nodeLBmap.get("port")); + nodeInfo.setTtl(Integer.parseInt(nodeLBmap.get("ttl"))); + nodeInfo.setCreated_at(new Date(Long.parseLong(nodeLBmap.get("created_at")))); + nodeInfo.setUpdated_at(new Date(Long.parseLong(nodeLBmap.get("updated_at")))); + nodeInfo.setExpiration(new Date(Long.parseLong(nodeLBmap.get("expiration")))); + + nodes.add(nodeInfo); + } + + microServiceInfo.setNodes(nodes); + } + + + + + return microServiceInfo; + } + + + public void deleteMicroService(String serviceName, String version,String serverPort) throws Exception { + if (null == version || "null".equals(version)) { + version = ""; + } + + Jedis jedis = null; + try { + jedis = JedisUtil.getJedis(); + String routekey = MicroServiceUtil.getPrefixedKey(serverPort,serviceName, version, "*"); + Set infoSet = jedis.keys(routekey); + + String[] paths = new String[infoSet.size()]; + + infoSet.toArray(paths); + + jedis.del(paths); + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new Exception("call redis throw exception:"+e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + } + + public void deleteNode(String serviceName, String version, String ip,String port) throws Exception { + if (null == version || "null".equals(version)) { + version = ""; + } + + Jedis jedis = null; + try { + jedis = JedisUtil.getJedis(); + String serviceLBkey = + MicroServiceUtil.getPrefixedKey("",serviceName, version, + MicroServiceUtil.ROUTE_PATH_LOADBALANCE,ip+"-"+port); + jedis.del(serviceLBkey); + } catch (Exception e) { + LOGGER.error("call redis throw exception", e); + throw new Exception("call redis throw exception:"+e.getMessage()); + } finally { + JedisUtil.returnJedisInstance(jedis); + } + + } + + + /** + * Determine whether the service needs to send a notification + * TODO: filter according to the agreement, + * the only notice of agreement for REST \ UI interface MSB - REST + * @param protocol + * @return + */ + private boolean isNeedNotifyByProtocol(String protocol) { + return "UI".equalsIgnoreCase(protocol) ||("REST".equalsIgnoreCase(protocol)); + } + + /** + * Determine whether the service needs to send a notification + * TODO: according to the visual range filter conditions + * @param visualRange + * @return + */ + private boolean isNeedNotifyByVisualRange(String visualRange) { + String[] rangeArray=StringUtils.split(visualRange, "|"); + return RouteUtil.contain(RouteUtil.visualRangeMatches, rangeArray); + } + + /** + * According to the MicroServiceInfo entity information to judge whether need to send a notification + * @param microServiceInfo + * @return + */ + private boolean isNeedNotify(Service microServiceInfo) { + if (null != microServiceInfo) { + return isNeedNotifyByProtocol(microServiceInfo.getProtocol()) && + isNeedNotifyByVisualRange(microServiceInfo.getVisualRange()); + } else { + return false; + } + } + + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MicroServiceUtil.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MicroServiceUtil.java new file mode 100644 index 0000000..67fd60f --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/MicroServiceUtil.java @@ -0,0 +1,96 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.util; + +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; + + +public class MicroServiceUtil { + public static final String PREFIX_PATH = "discover:microservices"; + + public static final String PREFIX_PATH_PORT = "discover:"; + + public static final String SUFFIX_PATH_INFO = "info"; + + public static final String REDIS_KEY_PATTERN = + "discover:microservices:(?[^:]+)(:(?[^:]*))?:info"; + + public static final String REQUEST_SUCCESS = "SUCCESS"; + + public static final String REQUEST_FAIL = "FAIL"; + + public static final String ROUTE_PATH_LOADBALANCE = "lb"; // 负载均衡路径名 + + + public static String getPrefixedKey(String... paths) { + StringBuffer sb = new StringBuffer(); + + if(paths[0].trim().equals("") || paths[0].equals(String.valueOf(JedisUtil.serverPort))){ + sb.append(PREFIX_PATH); + } + else{ + sb.append(PREFIX_PATH_PORT).append(paths[0]); + } + + for (int i = 1; i < paths.length; i++) { + sb.append(":"); + sb.append(paths[i]); + } + return sb.toString(); + } + + + public static String getServiceInfoKey(String serverPort,String serviceName, String version) { + return getPrefixedKey(serverPort,serviceName, version, SUFFIX_PATH_INFO); + } + + + + + + public static Pattern getRedisKeyPattern() { + return Pattern.compile(REDIS_KEY_PATTERN); + } + + public static String getRealIp(HttpServletRequest request) { + String ip = request.getHeader("X-Forwarded-For"); + if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) { + // After the reverse proxy can have multiple IP value for many times, the first IP is the real IP + int index = ip.indexOf(","); + if (index != -1) { + return ip.substring(0, index); + } else { + return ip; + } + } + ip = request.getHeader("X-Real-IP"); + + if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) { + return ip; + } + + + return request.getRemoteAddr(); + + } + + +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/RegExpTestUtil.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/RegExpTestUtil.java new file mode 100644 index 0000000..88e83b0 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/RegExpTestUtil.java @@ -0,0 +1,88 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.util; + +import java.util.regex.Pattern; + +public class RegExpTestUtil { + + + + public static boolean hostRegExpTest(String host){ + + String hostReg = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\." + +"(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + +"(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + +"(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)" + +":(\\d{1,5})$"; + return Pattern.matches(hostReg, host); + + } + + public static boolean ipRegExpTest(String ip){ + + String hostReg = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\." + +"(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + +"(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + +"(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$"; + return Pattern.matches(hostReg, ip); + + } + + public static boolean portRegExpTest(String port){ + + String hostReg = "^\\d{1,5}$"; + return Pattern.matches(hostReg, port); + + } + +public static boolean versionRegExpTest(String version){ + + String versionReg = "^v\\d+(\\.\\d+)?$"; + return Pattern.matches(versionReg, version); + + } + +public static boolean urlRegExpTest(String url){ + if(url.equals("/")) return true; + + String urlReg = "^\\/.*((?!\\/).)$"; + return Pattern.matches(urlReg, url); + +} + +public static boolean apiRouteUrlRegExpTest(String url){ + + String urlReg = "^\\/"+RouteUtil.API_ROOT_PATH+"\\/.*$"; + return Pattern.matches(urlReg, url); + +} + +public static boolean iuiRouteUrlRegExpTest(String url){ + + String urlReg = "^\\/"+RouteUtil.IUI_ROOT_PATH+"\\/.*$"; + return Pattern.matches(urlReg, url); + +} + + + + + public static void main(String[] args) { + System.out.println(urlRegExpTest("/api ")); +// System.out.println("api".startsWith("/")); + } +} diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/RouteUtil.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/RouteUtil.java new file mode 100644 index 0000000..5f60bd8 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/util/RouteUtil.java @@ -0,0 +1,135 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* Licensed 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 org.openo.msb.wrapper.util; + +import org.apache.commons.lang3.StringUtils; +import org.openo.msb.api.DiscoverInfo; + + +public class RouteUtil { + + public static String IUI_ROOT_PATH="iui"; + + public static String API_ROOT_PATH="api"; + + public static final String ROUTE_PATH="msb:routing"; + + public static final String APIROUTE="api"; + + public static final String IUIROUTE="iui"; + + public static final String CUSTOMROUTE="custom"; + + public static final String P2PROUTE="p2p"; + + + public static final String ROUTE_PATH_INFO="info"; + + public static final String ROUTE_PATH_LOADBALANCE="lb"; + + public static final String APIROUTE_PATH_LIFE="life"; + + + public static final String REQUEST_SUCCESS = "SUCCESS"; + + public static final String REQUEST_FAIL = "FAIL"; + + public static String PROTOCOL_LIST="REST,UI,MQ,FTP,SNMP,TCP,UDP"; + + public static DiscoverInfo discoverInfo=new DiscoverInfo(); + + + public static String[] visualRangeRange={"0","1"}; + + public static String[] controlRangeMatches={"0","1","2"}; + + public static String[] statusRangeMatches={"0","1"}; + + public static String[] useOwnUpstreamRangeMatches={"0","1"}; + + public static String[] visualRangeMatches={"1"}; + + /** + * @Title: getPrefixedKey + * @Description: TODO(Add base path prefix radis assembly path) + * @param: @param serviceName + * @param: @param version + * @param: @param type + * @param: @return + * @return: String + */ + + public static String getPrefixedKey(String...paths){ + StringBuffer sb= new StringBuffer(); + + if(paths[0].trim().equals("") || paths[0].equals(String.valueOf(JedisUtil.serverPort))){ + sb.append(ROUTE_PATH); + } + else{ + sb.append(paths[0]); + } + + for (int i = 1; i < paths.length; i++) { + sb.append(":"); + sb.append(paths[i]); + } + return sb.toString(); + } + + + public static Object[] concat(Object[] a, Object[] b) { + Object[] c= new Object[a.length+b.length]; + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + return c; + } + + public static boolean contain(String[] array,String str){ + for(int i=0;i + + + + + ZENAP API-DOC. + + ZENAP API-DOC + + + + + index.html + index.xhtml + index.htm + index.jsp + + + + diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/css/reset.css b/apiroute/apiroute-service/src/main/resources/api-doc/css/reset.css new file mode 100644 index 0000000..b2b0789 --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/api-doc/css/reset.css @@ -0,0 +1,125 @@ +/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */ +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} +body { + line-height: 1; +} +ol, +ul { + list-style: none; +} +blockquote, +q { + quotes: none; +} +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/css/screen.css b/apiroute/apiroute-service/src/main/resources/api-doc/css/screen.css new file mode 100644 index 0000000..32b199b --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/api-doc/css/screen.css @@ -0,0 +1,1256 @@ +/* Original style from softwaremaniacs.org (c) Ivan Sagalaev */ +.swagger-section pre code { + display: block; + padding: 0.5em; + background: #F0F0F0; +} +.swagger-section pre code, +.swagger-section pre .subst, +.swagger-section pre .tag .title, +.swagger-section pre .lisp .title, +.swagger-section pre .clojure .built_in, +.swagger-section pre .nginx .title { + color: black; +} +.swagger-section pre .string, +.swagger-section pre .title, +.swagger-section pre .constant, +.swagger-section pre .parent, +.swagger-section pre .tag .value, +.swagger-section pre .rules .value, +.swagger-section pre .rules .value .number, +.swagger-section pre .preprocessor, +.swagger-section pre .ruby .symbol, +.swagger-section pre .ruby .symbol .string, +.swagger-section pre .aggregate, +.swagger-section pre .template_tag, +.swagger-section pre .django .variable, +.swagger-section pre .smalltalk .class, +.swagger-section pre .addition, +.swagger-section pre .flow, +.swagger-section pre .stream, +.swagger-section pre .bash .variable, +.swagger-section pre .apache .tag, +.swagger-section pre .apache .cbracket, +.swagger-section pre .tex .command, +.swagger-section pre .tex .special, +.swagger-section pre .erlang_repl .function_or_atom, +.swagger-section pre .markdown .header { + color: #800; +} +.swagger-section pre .comment, +.swagger-section pre .annotation, +.swagger-section pre .template_comment, +.swagger-section pre .diff .header, +.swagger-section pre .chunk, +.swagger-section pre .markdown .blockquote { + color: #888; +} +.swagger-section pre .number, +.swagger-section pre .date, +.swagger-section pre .regexp, +.swagger-section pre .literal, +.swagger-section pre .smalltalk .symbol, +.swagger-section pre .smalltalk .char, +.swagger-section pre .go .constant, +.swagger-section pre .change, +.swagger-section pre .markdown .bullet, +.swagger-section pre .markdown .link_url { + color: #080; +} +.swagger-section pre .label, +.swagger-section pre .javadoc, +.swagger-section pre .ruby .string, +.swagger-section pre .decorator, +.swagger-section pre .filter .argument, +.swagger-section pre .localvars, +.swagger-section pre .array, +.swagger-section pre .attr_selector, +.swagger-section pre .important, +.swagger-section pre .pseudo, +.swagger-section pre .pi, +.swagger-section pre .doctype, +.swagger-section pre .deletion, +.swagger-section pre .envvar, +.swagger-section pre .shebang, +.swagger-section pre .apache .sqbracket, +.swagger-section pre .nginx .built_in, +.swagger-section pre .tex .formula, +.swagger-section pre .erlang_repl .reserved, +.swagger-section pre .prompt, +.swagger-section pre .markdown .link_label, +.swagger-section pre .vhdl .attribute, +.swagger-section pre .clojure .attribute, +.swagger-section pre .coffeescript .property { + color: #8888ff; +} +.swagger-section pre .keyword, +.swagger-section pre .id, +.swagger-section pre .phpdoc, +.swagger-section pre .title, +.swagger-section pre .built_in, +.swagger-section pre .aggregate, +.swagger-section pre .css .tag, +.swagger-section pre .javadoctag, +.swagger-section pre .phpdoc, +.swagger-section pre .yardoctag, +.swagger-section pre .smalltalk .class, +.swagger-section pre .winutils, +.swagger-section pre .bash .variable, +.swagger-section pre .apache .tag, +.swagger-section pre .go .typename, +.swagger-section pre .tex .command, +.swagger-section pre .markdown .strong, +.swagger-section pre .request, +.swagger-section pre .status { + font-weight: bold; +} +.swagger-section pre .markdown .emphasis { + font-style: italic; +} +.swagger-section pre .nginx .built_in { + font-weight: normal; +} +.swagger-section pre .coffeescript .javascript, +.swagger-section pre .javascript .xml, +.swagger-section pre .tex .formula, +.swagger-section pre .xml .javascript, +.swagger-section pre .xml .vbscript, +.swagger-section pre .xml .css, +.swagger-section pre .xml .cdata { + opacity: 0.5; +} +.swagger-section .swagger-ui-wrap { + line-height: 1; + font-family: "Droid Sans", sans-serif; + max-width: 960px; + margin-left: auto; + margin-right: auto; +} +.swagger-section .swagger-ui-wrap b, +.swagger-section .swagger-ui-wrap strong { + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap q, +.swagger-section .swagger-ui-wrap blockquote { + quotes: none; +} +.swagger-section .swagger-ui-wrap p { + line-height: 1.4em; + padding: 0 0 10px; + color: #333333; +} +.swagger-section .swagger-ui-wrap q:before, +.swagger-section .swagger-ui-wrap q:after, +.swagger-section .swagger-ui-wrap blockquote:before, +.swagger-section .swagger-ui-wrap blockquote:after { + content: none; +} +.swagger-section .swagger-ui-wrap .heading_with_menu h1, +.swagger-section .swagger-ui-wrap .heading_with_menu h2, +.swagger-section .swagger-ui-wrap .heading_with_menu h3, +.swagger-section .swagger-ui-wrap .heading_with_menu h4, +.swagger-section .swagger-ui-wrap .heading_with_menu h5, +.swagger-section .swagger-ui-wrap .heading_with_menu h6 { + display: block; + clear: none; + float: left; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + width: 60%; +} +.swagger-section .swagger-ui-wrap table { + border-collapse: collapse; + border-spacing: 0; +} +.swagger-section .swagger-ui-wrap table thead tr th { + padding: 5px; + font-size: 0.9em; + color: #666666; + border-bottom: 1px solid #999999; +} +.swagger-section .swagger-ui-wrap table tbody tr:last-child td { + border-bottom: none; +} +.swagger-section .swagger-ui-wrap table tbody tr.offset { + background-color: #f0f0f0; +} +.swagger-section .swagger-ui-wrap table tbody tr td { + padding: 6px; + font-size: 0.9em; + border-bottom: 1px solid #cccccc; + vertical-align: top; + line-height: 1.3em; +} +.swagger-section .swagger-ui-wrap ol { + margin: 0px 0 10px; + padding: 0 0 0 18px; + list-style-type: decimal; +} +.swagger-section .swagger-ui-wrap ol li { + padding: 5px 0px; + font-size: 0.9em; + color: #333333; +} +.swagger-section .swagger-ui-wrap ol, +.swagger-section .swagger-ui-wrap ul { + list-style: none; +} +.swagger-section .swagger-ui-wrap h1 a, +.swagger-section .swagger-ui-wrap h2 a, +.swagger-section .swagger-ui-wrap h3 a, +.swagger-section .swagger-ui-wrap h4 a, +.swagger-section .swagger-ui-wrap h5 a, +.swagger-section .swagger-ui-wrap h6 a { + text-decoration: none; +} +.swagger-section .swagger-ui-wrap h1 a:hover, +.swagger-section .swagger-ui-wrap h2 a:hover, +.swagger-section .swagger-ui-wrap h3 a:hover, +.swagger-section .swagger-ui-wrap h4 a:hover, +.swagger-section .swagger-ui-wrap h5 a:hover, +.swagger-section .swagger-ui-wrap h6 a:hover { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap h1 span.divider, +.swagger-section .swagger-ui-wrap h2 span.divider, +.swagger-section .swagger-ui-wrap h3 span.divider, +.swagger-section .swagger-ui-wrap h4 span.divider, +.swagger-section .swagger-ui-wrap h5 span.divider, +.swagger-section .swagger-ui-wrap h6 span.divider { + color: #aaaaaa; +} +.swagger-section .swagger-ui-wrap a { + color: #547f00; +} +.swagger-section .swagger-ui-wrap a img { + border: none; +} +.swagger-section .swagger-ui-wrap article, +.swagger-section .swagger-ui-wrap aside, +.swagger-section .swagger-ui-wrap details, +.swagger-section .swagger-ui-wrap figcaption, +.swagger-section .swagger-ui-wrap figure, +.swagger-section .swagger-ui-wrap footer, +.swagger-section .swagger-ui-wrap header, +.swagger-section .swagger-ui-wrap hgroup, +.swagger-section .swagger-ui-wrap menu, +.swagger-section .swagger-ui-wrap nav, +.swagger-section .swagger-ui-wrap section, +.swagger-section .swagger-ui-wrap summary { + display: block; +} +.swagger-section .swagger-ui-wrap pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #fcf6db; + border: 1px solid #e5e0c6; + padding: 10px; +} +.swagger-section .swagger-ui-wrap pre code { + line-height: 1.6em; + background: none; +} +.swagger-section .swagger-ui-wrap .content > .content-type > div > label { + clear: both; + display: block; + color: #0F6AB4; + font-size: 1.1em; + margin: 0; + padding: 15px 0 5px; +} +.swagger-section .swagger-ui-wrap .content pre { + font-size: 12px; + margin-top: 5px; + padding: 5px; +} +.swagger-section .swagger-ui-wrap .icon-btn { + cursor: pointer; +} +.swagger-section .swagger-ui-wrap .info_title { + padding-bottom: 10px; + font-weight: bold; + font-size: 25px; +} +.swagger-section .swagger-ui-wrap p.big, +.swagger-section .swagger-ui-wrap div.big p { + font-size: 1em; + margin-bottom: 10px; +} +.swagger-section .swagger-ui-wrap form.fullwidth ol li.string input, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.url input, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.text textarea, +.swagger-section .swagger-ui-wrap form.fullwidth ol li.numeric input { + width: 500px !important; +} +.swagger-section .swagger-ui-wrap .info_license { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_tos { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .message-fail { + color: #cc0000; +} +.swagger-section .swagger-ui-wrap .info_url { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_email { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_name { + padding-bottom: 5px; +} +.swagger-section .swagger-ui-wrap .info_description { + padding-bottom: 10px; + font-size: 15px; +} +.swagger-section .swagger-ui-wrap .markdown ol li, +.swagger-section .swagger-ui-wrap .markdown ul li { + padding: 3px 0px; + line-height: 1.4em; + color: #333333; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input { + display: block; + padding: 4px; + width: auto; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input.title, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input.title, +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input.title { + font-size: 1.3em; +} +.swagger-section .swagger-ui-wrap table.fullwidth { + width: 100%; +} +.swagger-section .swagger-ui-wrap .model-signature { + font-family: "Droid Sans", sans-serif; + font-size: 1em; + line-height: 1.5em; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav a { + text-decoration: none; + color: #AAA; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav a:hover { + text-decoration: underline; + color: black; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-nav .selected { + color: black; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap .model-signature .propType { + color: #5555aa; +} +.swagger-section .swagger-ui-wrap .model-signature pre:hover { + background-color: #ffffdd; +} +.swagger-section .swagger-ui-wrap .model-signature pre { + font-size: .85em; + line-height: 1.2em; + overflow: auto; + max-height: 200px; + cursor: pointer; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav { + display: block; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li:last-child { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li { + float: left; + margin: 0 5px 5px 0; + padding: 2px 5px 2px 0; + border-right: 1px solid #ddd; +} +.swagger-section .swagger-ui-wrap .model-signature .propOpt { + color: #555; +} +.swagger-section .swagger-ui-wrap .model-signature .snippet small { + font-size: 0.75em; +} +.swagger-section .swagger-ui-wrap .model-signature .propOptKey { + font-style: italic; +} +.swagger-section .swagger-ui-wrap .model-signature .description .strong { + font-weight: bold; + color: #000; + font-size: .9em; +} +.swagger-section .swagger-ui-wrap .model-signature .description div { + font-size: 0.9em; + line-height: 1.5em; + margin-left: 1em; +} +.swagger-section .swagger-ui-wrap .model-signature .description .stronger { + font-weight: bold; + color: #000; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper { + border-spacing: 0; + position: absolute; + background-color: #ffffff; + border: 1px solid #bbbbbb; + display: none; + font-size: 11px; + max-width: 400px; + line-height: 30px; + color: black; + padding: 5px; + margin-left: 10px; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper th { + text-align: center; + background-color: #eeeeee; + border: 1px solid #bbbbbb; + font-size: 11px; + color: #666666; + font-weight: bold; + padding: 5px; + line-height: 15px; +} +.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper .optionName { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .model-signature .propName { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .model-signature .signature-container { + clear: both; +} +.swagger-section .swagger-ui-wrap .body-textarea { + width: 300px; + height: 100px; + border: 1px solid #aaa; +} +.swagger-section .swagger-ui-wrap .markdown p code, +.swagger-section .swagger-ui-wrap .markdown li code { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #f0f0f0; + color: black; + padding: 1px 3px; +} +.swagger-section .swagger-ui-wrap .required { + font-weight: bold; +} +.swagger-section .swagger-ui-wrap input.parameter { + width: 300px; + border: 1px solid #aaa; +} +.swagger-section .swagger-ui-wrap h1 { + color: black; + font-size: 1.5em; + line-height: 1.3em; + padding: 10px 0 10px 0; + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap .heading_with_menu { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap .heading_with_menu ul { + display: block; + clear: none; + float: right; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + margin-top: 10px; +} +.swagger-section .swagger-ui-wrap h2 { + color: black; + font-size: 1.3em; + padding: 10px 0 10px 0; +} +.swagger-section .swagger-ui-wrap h2 a { + color: black; +} +.swagger-section .swagger-ui-wrap h2 span.sub { + font-size: 0.7em; + color: #999999; + font-style: italic; +} +.swagger-section .swagger-ui-wrap h2 span.sub a { + color: #777777; +} +.swagger-section .swagger-ui-wrap span.weak { + color: #666666; +} +.swagger-section .swagger-ui-wrap .message-success { + color: #89BF04; +} +.swagger-section .swagger-ui-wrap caption, +.swagger-section .swagger-ui-wrap th, +.swagger-section .swagger-ui-wrap td { + text-align: left; + font-weight: normal; + vertical-align: middle; +} +.swagger-section .swagger-ui-wrap .code { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.text textarea { + font-family: "Droid Sans", sans-serif; + height: 250px; + padding: 4px; + display: block; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.select select { + display: block; + clear: both; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean label { + display: block; + float: left; + clear: none; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean input { + display: block; + float: left; + clear: none; + margin: 0 5px 0 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.required label { + color: black; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label { + display: block; + clear: both; + width: auto; + padding: 0 0 3px; + color: #666666; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label abbr { + padding-left: 3px; + color: #888888; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li p.inline-hints { + margin-left: 0; + font-style: italic; + font-size: 0.9em; + margin: 0; +} +.swagger-section .swagger-ui-wrap form.formtastic fieldset.buttons { + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap span.blank, +.swagger-section .swagger-ui-wrap span.empty { + color: #888888; + font-style: italic; +} +.swagger-section .swagger-ui-wrap .markdown h3 { + color: #547f00; +} +.swagger-section .swagger-ui-wrap .markdown h4 { + color: #666666; +} +.swagger-section .swagger-ui-wrap .markdown pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + background-color: #fcf6db; + border: 1px solid #e5e0c6; + padding: 10px; + margin: 0 0 10px 0; +} +.swagger-section .swagger-ui-wrap .markdown pre code { + line-height: 1.6em; +} +.swagger-section .swagger-ui-wrap div.gist { + margin: 20px 0 25px 0 !important; +} +.swagger-section .swagger-ui-wrap ul#resources { + font-family: "Droid Sans", sans-serif; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource { + border-bottom: 1px solid #dddddd; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading h2 a, +.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading h2 a { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading ul.options li a, +.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading ul.options li a { + color: #555555; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource:last-child { + border-bottom: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading { + border: 1px solid transparent; + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options { + overflow: hidden; + padding: 0; + display: block; + clear: none; + float: right; + margin: 14px 10px 0 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li { + float: left; + clear: none; + margin: 0; + padding: 2px 10px; + border-right: 1px solid #dddddd; + color: #666666; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a { + color: #aaaaaa; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover { + text-decoration: underline; + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:active, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a.active { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.last { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 { + color: #999999; + padding-left: 0; + display: block; + clear: none; + float: left; + font-family: "Droid Sans", sans-serif; + font-weight: bold; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a { + color: #999999; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation { + float: none; + clear: both; + overflow: hidden; + display: block; + margin: 0 0 10px; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading { + float: none; + clear: both; + overflow: hidden; + display: block; + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 { + display: block; + clear: none; + float: left; + width: auto; + margin: 0; + padding: 0; + line-height: 1.1em; + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path { + padding-left: 10px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a { + color: black; + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.http_method a { + text-transform: uppercase; + text-decoration: none; + color: white; + display: inline-block; + width: 50px; + font-size: 0.7em; + text-align: center; + padding: 7px 0 4px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + -o-border-radius: 2px; + -ms-border-radius: 2px; + -khtml-border-radius: 2px; + border-radius: 2px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span { + margin: 0; + padding: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options { + overflow: hidden; + padding: 0; + display: block; + clear: none; + float: right; + margin: 6px 10px 0 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li { + float: left; + clear: none; + margin: 0; + padding: 2px 10px; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a { + text-decoration: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access { + color: black; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content { + border-top: none; + padding: 10px; + -moz-border-radius-bottomleft: 6px; + -webkit-border-bottom-left-radius: 6px; + -o-border-bottom-left-radius: 6px; + -ms-border-bottom-left-radius: 6px; + -khtml-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -moz-border-radius-bottomright: 6px; + -webkit-border-bottom-right-radius: 6px; + -o-border-bottom-right-radius: 6px; + -ms-border-bottom-right-radius: 6px; + -khtml-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + margin: 0 0 20px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content h4 { + font-size: 1.1em; + margin: 0; + padding: 15px 0 5px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header { + float: none; + clear: both; + overflow: hidden; + display: block; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header a { + padding: 4px 0 0 10px; + display: inline-block; + font-size: 0.9em; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header input.submit { + display: block; + clear: none; + float: left; + padding: 6px 8px; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber { + background-image: url('../images/throbber.gif'); + width: 128px; + height: 16px; + display: block; + clear: none; + float: right; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form input[type='text'].error { + outline: 2px solid black; + outline-color: #cc0000; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre { + font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; + padding: 10px; + font-size: 0.9em; + max-height: 400px; + overflow-y: auto; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading { + background-color: #f9f2e9; + border: 1px solid #f0e0ca; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading h3 span.http_method a { + background-color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #f0e0ca; + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li a { + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content { + background-color: #faf5ee; + border: 1px solid #f0e0ca; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content h4 { + color: #c5862b; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content div.sandbox_header a { + color: #dcb67f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading { + background-color: #fcffcd; + border: 1px solid black; + border-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading h3 span.http_method a { + text-transform: uppercase; + background-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #ffd20f; + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li a { + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content { + background-color: #fcffcd; + border: 1px solid black; + border-color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content h4 { + color: #ffd20f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content div.sandbox_header a { + color: #6fc992; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading { + background-color: #f5e8e8; + border: 1px solid #e8c6c7; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading h3 span.http_method a { + text-transform: uppercase; + background-color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #e8c6c7; + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li a { + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { + background-color: #f7eded; + border: 1px solid #e8c6c7; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content h4 { + color: #a41e22; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content div.sandbox_header a { + color: #c8787a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading { + background-color: #e7f6ec; + border: 1px solid #c3e8d1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading h3 span.http_method a { + background-color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3e8d1; + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li a { + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content { + background-color: #ebf7f0; + border: 1px solid #c3e8d1; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content h4 { + color: #10a54a; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content div.sandbox_header a { + color: #6fc992; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading { + background-color: #FCE9E3; + border: 1px solid #F5D5C3; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading h3 span.http_method a { + background-color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #f0cecb; + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li a { + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content { + background-color: #faf0ef; + border: 1px solid #f0cecb; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content h4 { + color: #D38042; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content div.sandbox_header a { + color: #dcb67f; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading { + background-color: #e7f0f7; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading h3 span.http_method a { + background-color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3d9ec; + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li a { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content h4 { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content div.sandbox_header a { + color: #6fa5d2; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading { + background-color: #e7f0f7; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading h3 span.http_method a { + background-color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li { + border-right: 1px solid #dddddd; + border-right-color: #c3d9ec; + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li a { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content h4 { + color: #0f6ab4; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content div.sandbox_header a { + color: #6fa5d2; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content { + border-top: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li:last-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li.last, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li.last { + padding-right: 0; + border-right: none; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:hover, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:active, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a.active { + text-decoration: underline; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations:first-child, +.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations.first { + padding-left: 0; +} +.swagger-section .swagger-ui-wrap p#colophon { + margin: 0 15px 40px 15px; + padding: 10px 0; + font-size: 0.8em; + border-top: 1px solid #dddddd; + font-family: "Droid Sans", sans-serif; + color: #999999; + font-style: italic; +} +.swagger-section .swagger-ui-wrap p#colophon a { + text-decoration: none; + color: #547f00; +} +.swagger-section .swagger-ui-wrap h3 { + color: black; + font-size: 1.1em; + padding: 10px 0 10px 0; +} +.swagger-section .swagger-ui-wrap .markdown ol, +.swagger-section .swagger-ui-wrap .markdown ul { + font-family: "Droid Sans", sans-serif; + margin: 5px 0 10px; + padding: 0 0 0 18px; + list-style-type: disc; +} +.swagger-section .swagger-ui-wrap form.form_box { + background-color: #ebf3f9; + border: 1px solid #c3d9ec; + padding: 10px; +} +.swagger-section .swagger-ui-wrap form.form_box label { + color: #0f6ab4 !important; +} +.swagger-section .swagger-ui-wrap form.form_box input[type=submit] { + display: block; + padding: 10px; +} +.swagger-section .swagger-ui-wrap form.form_box p.weak { + font-size: 0.8em; +} +.swagger-section .swagger-ui-wrap form.form_box p { + font-size: 0.9em; + padding: 0 0 15px; + color: #7e7b6d; +} +.swagger-section .swagger-ui-wrap form.form_box p a { + color: #646257; +} +.swagger-section .swagger-ui-wrap form.form_box p strong { + color: black; +} +.swagger-section .title { + font-style: bold; +} +.swagger-section .secondary_form { + display: none; +} +.swagger-section .main_image { + display: block; + margin-left: auto; + margin-right: auto; +} +.swagger-section .oauth_body { + margin-left: 100px; + margin-right: 100px; +} +.swagger-section .oauth_submit { + text-align: center; +} +.swagger-section .api-popup-dialog { + z-index: 10000; + position: absolute; + width: 500px; + background: #FFF; + padding: 20px; + border: 1px solid #ccc; + border-radius: 5px; + display: none; + font-size: 13px; + color: #777; +} +.swagger-section .api-popup-dialog .api-popup-title { + font-size: 24px; + padding: 10px 0; +} +.swagger-section .api-popup-dialog .api-popup-title { + font-size: 24px; + padding: 10px 0; +} +.swagger-section .api-popup-dialog p.error-msg { + padding-left: 5px; + padding-bottom: 5px; +} +.swagger-section .api-popup-dialog button.api-popup-authbtn { + height: 30px; +} +.swagger-section .api-popup-dialog button.api-popup-cancel { + height: 30px; +} +.swagger-section .api-popup-scopes { + padding: 10px 20px; +} +.swagger-section .api-popup-scopes li { + padding: 5px 0; + line-height: 20px; +} +.swagger-section .api-popup-scopes .api-scope-desc { + padding-left: 20px; + font-style: italic; +} +.swagger-section .api-popup-scopes li input { + position: relative; + top: 2px; +} +.swagger-section .api-popup-actions { + padding-top: 10px; +} +.swagger-section .access { + float: right; +} +.swagger-section .auth { + float: right; +} +.swagger-section #api_information_panel { + position: absolute; + background: #FFF; + border: 1px solid #ccc; + border-radius: 5px; + display: none; + font-size: 13px; + max-width: 300px; + line-height: 30px; + color: black; + padding: 5px; +} +.swagger-section #api_information_panel p .api-msg-enabled { + color: green; +} +.swagger-section #api_information_panel p .api-msg-disabled { + color: red; +} +.swagger-section .api-ic { + height: 18px; + vertical-align: middle; + display: inline-block; + background: url(../images/explorer_icons.png) no-repeat; +} +.swagger-section .ic-info { + background-position: 0 0; + width: 18px; + margin-top: -7px; + margin-left: 4px; +} +.swagger-section .ic-warning { + background-position: -60px 0; + width: 18px; + margin-top: -7px; + margin-left: 4px; +} +.swagger-section .ic-error { + background-position: -30px 0; + width: 18px; + margin-top: -7px; + margin-left: 4px; +} +.swagger-section .ic-off { + background-position: -90px 0; + width: 58px; + margin-top: -4px; + cursor: pointer; +} +.swagger-section .ic-on { + background-position: -160px 0; + width: 58px; + margin-top: -4px; + cursor: pointer; +} +.swagger-section #header { + background-color: #89bf04; + padding: 14px; +} +.swagger-section #header a#logo { + font-size: 1.5em; + font-weight: bold; + text-decoration: none; + background: transparent url(../images/logo_small.png) no-repeat left center; + padding: 20px 0 20px 40px; + color: white; +} +.swagger-section #header form#api_selector { + display: block; + clear: none; + float: right; +} +.swagger-section #header form#api_selector .input { + display: block; + clear: none; + float: left; + margin: 0 10px 0 0; +} +.swagger-section #header form#api_selector .input input#input_apiKey { + width: 200px; +} +.swagger-section #header form#api_selector .input input#input_baseUrl { + width: 400px; +} +.swagger-section #header form#api_selector .input a#explore { + display: block; + text-decoration: none; + font-weight: bold; + padding: 6px 8px; + font-size: 0.9em; + color: white; + background-color: #547f00; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -o-border-radius: 4px; + -ms-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; +} +.swagger-section #header form#api_selector .input a#explore:hover { + background-color: #547f00; +} +.swagger-section #header form#api_selector .input input { + font-size: 0.9em; + padding: 3px; + margin: 0; +} +.swagger-section #content_message { + margin: 10px 15px; + font-style: italic; + color: #999999; +} +.swagger-section #message-bar { + min-height: 30px; + text-align: center; + padding-top: 10px; +} diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/css/typography.css b/apiroute/apiroute-service/src/main/resources/api-doc/css/typography.css new file mode 100644 index 0000000..27c3751 --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/api-doc/css/typography.css @@ -0,0 +1,26 @@ +/* droid-sans-regular - latin */ +@font-face { + font-family: 'Droid Sans'; + font-style: normal; + font-weight: 400; + src: url('../fonts/droid-sans-v6-latin-regular.eot'); /* IE9 Compat Modes */ + src: local('Droid Sans'), local('DroidSans'), + url('../fonts/droid-sans-v6-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/droid-sans-v6-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/droid-sans-v6-latin-regular.woff') format('woff'), /* Modern Browsers */ + url('../fonts/droid-sans-v6-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/droid-sans-v6-latin-regular.svg#DroidSans') format('svg'); /* Legacy iOS */ +} +/* droid-sans-700 - latin */ +@font-face { + font-family: 'Droid Sans'; + font-style: normal; + font-weight: 700; + src: url('../fonts/droid-sans-v6-latin-700.eot'); /* IE9 Compat Modes */ + src: local('Droid Sans Bold'), local('DroidSans-Bold'), + url('../fonts/droid-sans-v6-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/droid-sans-v6-latin-700.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/droid-sans-v6-latin-700.woff') format('woff'), /* Modern Browsers */ + url('../fonts/droid-sans-v6-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/droid-sans-v6-latin-700.svg#DroidSans') format('svg'); /* Legacy iOS */ +} diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.eot b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.eot new file mode 100644 index 0000000000000000000000000000000000000000..2250b71a3cc65979f041a87add394baa4a8becf0 GIT binary patch literal 22924 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 literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.svg b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.svg new file mode 100644 index 0000000..a54bbbb --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.svg @@ -0,0 +1,411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.ttf b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.ttf new file mode 100644 index 0000000000000000000000000000000000000000..523cb92d512a1a9dbb21f0a80f7a156ccb1a1198 GIT binary patch literal 40516 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 literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.woff b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.woff new file mode 100644 index 0000000000000000000000000000000000000000..67e3e25f8362c71003430d52894260cd4c69413a GIT binary patch literal 25992 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

N`?RC`@lwXbC3bfgCS&z9};Se$Q$7 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 literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.woff2 b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-700.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1e726a7cfce4130243a3de4bb2bd412000658896 GIT binary patch literal 11480 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 literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.eot b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.eot new file mode 100644 index 0000000000000000000000000000000000000000..ac2698e85aee584b0f47c0cccff8a8b9b16a5c66 GIT binary patch literal 22008 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%0AA9nOF`EcNA#X$~6CRi6AAT-xXx{3Bx zY>P|Hk04QYUn_!+e;}eYbXCJX|4Z{6mH_z``?#@xAsubOT(Q@!DRqy4=LlRHzuK{Q zVtt;dn-i{BG!kd3yh#`P>4>SY2~5_}fOds~BSNBzz!Q|@r9)leN$s{gjLkY7&y@f` zoN!Q(BNFqfK|^g=H{YgY5#2jfQvMblq6WKGLCDvjhwi>Kyq&C_>djj)6sKLNOwl&UrzfT;hc1Pp=K5X)*Y| zqYjCBPSn;EHcs*8cWh!h$$Pe@0z6sF1FVT=*eug{5E_}aH;fY86|ssLSbKZCya%!t zJ5WfpG2?)&lLG9Gw9e?3(BufhIGq) z49JHokHKxrgU0)*boj{J@7r2S>m!^&L6Z$Zy!y*x9&MiH7i$0QQq($>57`k0YD_7p z6L)upccac+p~e&5@a$eBw7bI#CyQWt%v*eSmtVX2!+K@eu-3sw-SQ4zUQ5>ighWo+ zi8tnuZ-^9b6gWGu6yDY8?^|eEx{O-QB{{8xmo(d=s;`Cxbp@Tpw^QLUb9OM3u-~kx z#FA;*9%Kd0-5iq=nOuVC7mx3sWr}Aw)x?%Gn{h+1cBS?0&*7EYdWxcDeRjF5r;_g( z7Iw(-!4$TZM#zU2E6mR!dpU$?=Rc^=adMc_Z*xsXr)Pt&&=( zLZ#NGI%J$CY)9j{^Vc@D&p9Nx%fK+%nN6$#G4>@2*$`@fm!O9#;!S8IA`x_)GH zP|f3fSM)8&If}A=EL;I>LRjKGPJTK!#n?}O%yn=vs%sVCvl@kj_(f(<6T!i%*aA)> z@!wHp(RevjcE~!BIgDP6B`Q$c)MYhJHWm}UpAs3E7|k&8)F0i(Y6Ml2$?6@$mOh7J z21%|^QO>$1&)ja&MY2!WqBL>8utNGv%L;psd4G4WzRVa*`=UvY*H*aeMb3CGi%Bil zrN_2u*oS$<=<6=m2(^r&`S+2I9`~ZALArfEUjFK*jta%30kZXxhu}0C1DrcOYYSCG zwA5$zO3?S9vJxmAQd@M}7U>B<*~%|aCrun2&wiRo=7rUF3&A-p2G5*{>t@)r3Gvda zT_eqD&i+25KP#w#Mq&$}kk9qoaPk~hb*F*rBA71;bI{sNfjj)lSC*Ekl ztHQ0CThwZ`d0D|^kGK!jrXIhcq>5`OC@q(nBc;LpbajBV{tp4F z$Sfx^)Ey$UX7mu8KE}^*@20Q!bKG7RoM$#z-)Sh1?zSi1Q3;*!J5Rc`gc-XqrK{aJ ztzaEzzA}+f8Z*LK(RuYHWOW=vPhI74S+2BmxMpNb>iAeQ=~Qr!kVnQ;SW}EIB~IW$Op~V> zH?Aop!ZV9nuysQUS8)(%i4i>ZWf!pq#rgi4yqnlK1b(LNF3Kp4Ws zGOynzeH(!J%WdACSWVo7zPjcOez?P}YihMvavADhqw`4P7d=6V(G`;b;$Yn>r>E`Y zB=Ys|Y5&fj{@9c>z0_W>Hg`kcd=(Un2MVUu4!K7ptcMh zduEifSlBu)Id|prEYv;Q8srzlW%f2DYECLA<7WhO*qEXc+8Uy|D8RrZx5K*9nCB0RyJC}M)i>?J=5=iu zsJ9TwmvuP=3(nVXKR*g@dPQ%A4}=IwQTy@z9J5sf_W%fkkvxd%6uk53HC7L5D@bZ(qV-22a0U9aJHn>ZN;CJ6N+UqS40`~ zm;|FjP?<&Bn8vA7i_@Dc(YPe&WLoD3=e<3VLXy_ZdnI8+ZqTIOC-qy+5PL$3GMz0Q zXw8|3Ufr@&JH1dZ+M^6l&$MFY3V(fEep++f@W*u*hR;WHf}>Qx?i=>k+SQE@Y3#(; zvcyd!&ouwNTYYHhx;wGl^$Ca<<~NIlI2!rn1UnyH#=qQ#s`c`uOrRMDct|HVafiDd zWcKe3%s2(MrZOHpXO%o({`9RVT&7*aL30`Xif&4>WYePDOEx(mnFprju5iIU1#ir!vfFJCursjicebShp8 zB|d#K?<0dlzgm95E_a8dThpUrwUmA`^(MbT&<)#STP%%IA1cGrI*vpc<{ZQc?K~7O z|6L{WWhjSwr8vlR0t5*vAj|m?j&~o)I2zxvS8Z>kzqZOhNW=+u6T~k+Gp{BSCGw>1 z!|(HU19P?kVqTd~*>iT)DWQR_eU3#DpHidBFl9W9@rkyP-gfr8HB$|bwu})G64*+E zStPgm*ijJ(hqX|O-~gCPEsZCse86RU#d}FN&?A5-L_*0$GQ_XcOEBEI1lZ?UMszR5 z_jvnrWoVURqRdJM%lx5RCNk6n6+X_x)>Ik_LKD(QE{76U9EH&$p|XXuzI!BbGla$G zEo`R4TS*u_jIz4)8vL4z%E*3 zj9w_m9y3hjg9-+q@*u-hv}^=U-%m9Q@9H*HXU6y{%j56Y(|ZPM^LmUi)XQ2#WbF%H zC?TumDd)Ip9qH7J=M~q(EjKVq16wGy&897IG=n=&EUnw8#Ld4^9T<)I9<=0~Dn+`h z?+B@zi(j25wB_98j^P(*-NLBe zzwJEWdpPj?*T;U|HDVfj<;yDaF_JqbHTEoEid&NsV%0+}>PN%d>y-G*?wje+hG&p| z!*Z6_iSekxCe(fo(Vp>X603zA?vN-EJ~2{44Mu~gD_oFA_G1(u^9)t`?-1$}UR~GS z{(p7O^tDV)%2XH7`-HvlE}~K~UmN}fZZ6U{(YT)EX@o!+j7Pkj7&m#RiXfkb3t8c{ zGDFO)-S(eoYiUYS6y*qvPld!CRZotvv2Sg!vGC>d6CqzvTOfCqlmV7eBZ8%SrBLyg zCInw$1T+fz=iE60JxZ%mq*Rq3oOY5+8$1kd10+N8 z6N@e{sKHk&jLo|L{gxgW>^uTW>Lt5vXl4+!ncwf86hd_9En)O#khd7QV%YZ(_6MP* zaThsQ2FQd;LB@G$eswt6&(mni>`tvTEThq`z?8Xl?9Cv1$thDHI>?MNjurdJ7lrbz4XV3=g0p#o3CWqjjNGige zZyHnM$U2X1l_qB&>PW!fofneo%OuOvZb)#c$wab>$Ot%^t;ThcZ-VR!Bzi>+3q+}z z0Dj@^x2n1-4;A*&<=h$Xc{?fX4bEmCtZN zsobn>;9Ffei)mE=+42%fBr+Uw4bDxn%9PX*F8`)-`&Lhgnw#_?L9$LkqK6SQ@GfY; z!z{sBk|sj)N?^euO7xCZ0WM05klOhGcGdlaZ!v3&%}&QY z^+eP`Axh-v!mfIAOU90_)1KF)$W`%y#utXVf<>i%QoF~$|J2RgSVYi?Q0pY?9a{Gk zgzUAwDC1SLiC8jYXnC2iJA(W2EQfoAA5od9kNGnKccn)^_|N_U&p z%e{rF%QP?ig*!?~qoU^WW{!skd^TXK2(xvv#4+7r#uG3e4Y~6!d8a+;s#`Y09Tg$h zyi+U!_~JS0;4io6?0%3p6Fi21w?xRxhyJR+Dy!MCRqL?o)S^xUgR{9@-lbmy9D2^Jdg56(yBjeH`R-Z_nAc)>p6WmP3w_s2Ip8wDA#=Y#xOE1Bx^pq7r_U zh>JSdB!IPg=BY~DaT9_Hy*GssFbb^?xVomE2BE=$v1}W*R-~dW1Gq4D+&KUXq#2Bt zs?-%Kz^n%yj~bdDI)<7%K?=@4vY~*_(L<=TPHNB~G^O+M${K4ea@PD@zVbulEJbvE z7r50D)nx}cvXItnB04sPWflQqV#@zICq-oooM@G%6cgsK!W7h646Kp}W1paj(4X&V zQqxh%I9m?KMfVcHgV>Jnm5CI=&^iI!dV@o>V_e;q?nyP!=!jX$T4^z|KzjIXQ~UJC zQWDRds{f|0M2zD5N@Xu_5k&KBr>M&MDLey5K`Bu>+Oo379@1TO4d8EcLm`}!@`v=r z>LCSC&bHVPMTqjHVWa@kJ~tA875R*#S0Lg}1md(iVOp?pODup~qKJq|X{Ev+mnhLV zs?(9*!15Tl9(ZIRj1UjFaPXP>)vlC`Y6w2HD;fQCx#a=TV^|(fnHE-x zO?wzJiedZyIW~^;4*HTanrOa9#hiyCn zuh?3bG$$G7o$`7xBnCk)g752{a-z$_f>dx8+qIwXnMm9x=vo=89@)5Ccw~gc{i-Q? zSG|S$f~J6{nf?JkSr`^JfG}8BuQ$w) zc8eo2kZOloOSUGkzW;2ShtNe&g=-PS{05d>0Xuta2OIK{;;xbVq|6xBCOVP1KMbO+ z41CoS?R?Nl*MrmYAbV{};2u+r;lc;N)~Hr?BYJOr>`qU+8JOeSIh@fv4Kh|NdFUAa znEkWbwF*z9yFym=H|(w%JAx-Ci7v09YOPi8i&zbe+iyBl-h@+&XlDk<1Cs331mm*Y zo=yy_ER<*bd3tQxB39@<%ex4+D=-Cxn?eK%o9jmLlJ+@pNnCQEK?nGq=rx#bz~Nd5 zUT&A<5qd{@WE*Oo+f^YuX*3|#@zuivn&wjjtTBMt_0^0RK$C$$96*w!ydrmmoZ4$veC3{OuGBr%2!_JwOE8vIP;5 zcOh-KX3@sc4E<6Jb-jCvBuW}6A;#oNoWy(p#gswbgLt4w9w8wtk$-78G}V+rl;!MC zDNn#5Xy=VCB-&Kc-$`GkI;AP3A`lU^B)^9#E~Q)0e6b#=7wpvP^`ldU*M&j;#-re( z)Q6fB>?9h`?Nah!kvXV7;}?aDWR21mFSLUVX!8Ie8-XsSxk zmkSn1CWE#TC$Qj5stds9Ps1;P2D?+ztVlqD2Oq;#xnA%ip~{fQRP$$qRZZpLFMmD~ z8iaVubJOsT@HCu#hz5U2u^awEIB>72cr#+|IIue}5VS%5co-0nPd;hzx1zyV&QzKK zEf#CN@3A&S@4{(*O^BK}NLCsv3|Vf70hoB6_t?E73`rMOEU^d%10T%{0pKAD&%PT( zdldQ*$iRUv6Gzu8AWY5Jnl=RC*D6)Yb6nNgL6sOpq-wN6iLUJ`u3Al{`pnp#ap=M6T+c_THsepEipr$Z=sJO(JIT!|%f9E0_RWduX zDcS|p0QTARSBZYgG!Eh98XW2K&}kI8L771AP%{YJH(?3^k3EP)R|E@q+Reny)qD&~ z0B{vClK{lE=hm>oPQz#TnLx7-T}6R?M;?vPESnJWVRz85Mj7YlkKVf?{n2awR}3%M1!L? zT6Ei4W(s;~aNSzR(Ab~?0a&~OG7svaf;Za41tF}*fSKkM0}yPV!&7N z9Rgen?HNPdI3RXVRZtjYyRV*1-z85x(D@AOe3a2&$$%+?{(xQ%;?QL;LFn&2obVPb;R6yB6zUNS+#mVBCwm z24YEPmdQ}@I&SQPDT~-7uME=d>}FYV?iUT4-1o8FyE7Ee2m0OcNu({YFiny+Swju zAxKekYr_qzUajC6=)lj*=m|23sFw=R`E##9joh?4M1v6TYU%I?!EPZMx#(FI^)!h+ z5ZxP8#5MOrj=jSCWX%LBcc^FtMiY2ArjeA)Mnav{Gk`Ea00%<#m3bcP&va*z7i0~# zN)wIuq=g+9e)C;&kAKVIFXWJK73~iDDXS@G8u2W|jY*-SzAkqaE{jzXq>~0WZdZi5 z29HYO(#nhwh!MQLsDC#^W%(*wYrgx2keF*6c*g~OFZr&B&pb6lHF@%t{F-J z&cJMzyqOFm>&S{z%KIUrk{1w=ok5K3tIq?kYMTZcYB7hL=0u%)mZX!Tah2I6Js14?6?rr(56f*sJ zy*3r+=*?v`kz^ghI7%9myyzB5vAE^dt;vIr!|@sm_6>vQ%39xpzV!$*6+!sixu(z% zEEQ+K_$+w)$wJ(P{+mO*Sc1i+fOQYkWUB83!90U4v;`F!@{%>Rlh8QQkxq&<_1lGRTFEyQ0#D799pB&BcNi~X z!>DcF_yCbWY=O&^#@j+K>dYzafc=okA#)n2U@!^0UBdsprBZTRW%vpRb0moHUI1~l ze}i42Abm)|3F{h}FOD&6mjNmGuOuFLV(FyO#cT70n%yxG?EfuP+;4y}sR1Ktwx@f{eN1@nctQXzE-DKfO6~Tc2BBsHB zw;GAom_L6)UaiL>=a&IkuX-|9od5zF=C2@(5wJUIph}SvL7!|u@n?qG!@EUFdIqlU zB^LbBH2RA8**BP6<%PCvdzNtkekI80z`KmZ6)3fh*0A0orm zmyS)K#n^?#v=N!;O4F$&bYw*>8L}oSuSngLt3Yg)y?sr#*w6tKT&F80C}UIIL@;>K zMOG*-D6(u?O9KLOW$xcQQp{5bDcISDVm2T=B%u@A&jUmZsqQjM80E9Glc-x@1BfxVc;t<;cJbvf`b6z%8UQA?>io&iS;r=BQ zzXUKjE|_-+>bV`ZZ4}`aW-)}?ex!2^qzUK>Du#ba6Sf*w%8FV_U&dO(sv>k|kbV_y z*kDJ>Kcr2;sEe|81(G16Nu-#QjkmOzIdqLyKoyuA2g^J?hZ&^w+C7|V?nrrzPGNP4 zc1ewp9OFBd7l%h%B$$_LD$U&;mqflvNZm7=Vqz2-895=RY>LtG536sdcblE72o2Ny zsSH5ZpeU(ImrqlVIRv$&*#9W=L)^s7g|x(c2T%$_k)D5dRcjt9)W#HG^zat@ZxQ+} zU~aBKQ(Ed`q~T(nZCIle*Ot}JcdiOwzRBr+75QbR^uc|Ao$%lunS0+AQO9V2AHt9T z6~CgYw(i8H1kAQ!Dj5he0F&d!377$240XbIsz!GbiPWP&ZXoINstU6YV6mVvw8w)q zjopeOg>k4G?jMENIRCbToIYSR?|LzaY8_27|eJBEW?$jEFT5t##AT&nDXMUiA5MFc4}tZ^An}UboT{ za$6#CXj5F{ouNRG#m<}3(e<^HS zK_@^i^wBJ40mUpykEv|q;V(7rZVJS*%8-}Gaqk7wpyrT@TVoyK+m- zW;n)6pSBT!Y|gXr$z@GGMlbH{V4~myOzYbyq&o<}r5xZD%?~>hIR?-`RXv4o{>K4b zmhZMxTpF8NQT9l6scJ9H0Tf~V{X;(<} zK$qppI>Ra+U9#4(2L-y7F%hi7XwFY=jp3piiu>AvT=IQjWY-ohDS<_`&YMeCO~aII zH&CY<@7h-coq&SG=H0bn9`KN#hqNT5!6e%q?sKzCpBgYs*B6lrt$aLOqTnPiHy#e= zXpwFYOp10}rW#_Ft4t>gu3Rx;MN%z3!?2BGoX2SOu?kQC*I6k4Wq?_?)uV)IZpWU9 M4w+BmWsNjR(0tp4%K!iX literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.svg b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.svg new file mode 100644 index 0000000..d9f2a21 --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.svg @@ -0,0 +1,403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.ttf b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..76aede27f9d730b3b18576657d0994c38a88b976 GIT binary patch literal 39072 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 literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.woff b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..abf19899f75a0e8ab8e2d41caeecd8d764d3f08c GIT binary patch literal 24868 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 X8CoUMY*lMRVpH6ur?CLJX!hLz`Fb1d literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.woff2 b/apiroute/apiroute-service/src/main/resources/api-doc/fonts/droid-sans-v6-latin-regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..9f93f74c3bede4b96290650f7a21c98eb1e01351 GIT binary patch literal 11304 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 A82|tP literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/images/pet_store_api.png b/apiroute/apiroute-service/src/main/resources/api-doc/images/pet_store_api.png new file mode 100644 index 0000000000000000000000000000000000000000..f9f9cd4aeb35a108c4b2f1dddb59977d56c595d8 GIT binary patch literal 824 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 z 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 + + + + Swagger UI + + + + + + + + + + + + + + + + + + + + + + + + + +

+ +
 
+
+ + + diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/js/iframeResizer/iframeResizer.contentWindow.min.js b/apiroute/apiroute-service/src/main/resources/api-doc/js/iframeResizer/iframeResizer.contentWindow.min.js new file mode 100644 index 0000000..9330e0f --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/api-doc/js/iframeResizer/iframeResizer.contentWindow.min.js @@ -0,0 +1,10 @@ +/*! iFrame Resizer (iframeSizer.contentWindow.min.js) - v3.5.3 - 2016-02-23 + * Desc: Include this file in any page being loaded into an iframe + * to force the iframe to resize to the content size. + * Requires: iframeResizer.min.js on host page. + * Copyright: (c) 2016 David J. Bradshaw - dave@bradshaw.net + * License: MIT + */ + +!function(a){"use strict";function b(b,c,d){"addEventListener"in a?b.addEventListener(c,d,!1):"attachEvent"in a&&b.attachEvent("on"+c,d)}function c(b,c,d){"removeEventListener"in a?b.removeEventListener(c,d,!1):"detachEvent"in a&&b.detachEvent("on"+c,d)}function d(a){return a.charAt(0).toUpperCase()+a.slice(1)}function e(a){var b,c,d,e=null,f=0,g=function(){f=Fa(),e=null,d=a.apply(b,c),e||(b=c=null)};return function(){var h=Fa();f||(f=h);var i=xa-(h-f);return b=this,c=arguments,0>=i||i>xa?(e&&(clearTimeout(e),e=null),f=h,d=a.apply(b,c),e||(b=c=null)):e||(e=setTimeout(g,i)),d}}function f(a){return ma+"["+oa+"] "+a}function g(b){la&&"object"==typeof a.console&&console.log(f(b))}function h(b){"object"==typeof a.console&&console.warn(f(b))}function i(){j(),g("Initialising iFrame ("+location.href+")"),k(),n(),m("background",W),m("padding",$),A(),s(),t(),o(),C(),u(),ia=B(),N("init","Init message from host page"),Da()}function j(){function a(a){return"true"===a?!0:!1}var b=ha.substr(na).split(":");oa=b[0],X=void 0!==b[1]?Number(b[1]):X,_=void 0!==b[2]?a(b[2]):_,la=void 0!==b[3]?a(b[3]):la,ja=void 0!==b[4]?Number(b[4]):ja,U=void 0!==b[6]?a(b[6]):U,Y=b[7],fa=void 0!==b[8]?b[8]:fa,W=b[9],$=b[10],ua=void 0!==b[11]?Number(b[11]):ua,ia.enable=void 0!==b[12]?a(b[12]):!1,qa=void 0!==b[13]?b[13]:qa,Aa=void 0!==b[14]?b[14]:Aa}function k(){function b(){var b=a.iFrameResizer;g("Reading data from page: "+JSON.stringify(b)),Ca="messageCallback"in b?b.messageCallback:Ca,Da="readyCallback"in b?b.readyCallback:Da,ta="targetOrigin"in b?b.targetOrigin:ta,fa="heightCalculationMethod"in b?b.heightCalculationMethod:fa,Aa="widthCalculationMethod"in b?b.widthCalculationMethod:Aa}"iFrameResizer"in a&&Object===a.iFrameResizer.constructor&&b(),g("TargetOrigin for parent set to: "+ta)}function l(a,b){return-1!==b.indexOf("-")&&(h("Negative CSS value ignored for "+a),b=""),b}function m(a,b){void 0!==b&&""!==b&&"null"!==b&&(document.body.style[a]=b,g("Body "+a+' set to "'+b+'"'))}function n(){void 0===Y&&(Y=X+"px"),m("margin",l("margin",Y))}function o(){document.documentElement.style.height="",document.body.style.height="",g('HTML & body height set to "auto"')}function p(e){function f(){N(e.eventName,e.eventType)}var h={add:function(c){b(a,c,f)},remove:function(b){c(a,b,f)}};e.eventNames&&Array.prototype.map?(e.eventName=e.eventNames[0],e.eventNames.map(h[e.method])):h[e.method](e.eventName),g(d(e.method)+" event listener: "+e.eventType)}function q(a){p({method:a,eventType:"Animation Start",eventNames:["animationstart","webkitAnimationStart"]}),p({method:a,eventType:"Animation Iteration",eventNames:["animationiteration","webkitAnimationIteration"]}),p({method:a,eventType:"Animation End",eventNames:["animationend","webkitAnimationEnd"]}),p({method:a,eventType:"Input",eventName:"input"}),p({method:a,eventType:"Mouse Up",eventName:"mouseup"}),p({method:a,eventType:"Mouse Down",eventName:"mousedown"}),p({method:a,eventType:"Orientation Change",eventName:"orientationchange"}),p({method:a,eventType:"Print",eventName:["afterprint","beforeprint"]}),p({method:a,eventType:"Ready State Change",eventName:"readystatechange"}),p({method:a,eventType:"Touch Start",eventName:"touchstart"}),p({method:a,eventType:"Touch End",eventName:"touchend"}),p({method:a,eventType:"Touch Cancel",eventName:"touchcancel"}),p({method:a,eventType:"Transition Start",eventNames:["transitionstart","webkitTransitionStart","MSTransitionStart","oTransitionStart","otransitionstart"]}),p({method:a,eventType:"Transition Iteration",eventNames:["transitioniteration","webkitTransitionIteration","MSTransitionIteration","oTransitionIteration","otransitioniteration"]}),p({method:a,eventType:"Transition End",eventNames:["transitionend","webkitTransitionEnd","MSTransitionEnd","oTransitionEnd","otransitionend"]}),"child"===qa&&p({method:a,eventType:"IFrame Resized",eventName:"resize"})}function r(a,b,c,d){return b!==a&&(a in c||(h(a+" is not a valid option for "+d+"CalculationMethod."),a=b),g(d+' calculation method set to "'+a+'"')),a}function s(){fa=r(fa,ea,Ga,"height")}function t(){Aa=r(Aa,za,Ha,"width")}function u(){!0===U?(q("add"),F()):g("Auto Resize disabled")}function v(){g("Disable outgoing messages"),ra=!1}function w(){g("Remove event listener: Message"),c(a,"message",S)}function x(){null!==Z&&Z.disconnect()}function y(){q("remove"),x(),clearInterval(ka)}function z(){v(),w(),!0===U&&y()}function A(){var a=document.createElement("div");a.style.clear="both",a.style.display="block",document.body.appendChild(a)}function B(){function c(){return{x:void 0!==a.pageXOffset?a.pageXOffset:document.documentElement.scrollLeft,y:void 0!==a.pageYOffset?a.pageYOffset:document.documentElement.scrollTop}}function d(a){var b=a.getBoundingClientRect(),d=c();return{x:parseInt(b.left,10)+parseInt(d.x,10),y:parseInt(b.top,10)+parseInt(d.y,10)}}function e(a){function b(a){var b=d(a);g("Moving to in page link (#"+c+") at x: "+b.x+" y: "+b.y),R(b.y,b.x,"scrollToOffset")}var c=a.split("#")[1]||a,e=decodeURIComponent(c),f=document.getElementById(e)||document.getElementsByName(e)[0];void 0!==f?b(f):(g("In page link (#"+c+") not found in iFrame, so sending to parent"),R(0,0,"inPageLink","#"+c))}function f(){""!==location.hash&&"#"!==location.hash&&e(location.href)}function i(){function a(a){function c(a){a.preventDefault(),e(this.getAttribute("href"))}"#"!==a.getAttribute("href")&&b(a,"click",c)}Array.prototype.forEach.call(document.querySelectorAll('a[href^="#"]'),a)}function j(){b(a,"hashchange",f)}function k(){setTimeout(f,ba)}function l(){Array.prototype.forEach&&document.querySelectorAll?(g("Setting up location.hash handlers"),i(),j(),k()):h("In page linking not fully supported in this browser! (See README.md for IE8 workaround)")}return ia.enable?l():g("In page linking not enabled"),{findTarget:e}}function C(){g("Enable public methods"),Ba.parentIFrame={autoResize:function(a){return!0===a&&!1===U?(U=!0,u()):!1===a&&!0===U&&(U=!1,y()),U},close:function(){R(0,0,"close"),z()},getId:function(){return oa},getPageInfo:function(a){"function"==typeof a?(Ea=a,R(0,0,"pageInfo")):(Ea=function(){},R(0,0,"pageInfoStop"))},moveToAnchor:function(a){ia.findTarget(a)},reset:function(){Q("parentIFrame.reset")},scrollTo:function(a,b){R(b,a,"scrollTo")},scrollToOffset:function(a,b){R(b,a,"scrollToOffset")},sendMessage:function(a,b){R(0,0,"message",JSON.stringify(a),b)},setHeightCalculationMethod:function(a){fa=a,s()},setWidthCalculationMethod:function(a){Aa=a,t()},setTargetOrigin:function(a){g("Set targetOrigin: "+a),ta=a},size:function(a,b){var c=""+(a?a:"")+(b?","+b:"");N("size","parentIFrame.size("+c+")",a,b)}}}function D(){0!==ja&&(g("setInterval: "+ja+"ms"),ka=setInterval(function(){N("interval","setInterval: "+ja)},Math.abs(ja)))}function E(){function b(a){function b(a){!1===a.complete&&(g("Attach listeners to "+a.src),a.addEventListener("load",f,!1),a.addEventListener("error",h,!1),k.push(a))}"attributes"===a.type&&"src"===a.attributeName?b(a.target):"childList"===a.type&&Array.prototype.forEach.call(a.target.querySelectorAll("img"),b)}function c(a){k.splice(k.indexOf(a),1)}function d(a){g("Remove listeners from "+a.src),a.removeEventListener("load",f,!1),a.removeEventListener("error",h,!1),c(a)}function e(a,b,c){d(a.target),N(b,c+": "+a.target.src,void 0,void 0)}function f(a){e(a,"imageLoad","Image loaded")}function h(a){e(a,"imageLoadFailed","Image load failed")}function i(a){N("mutationObserver","mutationObserver: "+a[0].target+" "+a[0].type),a.forEach(b)}function j(){var a=document.querySelector("body"),b={attributes:!0,attributeOldValue:!1,characterData:!0,characterDataOldValue:!1,childList:!0,subtree:!0};return m=new l(i),g("Create body MutationObserver"),m.observe(a,b),m}var k=[],l=a.MutationObserver||a.WebKitMutationObserver,m=j();return{disconnect:function(){"disconnect"in m&&(g("Disconnect body MutationObserver"),m.disconnect(),k.forEach(d))}}}function F(){var b=0>ja;a.MutationObserver||a.WebKitMutationObserver?b?D():Z=E():(g("MutationObserver not supported in this browser!"),D())}function G(a,b){function c(a){var c=/^\d+(px)?$/i;if(c.test(a))return parseInt(a,V);var d=b.style.left,e=b.runtimeStyle.left;return b.runtimeStyle.left=b.currentStyle.left,b.style.left=a||0,a=b.style.pixelLeft,b.style.left=d,b.runtimeStyle.left=e,a}var d=0;return b=b||document.body,"defaultView"in document&&"getComputedStyle"in document.defaultView?(d=document.defaultView.getComputedStyle(b,null),d=null!==d?d[a]:0):d=c(b.currentStyle[a]),parseInt(d,V)}function H(a){a>xa/2&&(xa=2*a,g("Event throttle increased to "+xa+"ms"))}function I(a,b){for(var c=b.length,e=0,f=0,h=d(a),i=Fa(),j=0;c>j;j++)e=b[j].getBoundingClientRect()[a]+G("margin"+h,b[j]),e>f&&(f=e);return i=Fa()-i,g("Parsed "+c+" HTML elements"),g("Element position calculated in "+i+"ms"),H(i),f}function J(a){return[a.bodyOffset(),a.bodyScroll(),a.documentElementOffset(),a.documentElementScroll()]}function K(a,b){function c(){return h("No tagged elements ("+b+") found on page"),da}var d=document.querySelectorAll("["+b+"]");return 0===d.length?c():I(a,d)}function L(){return document.querySelectorAll("body *")}function M(a,b,c,d){function e(){da=l,ya=m,R(da,ya,a)}function f(){function a(a,b){var c=Math.abs(a-b)<=ua;return!c}return l=void 0!==c?c:Ga[fa](),m=void 0!==d?d:Ha[Aa](),a(da,l)||_&&a(ya,m)}function h(){return!(a in{init:1,interval:1,size:1})}function i(){return fa in pa||_&&Aa in pa}function j(){g("No change in size detected")}function k(){h()&&i()?Q(b):a in{interval:1}||j()}var l,m;f()||"init"===a?(O(),e()):k()}function N(a,b,c,d){function e(){a in{reset:1,resetPage:1,init:1}||g("Trigger event: "+b)}function f(){return va&&a in aa}f()?g("Trigger event cancelled: "+a):(e(),Ia(a,b,c,d))}function O(){va||(va=!0,g("Trigger event lock on")),clearTimeout(wa),wa=setTimeout(function(){va=!1,g("Trigger event lock off"),g("--")},ba)}function P(a){da=Ga[fa](),ya=Ha[Aa](),R(da,ya,a)}function Q(a){var b=fa;fa=ea,g("Reset trigger event: "+a),O(),P("reset"),fa=b}function R(a,b,c,d,e){function f(){void 0===e?e=ta:g("Message targetOrigin: "+e)}function h(){var f=a+":"+b,h=oa+":"+f+":"+c+(void 0!==d?":"+d:"");g("Sending message to host page ("+h+")"),sa.postMessage(ma+h,e)}!0===ra&&(f(),h())}function S(b){function c(){return ma===(""+b.data).substr(0,na)}function d(){ha=b.data,sa=b.source,i(),ca=!1,setTimeout(function(){ga=!1},ba)}function e(){ga?g("Page reset ignored by init"):(g("Page size reset by host page"),P("resetPage"))}function f(){N("resizeParent","Parent window requested size check")}function j(){var a=l();ia.findTarget(a)}function k(){return b.data.split("]")[1].split(":")[0]}function l(){return b.data.substr(b.data.indexOf(":")+1)}function m(){return"iFrameResize"in a}function n(){var a=l();g("MessageCallback called from parent: "+a),Ca(JSON.parse(a)),g(" --")}function o(){var a=l();g("PageInfoFromParent called from parent: "+a),Ea(JSON.parse(a)),g(" --")}function p(){return b.data.split(":")[2]in{"true":1,"false":1}}function q(){switch(k()){case"reset":e();break;case"resize":f();break;case"moveToAnchor":j();break;case"message":n();break;case"pageInfo":o();break;default:m()||p()||h("Unexpected message ("+b.data+")")}}function r(){!1===ca?q():p()?d():g('Ignored message of type "'+k()+'". Received before initialization.')}c()&&r()}function T(){"loading"!==document.readyState&&a.parent.postMessage("[iFrameResizerChild]Ready","*")}var U=!0,V=10,W="",X=0,Y="",Z=null,$="",_=!1,aa={resize:1,click:1},ba=128,ca=!0,da=1,ea="bodyOffset",fa=ea,ga=!0,ha="",ia={},ja=32,ka=null,la=!1,ma="[iFrameSizer]",na=ma.length,oa="",pa={max:1,min:1,bodyScroll:1,documentElementScroll:1},qa="child",ra=!0,sa=a.parent,ta="*",ua=0,va=!1,wa=null,xa=16,ya=1,za="scroll",Aa=za,Ba=a,Ca=function(){h("MessageCallback function not defined")},Da=function(){},Ea=function(){},Fa=Date.now||function(){return(new Date).getTime()},Ga={bodyOffset:function(){return document.body.offsetHeight+G("marginTop")+G("marginBottom")},offset:function(){return Ga.bodyOffset()},bodyScroll:function(){return document.body.scrollHeight},documentElementOffset:function(){return document.documentElement.offsetHeight},documentElementScroll:function(){return document.documentElement.scrollHeight},max:function(){return Math.max.apply(null,J(Ga))},min:function(){return Math.min.apply(null,J(Ga))},grow:function(){return Ga.max()},lowestElement:function(){return Math.max(Ga.bodyOffset(),I("bottom",L()))},taggedElement:function(){return K("bottom","data-iframe-height")}},Ha={bodyScroll:function(){return document.body.scrollWidth},bodyOffset:function(){return document.body.offsetWidth},documentElementScroll:function(){return document.documentElement.scrollWidth},documentElementOffset:function(){return document.documentElement.offsetWidth},scroll:function(){return Math.max(Ha.bodyScroll(),Ha.documentElementScroll())},max:function(){return Math.max.apply(null,J(Ha))},min:function(){return Math.min.apply(null,J(Ha))},rightMostElement:function(){return I("right",L())},taggedElement:function(){return K("right","data-iframe-width")}},Ia=e(M);b(a,"message",S),T()}(window||{}); +//# sourceMappingURL=iframeResizer.contentWindow.map \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/resources/api-doc/js/iframeResizer/iframeResizer.min.js b/apiroute/apiroute-service/src/main/resources/api-doc/js/iframeResizer/iframeResizer.min.js new file mode 100644 index 0000000..e8f4bcb --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/api-doc/js/iframeResizer/iframeResizer.min.js @@ -0,0 +1,9 @@ +/*! iFrame Resizer (iframeSizer.min.js ) - v3.5.3 - 2016-02-23 + * Desc: Force cross domain iframes to size to content. + * Requires: iframeResizer.contentWindow.min.js to be loaded into the target frame. + * Copyright: (c) 2016 David J. Bradshaw - dave@bradshaw.net + * License: MIT + */ + +!function(a){"use strict";function b(b,c,d){"addEventListener"in a?b.addEventListener(c,d,!1):"attachEvent"in a&&b.attachEvent("on"+c,d)}function c(b,c,d){"removeEventListener"in a?b.removeEventListener(c,d,!1):"detachEvent"in a&&b.detachEvent("on"+c,d)}function d(){var b,c=["moz","webkit","o","ms"];for(b=0;be&&(e=c,h(W,"Set "+d+" to min value")),e>b&&(e=b,h(W,"Set "+d+" to max value")),V[d]=""+e}function k(){function a(){function a(){var a=0,d=!1;for(h(W,"Checking connection is from allowed list of origins: "+c);aP[w]["max"+a])throw new Error("Value for min"+a+" can not be greater than max"+a)}c("Height"),c("Width"),b("maxHeight"),b("minHeight"),b("maxWidth"),b("minWidth")}function e(){var a=c&&c.id||S.id+F++;return null!==document.getElementById(a)&&(a+=F++),a}function f(b){return R=b,""===b&&(a.id=b=e(),G=(c||{}).log,R=b,h(b,"Added missing iframe ID: "+b+" ("+a.src+")")),b}function g(){h(w,"IFrame scrolling "+(P[w].scrolling?"enabled":"disabled")+" for "+w),a.style.overflow=!1===P[w].scrolling?"hidden":"auto",a.scrolling=!1===P[w].scrolling?"no":"yes"}function i(){("number"==typeof P[w].bodyMargin||"0"===P[w].bodyMargin)&&(P[w].bodyMarginV1=P[w].bodyMargin,P[w].bodyMargin=""+P[w].bodyMargin+"px")}function k(){var b=P[w].firstRun,c=P[w].heightCalculationMethod in O;!b&&c&&r({iframe:a,height:0,width:0,type:"init"})}function l(){Function.prototype.bind&&(P[w].iframe.iFrameResizer={close:n.bind(null,P[w].iframe),resize:u.bind(null,"Window resize","resize",P[w].iframe),moveToAnchor:function(a){u("Move to anchor","inPageLink:"+a,P[w].iframe,w)},sendMessage:function(a){a=JSON.stringify(a),u("Send Message","message:"+a,P[w].iframe,w)}})}function m(c){function d(){u("iFrame.onload",c,a),k()}b(a,"load",d),u("init",c,a)}function o(a){if("object"!=typeof a)throw new TypeError("Options is not an object")}function p(a){for(var b in S)S.hasOwnProperty(b)&&(P[w][b]=a.hasOwnProperty(b)?a[b]:S[b])}function q(a){return""===a||"file://"===a?"*":a}function s(b){b=b||{},P[w]={firstRun:!0,iframe:a,remoteHost:a.src.split("/").slice(0,3).join("/")},o(b),p(b),P[w].targetOrigin=!0===P[w].checkOrigin?q(P[w].remoteHost):"*"}function t(){return w in P&&"iFrameResizer"in a}var w=f(a.id);t()?j(w,"Ignored iFrame, already setup."):(s(c),g(),d(),i(),m(v(w)),l())}function x(a,b){null===Q&&(Q=setTimeout(function(){Q=null,a()},b))}function y(){function b(){function a(a){function b(b){return"0px"===P[a].iframe.style[b]}function c(a){return null!==a.offsetParent}c(P[a].iframe)&&(b("height")||b("width"))&&u("Visibility change","resize",P[a].iframe,a)}for(var b in P)a(b)}function c(a){h("window","Mutation observed: "+a[0].target+" "+a[0].type),x(b,16)}function d(){var a=document.querySelector("body"),b={attributes:!0,attributeOldValue:!1,characterData:!0,characterDataOldValue:!1,childList:!0,subtree:!0},d=new e(c);d.observe(a,b)}var e=a.MutationObserver||a.WebKitMutationObserver;e&&d()}function z(a){function b(){B("Window "+a,"resize")}h("window","Trigger event: "+a),x(b,16)}function A(){function a(){B("Tab Visable","resize")}"hidden"!==document.visibilityState&&(h("document","Trigger event: Visiblity change"),x(a,16))}function B(a,b){function c(a){return"parent"===P[a].resizeFrom&&P[a].autoResize&&!P[a].firstRun}for(var d in P)c(d)&&u(a,b,document.getElementById(d),d)}function C(){b(a,"message",l),b(a,"resize",function(){z("resize")}),b(document,"visibilitychange",A),b(document,"-webkit-visibilitychange",A),b(a,"focusin",function(){z("focus")}),b(a,"focus",function(){z("focus")})}function D(){function a(a,c){function d(){if(!c.tagName)throw new TypeError("Object is not a valid DOM element");if("IFRAME"!==c.tagName.toUpperCase())throw new TypeError("Expected + + + + + + + + + + + + + \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/i18n/loadi18nApp_iui-route_view.js b/apiroute/apiroute-service/src/main/resources/iui-route/i18n/loadi18nApp_iui-route_view.js new file mode 100644 index 0000000..ad8dc39 --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/iui-route/i18n/loadi18nApp_iui-route_view.js @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) + * + * Licensed 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. + */ +function loadPropertiesSideMenu(lang, propertiesFileNamePrefix, propertiesFilePath){ + jQuery.i18n.properties({ + // language:lang, + name:propertiesFileNamePrefix, + path:propertiesFilePath, // 资源文件路径 + mode:'map', // 用 Map 的方式使用资源文件中的值 + callback: function() {// 加载成功后设置显示内容 + + var i18nItems = $('[name_i18n=org_openo_msb_route_ui_i18n]'); + for(var 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 literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/img/conductor-logo.png b/apiroute/apiroute-service/src/main/resources/iui-route/img/conductor-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5d78c453bc85cecc57036c36b14458860f5c233f GIT binary patch literal 7470 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 literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/img/details_close.png b/apiroute/apiroute-service/src/main/resources/iui-route/img/details_close.png new file mode 100644 index 0000000000000000000000000000000000000000..e6281bae00a93ba76b71e759e42845a827976362 GIT binary patch literal 3300 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 literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/img/loading-spinner-grey.gif b/apiroute/apiroute-service/src/main/resources/iui-route/img/loading-spinner-grey.gif new file mode 100644 index 0000000000000000000000000000000000000000..6d614d3da4cc1fe2e7d7327e07319dfc61b1c98d GIT binary patch literal 5203 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%0A{|AOlb=G#ZaSjR@?-ut#R}83-uE}smLaV%BO#dZ h3MmYzkS#|3I{@-ma?2T(T%rH~002ovPDHLkV1l7bb_)Oi literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/img/netnumenLogo.png b/apiroute/apiroute-service/src/main/resources/iui-route/img/netnumenLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..289a8f77e6de655700419d0641eb94ad98408b55 GIT binary patch literal 5639 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 Fe*qJ%6a@eP literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/img/throbber.gif b/apiroute/apiroute-service/src/main/resources/iui-route/img/throbber.gif new file mode 100644 index 0000000000000000000000000000000000000000..06393889242fb3ea9e0205fa84369ec7bb66d15a GIT binary patch literal 9257 zcmd^^X;@R|x`tQg5wbE8AV3mAn1TjmQ&en2CK8~ENEH<+P_)pZ24y2E+7O0>K^a6u zQ3;5MiU^7p6*M3qDk!2=YEcHMQ>nzEYP;R`e2C@r+U+?#XaC*&gKPcB#k$`o&;7mu zYNhYYXe|Uo84#4ZIko#rcU5K8*yFL{qT47O&^5fZH$ zVZ@%(l~vVHjnm;H@KL8@r%yUHoo;rbHI_4lIH(_nsTT>S2`DFOD~uCb9_dF4`#QgI zy7ldMcLs+A_s%|e1pRPrbX-tpeNP!9(IpMFTce`t_5U%lP99z%&i6`1d~ zWeM!Rxc50<+d$e^9LT`?B+aMK~apR zHm?q;p<7{wN2g|I^aGlSws;VP84j(z%aQwvAWv83Z$}p(% zZ^?2;gxg(ey_`V5J7{;!o;o;KslW@z5EP~JGs|U)J7dF&(ff#A=6vU?cGQ$-4+;Jf z-ggJEa!yStn`_EWvl)#yhm6XVs}UUbsi;+agri;mCfjH^Uy;lH+Zw^h)4N?oZgZz4 zJk(fTZ|Bi^;+s_M=~+d#vyoxEPzTlOS=mX@sbl*uRj>=MaMr}cFIY8i?UM61>86uB zV$DlOUCiUJwbzJMP@D$urzK|lL2-PC!p1l47V-ZG<5Ev0Z5h~Kx?`KOp7gkAjV93A z-Gc7MrlxTf?wF;CbNc@tCHJH{TB3c;#{SVu%97}tyAM2n&|9W_?qv}$*Jt*%7Yxb# zV0;d;7|lDEltJYS+U)#aiJO};?_Jyy_4%syQ(uy?-J-Yx-9O5nKRk@@XSS~X<(2u~ zV-LamWm~!iqtH9wkpf8mAXZhOD&L#aA_%)4h2M;1M5jt zIR>Us+%W-GXa_f^opKg=DSrAs)AXeRa;Hp0aC1OgbxQ%Qr_QvTleM1jkR!2mkcX$3 ztsR8~G9iqh(-FJ@F_rQBIYDXV_6s7G9SxaVF^laZqcx$!D97m|7t16j6@Jt6UdDRy49Qyvs|c>RuA|@b%}`*wU}2^7q;&Vtc6@lb zcXl)T!6nYDzmMJ~%n$KNXyNlCG)GkJ4!82;v6@d3>s5r~E+3!O?049JDr14Y^PeMI02R`0lJ^=oJ zYd|*u9|SU(j7hY?+<=(?fP*mtV*zFhOrz6%{VA?ozdm&(Jf^V zMfPZ?>l`mS3{Uq8IM;e!+1YjJy2!mzK$O|wPeU{*QSbs9m+@`f5KxO3PBnQ=%RsZg%go*fJ`*w9TL{-WgZVIA$!YV}3BRcfeXaR$x#b zW)Tpd#8E4)^MyYdkH;4_;ChJuw%n+Be7Ko4;w-nHvyo$d_0e-YiF78Df&)_)(}fcr_r0mPH(4RRYWIu+d@t0&Ss@O^s! zOKyX&13)%N@83r^;QsgN{rl(!0|RF1FA)b1{CRXAy&1ySz@>olPiR4r$aMdq&_=nK zq|cFs8phWJ1@%dZ-gXd{zDbTILD>)qEvH-NU*Rf1b2J1Ri79`rBFl@ z8E^0I)OqEi{pH(a24b9YPG;Kz@t-qZW;3Mpe`MRlmYx{7bH-XZ&`RQ7Rb^%}gc&X| zd}Q-FZf|RWxHU?PR!(C?80zu(^l>*h{#ulSiid(O!J(8P-41bNM3tnX@U6NS5yo0? zdcF)~xFE&+&|gZ$23dV5t~?$$&ymZ;F8j7GGMncGSsDo%>J`26=&l=X#rSKv_64;0 zr;k6no@=gV`P)K!=kaHl>q?!`X>(A;84tg^Md<`zA%qbRLby1Z=fn*ZRdNqs%Tq|3 zOt}lZu0q9oKJhgz&+^7PCt$=UFW=R*w?a1)ePoL*`R$Gxj?TU@12tTHsT$giHQU+sqf;fS0FpT!< z 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 Y06eiBG8L{6am}6+T0JMVdrp~7I%7KNwE5&S zmQ&6;&%EHj;8Nt$t5M6YMlZV>v+P>J%4&iY$<8*k@tyq&l4R{q9Y z1)FXaZoXZz`c+!!_OK$(2d+OQR%ddCde0%NX z*Q+nS+<5i%@rNJpzWsjt_1C^T@2Dg7%&VGp!k!8k%7UTK?mes zP@FKZFKIC6=H_Z{=IUteXzJjWGPGu9ayF4{na;(h&FVIH9-FQ}8@J}XdF=D_xH-8b z?Uz~Us2bS0amch9EwdEulxK0UXtABgAluR@CaKZEJkLgA`eNahb8e2}yq)c7cV@ X5{G0%f=4g!ojjGGmYlo24h+@+45`r6 literal 0 HcmV?d00001 diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/index.html b/apiroute/apiroute-service/src/main/resources/iui-route/index.html new file mode 100644 index 0000000..92af3ca --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/iui-route/index.html @@ -0,0 +1,1039 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+
+ + + +
+ + + + +
+ + + +
+ +
+
+ + +
+
+ + {{route.version}} + +
+ + + + + + + + + + + + +
+ +
+ +
+ + +
+
+
+ + +
+ + + +
+ + + +
+ +
+
+ + +
+
+ + {{route.servers[0].ip}}:{{route.servers[0].port}} +
+ + + + + + + + + +
+ +
+ +
+ +
+ +
+
+ + +
+ + + +
+ + + + +
+
+ {{routeUtil.showGroupName($index,el)}} + {{customGroupRouteArray[$index].length}} + +
+ +
+
+
+
+
+
+ + +
+
+ + {{elem.servers[0].ip}}:{{elem.servers[0].port}} +
+ + + + + + + + + +
+ +
+
+ +
+ + + +
+ +
+ + +
+
+ + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{node.ip}}:{{node.port}}:{{node.ttl}} + {{msb.url==""?"/":msb.url}} + + + + +
+ +
+ + + + + +
+ +
+ +
+ + + +
+
+ +
+
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ + + +
+
+ + +
+ +
+
+ + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/js/avalon.js b/apiroute/apiroute-service/src/main/resources/iui-route/js/avalon.js new file mode 100644 index 0000000..a22bb7d --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/iui-route/js/avalon.js @@ -0,0 +1,5819 @@ +/*================================================== + Copyright (c) 2013-2015 司徒正美 and other contributors + http://www.cnblogs.com/rubylouvre/ + https://github.com/RubyLouvre + http://weibo.com/jslouvre/ + + Released under the MIT license + avalon.js 1.45 built in 2015.7.17 + support IE6+ and other browsers + ==================================================*/ +(function(global, factory) { + + if (typeof module === "object" && typeof module.exports === "object") { + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get avalon. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var avalon = require("avalon")(window); + module.exports = global.document ? factory(global, true) : function(w) { + if (!w.document) { + throw new Error("Avalon requires a window with a document") + } + return factory(w) + } + } else { + factory(global) + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function(window, noGlobal){ + +/********************************************************************* + * 全局变量及方法 * + **********************************************************************/ +var expose = new Date() - 0 +//http://stackoverflow.com/questions/7290086/javascript-use-strict-and-nicks-find-global-function +var DOC = window.document +var head = DOC.getElementsByTagName("head")[0] //HEAD元素 +var ifGroup = head.insertBefore(document.createElement("avalon"), head.firstChild) //避免IE6 base标签BUG +ifGroup.innerHTML = "X" +ifGroup.setAttribute("ms-skip", "1") +ifGroup.className = "avalonHide" +var rnative = /\[native code\]/ //判定是否原生函数 +function log() { + if (window.console && avalon.config.debug) { + // http://stackoverflow.com/questions/8785624/how-to-safely-wrap-console-log + Function.apply.call(console.log, console, arguments) + } +} + + +var subscribers = "$" + expose +var otherRequire = window.require +var otherDefine = window.define +var innerRequire +var stopRepeatAssign = false +var rword = /[^, ]+/g //切割字符串为一个个小块,以空格或豆号分开它们,结合replace实现字符串的forEach +var rcomplexType = /^(?:object|array)$/ +var rsvg = /^\[object SVG\w*Element\]$/ +var rwindow = /^\[object (?:Window|DOMWindow|global)\]$/ +var oproto = Object.prototype +var ohasOwn = oproto.hasOwnProperty +var serialize = oproto.toString +var ap = Array.prototype +var aslice = ap.slice +var Registry = {} //将函数曝光到此对象上,方便访问器收集依赖 +var W3C = window.dispatchEvent +var root = DOC.documentElement +var avalonFragment = DOC.createDocumentFragment() +var cinerator = DOC.createElement("div") +var class2type = {} +"Boolean Number String Function Array Date RegExp Object Error".replace(rword, function (name) { + class2type["[object " + name + "]"] = name.toLowerCase() +}) + + +function noop() { +} + + +function oneObject(array, val) { + if (typeof array === "string") { + array = array.match(rword) || [] + } + var result = {}, + value = val !== void 0 ? val : 1 + for (var i = 0, n = array.length; i < n; i++) { + result[array[i]] = value + } + return result +} + +//生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript +var generateID = function (prefix) { + prefix = prefix || "avalon" + return String(Math.random() + Math.random()).replace(/\d\.\d{4}/, prefix) +} +function IE() { + if (window.VBArray) { + var mode = document.documentMode + return mode ? mode : window.XMLHttpRequest ? 7 : 6 + } else { + return NaN + } +} +var IEVersion = IE() + +avalon = function (el) { //创建jQuery式的无new 实例化结构 + return new avalon.init(el) +} + +avalon.profile = function () { + if (window.console && avalon.config.profile) { + Function.apply.call(console.log, console, arguments) + } +} + +/*视浏览器情况采用最快的异步回调*/ +avalon.nextTick = new function () {// jshint ignore:line + var tickImmediate = window.setImmediate + var tickObserver = window.MutationObserver + var tickPost = W3C && window.postMessage + if (tickImmediate) { + return tickImmediate.bind(window) + } + + var queue = [] + function callback() { + var n = queue.length + for (var i = 0; i < n; i++) { + queue[i]() + } + queue = queue.slice(n) + } + + if (tickObserver) { + var node = document.createTextNode("avalon") + new tickObserver(callback).observe(node, {characterData: true})// jshint ignore:line + return function (fn) { + queue.push(fn) + node.data = Math.random() + } + } + + if (tickPost) { + window.addEventListener("message", function (e) { + var source = e.source + if ((source === window || source === null) && e.data === "process-tick") { + e.stopPropagation() + callback() + } + }) + + return function (fn) { + queue.push(fn) + window.postMessage('process-tick', '*') + } + } + + return function (fn) { + setTimeout(fn, 0) + } +}// jshint ignore:line +/********************************************************************* + * avalon的静态方法定义区 * + **********************************************************************/ +avalon.init = function (el) { + this[0] = this.element = el +} +avalon.fn = avalon.prototype = avalon.init.prototype + +avalon.type = function (obj) { //取得目标的类型 + if (obj == null) { + return String(obj) + } + // 早期的webkit内核浏览器实现了已废弃的ecma262v4标准,可以将正则字面量当作函数使用,因此typeof在判定正则时会返回function + return typeof obj === "object" || typeof obj === "function" ? + class2type[serialize.call(obj)] || "object" : + typeof obj +} + +var isFunction = typeof alert === "object" ? function (fn) { + try { + return /^\s*\bfunction\b/.test(fn + "") + } catch (e) { + return false + } +} : function (fn) { + return serialize.call(fn) === "[object Function]" +} +avalon.isFunction = isFunction + +avalon.isWindow = function (obj) { + if (!obj) + return false + // 利用IE678 window == document为true,document == window竟然为false的神奇特性 + // 标准浏览器及IE9,IE10等使用 正则检测 + return obj == obj.document && obj.document != obj //jshint ignore:line +} + +function isWindow(obj) { + return rwindow.test(serialize.call(obj)) +} +if (isWindow(window)) { + avalon.isWindow = isWindow +} +var enu +for (enu in avalon({})) { + break +} +var enumerateBUG = enu !== "0" //IE6下为true, 其他为false +/*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/ +avalon.isPlainObject = function (obj, key) { + if (!obj || avalon.type(obj) !== "object" || obj.nodeType || avalon.isWindow(obj)) { + return false; + } + try { //IE内置对象没有constructor + if (obj.constructor && !ohasOwn.call(obj, "constructor") && !ohasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { + return false; + } + } catch (e) { //IE8 9会在这里抛错 + return false; + } + if (enumerateBUG) { + for (key in obj) { + return ohasOwn.call(obj, key) + } + } + for (key in obj) { + } + return key === void 0 || ohasOwn.call(obj, key) +} +if (rnative.test(Object.getPrototypeOf)) { + avalon.isPlainObject = function (obj) { + // 简单的 typeof obj === "object"检测,会致使用isPlainObject(window)在opera下通不过 + return serialize.call(obj) === "[object Object]" && Object.getPrototypeOf(obj) === oproto + } +} +//与jQuery.extend方法,可用于浅拷贝,深拷贝 +avalon.mix = avalon.fn.mix = function () { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false + + // 如果第一个参数为布尔,判定是否深拷贝 + if (typeof target === "boolean") { + deep = target + target = arguments[1] || {} + i++ + } + + //确保接受方为一个复杂的数据类型 + if (typeof target !== "object" && !isFunction(target)) { + target = {} + } + + //如果只有一个参数,那么新成员添加于mix所在的对象上 + if (i === length) { + target = this + i-- + } + + for (; i < length; i++) { + //只处理非空参数 + if ((options = arguments[i]) != null) { + for (name in options) { + src = target[name] + try { + copy = options[name] //当options为VBS对象时报错 + } catch (e) { + continue + } + + // 防止环引用 + if (target === copy) { + continue + } + if (deep && copy && (avalon.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { + + if (copyIsArray) { + copyIsArray = false + clone = src && Array.isArray(src) ? src : [] + + } else { + clone = src && avalon.isPlainObject(src) ? src : {} + } + + target[name] = avalon.mix(deep, clone, copy) + } else if (copy !== void 0) { + target[name] = copy + } + } + } + } + return target +} + +function _number(a, len) { //用于模拟slice, splice的效果 + a = Math.floor(a) || 0 + return a < 0 ? Math.max(len + a, 0) : Math.min(a, len); +} +avalon.mix({ + rword: rword, + subscribers: subscribers, + version: 1.45, + ui: {}, + log: log, + slice: W3C ? function (nodes, start, end) { + return aslice.call(nodes, start, end) + } : function (nodes, start, end) { + var ret = [] + var len = nodes.length + if (end === void 0) + end = len + if (typeof end === "number" && isFinite(end)) { + start = _number(start, len) + end = _number(end, len) + for (var i = start; i < end; ++i) { + ret[i - start] = nodes[i] + } + } + return ret + }, + noop: noop, + /*如果不用Error对象封装一下,str在控制台下可能会乱码*/ + error: function (str, e) { + throw (e || Error)(str) + }, + /*将一个以空格或逗号隔开的字符串或数组,转换成一个键值都为1的对象*/ + oneObject: oneObject, + /* avalon.range(10) + => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + avalon.range(1, 11) + => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + avalon.range(0, 30, 5) + => [0, 5, 10, 15, 20, 25] + avalon.range(0, -10, -1) + => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + avalon.range(0) + => []*/ + range: function (start, end, step) { // 用于生成整数数组 + step || (step = 1) + if (end == null) { + end = start || 0 + start = 0 + } + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = new Array(length) + while (++index < length) { + result[index] = start + start += step + } + return result + }, + eventHooks: [], + /*绑定事件*/ + bind: function(el, type, fn, phase) { + var hooks = avalon.eventHooks + var hook = hooks[type] + if (typeof hook === "object") { + type = hook.type + if (hook.deel) { + fn = hook.deel(el, type, fn, phase) + } + } + var callback = W3C ? fn : function(e) { + fn.call(el, fixEvent(e)); + } + if (W3C) { + el.addEventListener(type, callback, !!phase) + } else { + el.attachEvent("on" + type, callback) + } + return callback + }, + /*卸载事件*/ + unbind: function(el, type, fn, phase) { + var hooks = avalon.eventHooks + var hook = hooks[type] + var callback = fn || noop + if (typeof hook === "object") { + type = hook.type + if (hook.deel) { + fn = hook.deel(el, type, fn, false) + } + } + if (W3C) { + el.removeEventListener(type, callback, !!phase) + } else { + el.detachEvent("on" + type, callback) + } + }, + /*读写删除元素节点的样式*/ + css: function (node, name, value) { + if (node instanceof avalon) { + node = node[0] + } + var prop = /[_-]/.test(name) ? camelize(name) : name, fn + name = avalon.cssName(prop) || prop + if (value === void 0 || typeof value === "boolean") { //获取样式 + fn = cssHooks[prop + ":get"] || cssHooks["@:get"] + if (name === "background") { + name = "backgroundColor" + } + var val = fn(node, name) + return value === true ? parseFloat(val) || 0 : val + } else if (value === "") { //请除样式 + node.style[name] = "" + } else { //设置样式 + if (value == null || value !== value) { + return + } + if (isFinite(value) && !avalon.cssNumber[prop]) { + value += "px" + } + fn = cssHooks[prop + ":set"] || cssHooks["@:set"] + fn(node, name, value) + } + }, + /*遍历数组与对象,回调的第一个参数为索引或键名,第二个或元素或键值*/ + each: function (obj, fn) { + if (obj) { //排除null, undefined + var i = 0 + if (isArrayLike(obj)) { + for (var n = obj.length; i < n; i++) { + if (fn(i, obj[i]) === false) + break + } + } else { + for (i in obj) { + if (obj.hasOwnProperty(i) && fn(i, obj[i]) === false) { + break + } + } + } + } + }, + //收集元素的data-{{prefix}}-*属性,并转换为对象 + getWidgetData: function (elem, prefix) { + var raw = avalon(elem).data() + var result = {} + for (var i in raw) { + if (i.indexOf(prefix) === 0) { + result[i.replace(prefix, "").replace(/\w/, function (a) { + return a.toLowerCase() + })] = raw[i] + } + } + return result + }, + Array: { + /*只有当前数组不存在此元素时只添加它*/ + ensure: function (target, item) { + if (target.indexOf(item) === -1) { + return target.push(item) + } + }, + /*移除数组中指定位置的元素,返回布尔表示成功与否*/ + removeAt: function (target, index) { + return !!target.splice(index, 1).length + }, + /*移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否*/ + remove: function (target, item) { + var index = target.indexOf(item) + if (~index) + return avalon.Array.removeAt(target, index) + return false + } + } +}) + +var bindingHandlers = avalon.bindingHandlers = {} +var bindingExecutors = avalon.bindingExecutors = {} + +/*判定是否类数组,如节点集合,纯数组,arguments与拥有非负整数的length属性的纯JS对象*/ +function isArrayLike(obj) { + if (!obj) + return false + var n = obj.length + if (n === (n >>> 0)) { //检测length属性是否为非负整数 + var type = serialize.call(obj).slice(8, -1) + if (/(?:regexp|string|function|window|global)$/i.test(type)) + return false + if (type === "Array") + return true + try { + if ({}.propertyIsEnumerable.call(obj, "length") === false) { //如果是原生对象 + return /^\s?function/.test(obj.item || obj.callee) + } + return true + } catch (e) { //IE的NodeList直接抛错 + return !obj.window //IE6-8 window + } + } + return false +} + + +// https://github.com/rsms/js-lru +var Cache = new function() {// jshint ignore:line + function LRU(maxLength) { + this.size = 0 + this.limit = maxLength + this.head = this.tail = void 0 + this._keymap = {} + } + + var p = LRU.prototype + + p.put = function(key, value) { + var entry = { + key: key, + value: value + } + this._keymap[key] = entry + if (this.tail) { + this.tail.newer = entry + entry.older = this.tail + } else { + this.head = entry + } + this.tail = entry + if (this.size === this.limit) { + this.shift() + } else { + this.size++ + } + return value + } + + p.shift = function() { + var entry = this.head + if (entry) { + this.head = this.head.newer + this.head.older = + entry.newer = + entry.older = + this._keymap[entry.key] = void 0 + } + } + p.get = function(key) { + var entry = this._keymap[key] + if (entry === void 0) + return + if (entry === this.tail) { + return entry.value + } + // HEAD--------------TAIL + // <.older .newer> + // <--- add direction -- + // A B C E + if (entry.newer) { + if (entry === this.head) { + this.head = entry.newer + } + entry.newer.older = entry.older // C <-- E. + } + if (entry.older) { + entry.older.newer = entry.newer // C. --> E + } + entry.newer = void 0 // D --x + entry.older = this.tail // D. --> E + if (this.tail) { + this.tail.newer = entry // E. <-- D + } + this.tail = entry + return entry.value + } + return LRU +}// jshint ignore:line + +/********************************************************************* + * javascript 底层补丁 * + **********************************************************************/ +if (!"司徒正美".trim) { + var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g + String.prototype.trim = function () { + return this.replace(rtrim, "") + } +} +var hasDontEnumBug = !({ + 'toString': null +}).propertyIsEnumerable('toString'), + hasProtoEnumBug = (function () { + }).propertyIsEnumerable('prototype'), + dontEnums = [ + "toString", + "toLocaleString", + "valueOf", + "hasOwnProperty", + "isPrototypeOf", + "propertyIsEnumerable", + "constructor" + ], + dontEnumsLength = dontEnums.length; +if (!Object.keys) { + Object.keys = function (object) { //ecma262v5 15.2.3.14 + var theKeys = [] + var skipProto = hasProtoEnumBug && typeof object === "function" + if (typeof object === "string" || (object && object.callee)) { + for (var i = 0; i < object.length; ++i) { + theKeys.push(String(i)) + } + } else { + for (var name in object) { + if (!(skipProto && name === "prototype") && ohasOwn.call(object, name)) { + theKeys.push(String(name)) + } + } + } + + if (hasDontEnumBug) { + var ctor = object.constructor, + skipConstructor = ctor && ctor.prototype === object + for (var j = 0; j < dontEnumsLength; j++) { + var dontEnum = dontEnums[j] + if (!(skipConstructor && dontEnum === "constructor") && ohasOwn.call(object, dontEnum)) { + theKeys.push(dontEnum) + } + } + } + return theKeys + } +} +if (!Array.isArray) { + Array.isArray = function (a) { + return serialize.call(a) === "[object Array]" + } +} + +if (!noop.bind) { + Function.prototype.bind = function (scope) { + if (arguments.length < 2 && scope === void 0) + return this + var fn = this, + argv = arguments + return function () { + var args = [], + i + for (i = 1; i < argv.length; i++) + args.push(argv[i]) + for (i = 0; i < arguments.length; i++) + args.push(arguments[i]) + return fn.apply(scope, args) + } + } +} + +function iterator(vars, body, ret) { + var fun = 'for(var ' + vars + 'i=0,n = this.length; i < n; i++){' + body.replace('_', '((i in this) && fn.call(scope,this[i],i,this))') + '}' + ret + /* jshint ignore:start */ + return Function("fn,scope", fun) + /* jshint ignore:end */ +} +if (!rnative.test([].map)) { + avalon.mix(ap, { + //定位操作,返回数组中第一个等于给定参数的元素的索引值。 + indexOf: function (item, index) { + var n = this.length, + i = ~~index + if (i < 0) + i += n + for (; i < n; i++) + if (this[i] === item) + return i + return -1 + }, + //定位操作,同上,不过是从后遍历。 + lastIndexOf: function (item, index) { + var n = this.length, + i = index == null ? n - 1 : index + if (i < 0) + i = Math.max(0, n + i) + for (; i >= 0; i--) + if (this[i] === item) + return i + return -1 + }, + //迭代操作,将数组的元素挨个儿传入一个函数中执行。Prototype.js的对应名字为each。 + forEach: iterator("", '_', ""), + //迭代类 在数组中的每个项上运行一个函数,如果此函数的值为真,则此元素作为新数组的元素收集起来,并返回新数组 + filter: iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'), + //收集操作,将数组的元素挨个儿传入一个函数中执行,然后把它们的返回值组成一个新数组返回。Prototype.js的对应名字为collect。 + map: iterator('r=[],', 'r[i]=_', 'return r'), + //只要数组中有一个元素满足条件(放进给定函数返回true),那么它就返回true。Prototype.js的对应名字为any。 + some: iterator("", 'if(_)return true', 'return false'), + //只有数组中的元素都满足条件(放进给定函数返回true),它才返回true。Prototype.js的对应名字为all。 + every: iterator("", 'if(!_)return false', 'return true') + }) +} +/********************************************************************* + * DOM 底层补丁 * + **********************************************************************/ + +function fixContains(root, el) { + try { //IE6-8,游离于DOM树外的文本节点,访问parentNode有时会抛错 + while ((el = el.parentNode)) + if (el === root) + return true + return false + } catch (e) { + return false + } +} +avalon.contains = fixContains +//IE6-11的文档对象没有contains +if (!DOC.contains) { + DOC.contains = function (b) { + return fixContains(DOC, b) + } +} + +function outerHTML() { + return new XMLSerializer().serializeToString(this) +} + +if (window.SVGElement) { + //safari5+是把contains方法放在Element.prototype上而不是Node.prototype + if (!DOC.createTextNode("x").contains) { + Node.prototype.contains = function (arg) {//IE6-8没有Node对象 + return !!(this.compareDocumentPosition(arg) & 16) + } + } + var svgns = "http://www.w3.org/2000/svg" + var svg = DOC.createElementNS(svgns, "svg") + svg.innerHTML = '' + if (!rsvg.test(svg.firstChild)) { // #409 + function enumerateNode(node, targetNode) {// jshint ignore:line + if (node && node.childNodes) { + var nodes = node.childNodes + for (var i = 0, el; el = nodes[i++]; ) { + if (el.tagName) { + var svg = DOC.createElementNS(svgns, + el.tagName.toLowerCase()) + ap.forEach.call(el.attributes, function (attr) { + svg.setAttribute(attr.name, attr.value) //复制属性 + })// jshint ignore:line + // 递归处理子节点 + enumerateNode(el, svg) + targetNode.appendChild(svg) + } + } + } + } + Object.defineProperties(SVGElement.prototype, { + "outerHTML": {//IE9-11,firefox不支持SVG元素的innerHTML,outerHTML属性 + enumerable: true, + configurable: true, + get: outerHTML, + set: function (html) { + var tagName = this.tagName.toLowerCase(), + par = this.parentNode, + frag = avalon.parseHTML(html) + // 操作的svg,直接插入 + if (tagName === "svg") { + par.insertBefore(frag, this) + // svg节点的子节点类似 + } else { + var newFrag = DOC.createDocumentFragment() + enumerateNode(frag, newFrag) + par.insertBefore(newFrag, this) + } + par.removeChild(this) + } + }, + "innerHTML": { + enumerable: true, + configurable: true, + get: function () { + var s = this.outerHTML + var ropen = new RegExp("<" + this.nodeName + '\\b(?:(["\'])[^"]*?(\\1)|[^>])*>', "i") + var rclose = new RegExp("<\/" + this.nodeName + ">$", "i") + return s.replace(ropen, "").replace(rclose, "") + }, + set: function (html) { + if (avalon.clearHTML) { + avalon.clearHTML(this) + var frag = avalon.parseHTML(html) + enumerateNode(frag, this) + } + } + } + }) + } +} +if (!root.outerHTML && window.HTMLElement) { //firefox 到11时才有outerHTML + HTMLElement.prototype.__defineGetter__("outerHTML", outerHTML); +} + + +//============================= event binding ======================= +var rmouseEvent = /^(?:mouse|contextmenu|drag)|click/ +function fixEvent(event) { + var ret = {} + for (var i in event) { + ret[i] = event[i] + } + var target = ret.target = event.srcElement + if (event.type.indexOf("key") === 0) { + ret.which = event.charCode != null ? event.charCode : event.keyCode + } else if (rmouseEvent.test(event.type)) { + var doc = target.ownerDocument || DOC + var box = doc.compatMode === "BackCompat" ? doc.body : doc.documentElement + ret.pageX = event.clientX + (box.scrollLeft >> 0) - (box.clientLeft >> 0) + ret.pageY = event.clientY + (box.scrollTop >> 0) - (box.clientTop >> 0) + ret.wheelDeltaY = ret.wheelDelta + ret.wheelDeltaX = 0 + } + ret.timeStamp = new Date() - 0 + ret.originalEvent = event + ret.preventDefault = function () { //阻止默认行为 + event.returnValue = false + } + ret.stopPropagation = function () { //阻止事件在DOM树中的传播 + event.cancelBubble = true + } + return ret +} + +var eventHooks = avalon.eventHooks +//针对firefox, chrome修正mouseenter, mouseleave +if (!("onmouseenter" in root)) { + avalon.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" + }, function (origType, fixType) { + eventHooks[origType] = { + type: fixType, + deel: function (elem, _, fn) { + return function (e) { + var t = e.relatedTarget + if (!t || (t !== elem && !(elem.compareDocumentPosition(t) & 16))) { + delete e.type + e.type = origType + return fn.call(elem, e) + } + } + } + } + }) +} +//针对IE9+, w3c修正animationend +avalon.each({ + AnimationEvent: "animationend", + WebKitAnimationEvent: "webkitAnimationEnd" +}, function (construct, fixType) { + if (window[construct] && !eventHooks.animationend) { + eventHooks.animationend = { + type: fixType + } + } +}) +//针对IE6-8修正input +if (!("oninput" in DOC.createElement("input"))) { + eventHooks.input = { + type: "propertychange", + deel: function (elem, _, fn) { + return function (e) { + if (e.propertyName === "value") { + e.type = "input" + return fn.call(elem, e) + } + } + } + } +} +if (DOC.onmousewheel === void 0) { + /* IE6-11 chrome mousewheel wheelDetla 下 -120 上 120 + firefox DOMMouseScroll detail 下3 上-3 + firefox wheel detlaY 下3 上-3 + IE9-11 wheel deltaY 下40 上-40 + chrome wheel deltaY 下100 上-100 */ + var fixWheelType = DOC.onwheel !== void 0 ? "wheel" : "DOMMouseScroll" + var fixWheelDelta = fixWheelType === "wheel" ? "deltaY" : "detail" + eventHooks.mousewheel = { + type: fixWheelType, + deel: function (elem, _, fn) { + return function (e) { + e.wheelDeltaY = e.wheelDelta = e[fixWheelDelta] > 0 ? -120 : 120 + e.wheelDeltaX = 0 + if (Object.defineProperty) { + Object.defineProperty(e, "type", { + value: "mousewheel" + }) + } + fn.call(elem, e) + } + } + } +} + + + +/********************************************************************* + * 配置系统 * + **********************************************************************/ + +function kernel(settings) { + for (var p in settings) { + if (!ohasOwn.call(settings, p)) + continue + var val = settings[p] + if (typeof kernel.plugins[p] === "function") { + kernel.plugins[p](val) + } else if (typeof kernel[p] === "object") { + avalon.mix(kernel[p], val) + } else { + kernel[p] = val + } + } + return this +} +var openTag, closeTag, rexpr, rexprg, rbind, rregexp = /[-.*+?^${}()|[\]\/\\]/g + +function escapeRegExp(target) { + //http://stevenlevithan.com/regex/xregexp/ + //将字符串安全格式化为正则表达式的源码 + return (target + "").replace(rregexp, "\\$&") +} + +var plugins = { + loader: function (builtin) { + var flag = innerRequire && builtin + window.require = flag ? innerRequire : otherRequire + window.define = flag ? innerRequire.define : otherDefine + }, + interpolate: function (array) { + openTag = array[0] + closeTag = array[1] + if (openTag === closeTag) { + throw new SyntaxError("openTag!==closeTag") + var test = openTag + "test" + closeTag + cinerator.innerHTML = test + if (cinerator.innerHTML !== test && cinerator.innerHTML.indexOf("<") > -1) { + throw new SyntaxError("此定界符不合法") + } + cinerator.innerHTML = "" + } + var o = escapeRegExp(openTag), + c = escapeRegExp(closeTag) + rexpr = new RegExp(o + "(.*?)" + c) + rexprg = new RegExp(o + "(.*?)" + c, "g") + rbind = new RegExp(o + ".*?" + c + "|\\sms-") + } +} + +kernel.debug = true +kernel.plugins = plugins +kernel.plugins['interpolate'](["{{", "}}"]) +kernel.paths = {} +kernel.shim = {} +kernel.maxRepeatSize = 100 +avalon.config = kernel +var ravalon = /(\w+)\[(avalonctrl)="(\S+)"\]/ +var findNodes = DOC.querySelectorAll ? function(str) { + return DOC.querySelectorAll(str) +} : function(str) { + var match = str.match(ravalon) + var all = DOC.getElementsByTagName(match[1]) + var nodes = [] + for (var i = 0, el; el = all[i++]; ) { + if (el.getAttribute(match[2]) === match[3]) { + nodes.push(el) + } + } + return nodes +} +/********************************************************************* + * 事件总线 * + **********************************************************************/ +var EventBus = { + $watch: function (type, callback) { + if (typeof callback === "function") { + var callbacks = this.$events[type] + if (callbacks) { + callbacks.push(callback) + } else { + this.$events[type] = [callback] + } + } else { //重新开始监听此VM的第一重简单属性的变动 + this.$events = this.$watch.backup + } + return this + }, + $unwatch: function (type, callback) { + var n = arguments.length + if (n === 0) { //让此VM的所有$watch回调无效化 + this.$watch.backup = this.$events + this.$events = {} + } else if (n === 1) { + this.$events[type] = [] + } else { + var callbacks = this.$events[type] || [] + var i = callbacks.length + while (~--i < 0) { + if (callbacks[i] === callback) { + return callbacks.splice(i, 1) + } + } + } + return this + }, + $fire: function (type) { + var special, i, v, callback + if (/^(\w+)!(\S+)$/.test(type)) { + special = RegExp.$1 + type = RegExp.$2 + } + var events = this.$events + if (!events) + return + var args = aslice.call(arguments, 1) + var detail = [type].concat(args) + if (special === "all") { + for (i in avalon.vmodels) { + v = avalon.vmodels[i] + if (v !== this) { + v.$fire.apply(v, detail) + } + } + } else if (special === "up" || special === "down") { + var elements = events.expr ? findNodes(events.expr) : [] + if (elements.length === 0) + return + for (i in avalon.vmodels) { + v = avalon.vmodels[i] + if (v !== this) { + if (v.$events.expr) { + var eventNodes = findNodes(v.$events.expr) + if (eventNodes.length === 0) { + continue + } + //循环两个vmodel中的节点,查找匹配(向上匹配或者向下匹配)的节点并设置标识 + /* jshint ignore:start */ + ap.forEach.call(eventNodes, function (node) { + ap.forEach.call(elements, function (element) { + var ok = special === "down" ? element.contains(node) : //向下捕获 + node.contains(element) //向上冒泡 + if (ok) { + node._avalon = v //符合条件的加一个标识 + } + }); + }) + /* jshint ignore:end */ + } + } + } + var nodes = DOC.getElementsByTagName("*") //实现节点排序 + var alls = [] + ap.forEach.call(nodes, function (el) { + if (el._avalon) { + alls.push(el._avalon) + el._avalon = "" + el.removeAttribute("_avalon") + } + }) + if (special === "up") { + alls.reverse() + } + for (i = 0; callback = alls[i++]; ) { + if (callback.$fire.apply(callback, detail) === false) { + break + } + } + } else { + var callbacks = events[type] || [] + var all = events.$all || [] + for (i = 0; callback = callbacks[i++]; ) { + if (isFunction(callback)) + callback.apply(this, args) + } + for (i = 0; callback = all[i++]; ) { + if (isFunction(callback)) + callback.apply(this, arguments) + } + } + } +} + +/********************************************************************* + * modelFactory * + **********************************************************************/ +//avalon最核心的方法的两个方法之一(另一个是avalon.scan),返回一个ViewModel(VM) +var VMODELS = avalon.vmodels = {} //所有vmodel都储存在这里 +avalon.define = function (id, factory) { + var $id = id.$id || id + if (!$id) { + log("warning: vm必须指定$id") + } + if (VMODELS[$id]) { + log("warning: " + $id + " 已经存在于avalon.vmodels中") + } + if (typeof id === "object") { + var model = modelFactory(id) + } else { + var scope = { + $watch: noop + } + factory(scope) //得到所有定义 + + model = modelFactory(scope) //偷天换日,将scope换为model + stopRepeatAssign = true + factory(model) + stopRepeatAssign = false + } + model.$id = $id + return VMODELS[$id] = model +} + +//一些不需要被监听的属性 +var $$skipArray = String("$id,$watch,$unwatch,$fire,$events,$model,$skipArray,$proxy,$reinitialize,$propertyNames").match(rword) +var defineProperty = Object.defineProperty +var canHideOwn = true +//如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 +//标准浏览器使用__defineGetter__, __defineSetter__实现 +try { + defineProperty({}, "_", { + value: "x" + }) + var defineProperties = Object.defineProperties +} catch (e) { + canHideOwn = false +} + +function modelFactory(source, $special, $model) { + if (Array.isArray(source)) { + var arr = source.concat() + source.length = 0 + var collection = arrayFactory(source) + collection.pushArray(arr) + return collection + } + //0 null undefined || Node || VModel(fix IE6-8 createWithProxy $val: val引发的BUG) + if (!source || source.nodeType > 0 || (source.$id && source.$events)) { + return source + } + var $skipArray = Array.isArray(source.$skipArray) ? source.$skipArray : [] + $skipArray.$special = $special || {} //强制要监听的属性 + var $vmodel = {} //要返回的对象, 它在IE6-8下可能被偷龙转凤 + $model = $model || {} //vmodels.$model属性 + var $events = {} //vmodel.$events属性 + var accessors = {} //监控属性 + var computed = [] + $$skipArray.forEach(function (name) { + delete source[name] + }) + var names = Object.keys(source) + /* jshint ignore:start */ + names.forEach(function (name, accessor) { + var val = source[name] + $model[name] = val + if (isObservable(name, val, $skipArray)) { + //总共产生三种accessor + $events[name] = [] + var valueType = avalon.type(val) + //总共产生三种accessor + if (valueType === "object" && isFunction(val.get) && Object.keys(val).length <= 2) { + accessor = makeComputedAccessor(name, val) + computed.push(accessor) + } else if (rcomplexType.test(valueType)) { + accessor = makeComplexAccessor(name, val, valueType, $events[name]) + } else { + accessor = makeSimpleAccessor(name, val) + } + accessors[name] = accessor + } + }) + /* jshint ignore:end */ + + $vmodel = defineProperties($vmodel, descriptorFactory(accessors), source) //生成一个空的ViewModel + for (var i = 0; i < names.length; i++) { + var name = names[i] + if (!accessors[name]) { + $vmodel[name] = source[name] + } + } + //添加$id, $model, $events, $watch, $unwatch, $fire + $vmodel.$propertyNames = names.join("­") + $vmodel.$id = generateID() + $vmodel.$model = $model + $vmodel.$events = $events + for (i in EventBus) { + var fn = EventBus[i] + if (!W3C) { //在IE6-8下,VB对象的方法里的this并不指向自身,需要用bind处理一下 + fn = fn.bind($vmodel) + } + $vmodel[i] = fn + } + if (canHideOwn) { + Object.defineProperty($vmodel, "hasOwnProperty", hasOwnDescriptor) + } else { + /* jshint ignore:start */ + $vmodel.hasOwnProperty = function (name) { + return name in $vmodel.$model + } + /* jshint ignore:end */ + } + + $vmodel.$reinitialize = function () { + computed.forEach(function (accessor) { + delete accessor._value + delete accessor.oldArgs + accessor.digest = function () { + accessor.call($vmodel) + } + dependencyDetection.begin({ + callback: function (vm, dependency) {//dependency为一个accessor + var name = dependency._name + if (dependency !== accessor) { + var list = vm.$events[name] + injectDependency(list, accessor.digest) + } + } + }) + try { + accessor.get.call($vmodel) + } finally { + dependencyDetection.end() + } + }) + } + $vmodel.$reinitialize() + return $vmodel +} + +var hasOwnDescriptor = { + value: function (name) { + return name in this.$model + }, + writable: false, + enumerable: false, + configurable: true +} +//创建一个简单访问器 +function makeSimpleAccessor(name, value) { + function accessor(value) { + var oldValue = accessor._value + if (arguments.length > 0) { + if (!stopRepeatAssign && !isEqual(value, oldValue)) { + accessor.updateValue(this, value) + accessor.notify(this, value, oldValue) + } + return this + } else { + dependencyDetection.collectDependency(this, accessor) + return oldValue + } + } + accessorFactory(accessor, name) + accessor._value = value + return accessor; +} + +//创建一个计算访问器 +function makeComputedAccessor(name, options) { + function accessor(value) {//计算属性 + var oldValue = accessor._value + var init = ("_value" in accessor) + if (arguments.length > 0) { + if (stopRepeatAssign) { + return this + } + if (typeof accessor.set === "function") { + if (accessor.oldArgs !== value) { + accessor.oldArgs = value + var $events = this.$events + var lock = $events[name] + $events[name] = [] //清空回调,防止内部冒泡而触发多次$fire + accessor.set.call(this, value) + $events[name] = lock + value = accessor.get.call(this) + if (value !== oldValue) { + accessor.updateValue(this, value) + accessor.notify(this, value, oldValue) //触发$watch回调 + } + } + } + return this + } else { + //将依赖于自己的高层访问器或视图刷新函数(以绑定对象形式)放到自己的订阅数组中 + //将自己注入到低层访问器的订阅数组中 + value = accessor.get.call(this) + accessor.updateValue(this, value) + if (init && oldValue !== value) { + accessor.notify(this, value, oldValue) //触发$watch回调 + } + return value + } + } + accessor.set = options.set + accessor.get = options.get + accessorFactory(accessor, name) + return accessor +} + +//创建一个复杂访问器 +function makeComplexAccessor(name, initValue, valueType, list) { + function accessor(value) { + var oldValue = accessor._value + + var son = accessor._vmodel + if (arguments.length > 0) { + if (stopRepeatAssign) { + return this + } + if (valueType === "array") { + var a = son, b = value, + an = a.length, + bn = b.length + a.$lock = true + if (an > bn) { + a.splice(bn, an - bn) + } else if (bn > an) { + a.push.apply(a, b.slice(an)) + } + var n = Math.min(an, bn) + for (var i = 0; i < n; i++) { + a.set(i, b[i]) + } + delete a.$lock + a._fire("set") + } else if (valueType === "object") { + var newPropertyNames = Object.keys(value).join("­") + if (son.$propertyNames === newPropertyNames) { + for (i in value) { + son[i] = value[i] + } + } else { + var sson = accessor._vmodel = modelFactory(value) + var sevent = sson.$events + var oevent = son.$events + for (var i in sevent) { + var arr = sevent[i] + if (Array.isArray(arr)) { + arr = arr.concat(oevent[i]) + } + } + sevent[subscribers] = oevent[subscribers] + sson.$proxy = son.$proxy + son = sson + } + } + accessor.updateValue(this, son.$model) + accessor.notify(this, this._value, oldValue) + return this + } else { + dependencyDetection.collectDependency(this, accessor) + return son + } + } + accessorFactory(accessor, name) + var son = accessor._vmodel = modelFactory(initValue) + son.$events[subscribers] = list + return accessor +} + +function globalUpdateValue(vmodel, value) { + vmodel.$model[this._name] = this._value = value +} + +function globalNotify(vmodel, value, oldValue) { + var name = this._name + var array = vmodel.$events[name] //刷新值 + if (array) { + fireDependencies(array) //同步视图 + EventBus.$fire.call(vmodel, name, value, oldValue) //触发$watch回调 + } +} + +function accessorFactory(accessor, name) { + accessor._name = name + //同时更新_value与model + accessor.updateValue = globalUpdateValue + accessor.notify = globalNotify +} + +//比较两个值是否相等 +var isEqual = Object.is || function (v1, v2) { + if (v1 === 0 && v2 === 0) { + return 1 / v1 === 1 / v2 + } else if (v1 !== v1) { + return v2 !== v2 + } else { + return v1 === v2 + } +} + +function isObservable(name, value, $skipArray) { + if (isFunction(value) || value && value.nodeType) { + return false + } + if ($skipArray.indexOf(name) !== -1) { + return false + } + var $special = $skipArray.$special + if (name && name.charAt(0) === "$" && !$special[name]) { + return false + } + return true +} + +var descriptorFactory = W3C ? function (obj) { + var descriptors = {} + for (var i in obj) { + descriptors[i] = { + get: obj[i], + set: obj[i], + enumerable: true, + configurable: true + } + } + return descriptors +} : function (a) { + return a +} + +//===================修复浏览器对Object.defineProperties的支持================= +if (!canHideOwn) { + if ("__defineGetter__" in avalon) { + defineProperty = function (obj, prop, desc) { + if ('value' in desc) { + obj[prop] = desc.value + } + if ("get" in desc) { + obj.__defineGetter__(prop, desc.get) + } + if ('set' in desc) { + obj.__defineSetter__(prop, desc.set) + } + return obj + } + defineProperties = function (obj, descs) { + for (var prop in descs) { + if (descs.hasOwnProperty(prop)) { + defineProperty(obj, prop, descs[prop]) + } + } + return obj + } + } + if (IEVersion) { + var VBClassPool = {} + window.execScript([// jshint ignore:line + "Function parseVB(code)", + "\tExecuteGlobal(code)", + "End Function" //转换一段文本为VB代码 + ].join("\n"), "VBScript") + function VBMediator(instance, accessors, name, value) {// jshint ignore:line + var accessor = accessors[name] + if (arguments.length === 4) { + accessor.call(instance, value) + } else { + return accessor.call(instance) + } + } + defineProperties = function (name, accessors, properties) { + // jshint ignore:line + var buffer = [] + buffer.push( + "\r\n\tPrivate [__data__], [__proxy__]", + "\tPublic Default Function [__const__](d, p)", + "\t\tSet [__data__] = d: set [__proxy__] = p", + "\t\tSet [__const__] = Me", //链式调用 + "\tEnd Function") + //添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好 + for (name in properties) { + if (!accessors.hasOwnProperty(name)) { + buffer.push("\tPublic [" + name + "]") + } + } + $$skipArray.forEach(function (name) { + if (!accessors.hasOwnProperty(name)) { + buffer.push("\tPublic [" + name + "]") + } + }) + buffer.push("\tPublic [" + 'hasOwnProperty' + "]") + //添加访问器属性 + for (name in accessors) { + buffer.push( + //由于不知对方会传入什么,因此set, let都用上 + "\tPublic Property Let [" + name + "](val" + expose + ")", //setter + "\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")", + "\tEnd Property", + "\tPublic Property Set [" + name + "](val" + expose + ")", //setter + "\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")", + "\tEnd Property", + "\tPublic Property Get [" + name + "]", //getter + "\tOn Error Resume Next", //必须优先使用set语句,否则它会误将数组当字符串返回 + "\t\tSet[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")", + "\tIf Err.Number <> 0 Then", + "\t\t[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")", + "\tEnd If", + "\tOn Error Goto 0", + "\tEnd Property") + + } + + buffer.push("End Class") + var body = buffer.join("\r\n") + var className =VBClassPool[body] + if (!className) { + className = generateID("VBClass") + window.parseVB("Class " + className + body) + window.parseVB([ + "Function " + className + "Factory(a, b)", //创建实例并传入两个关键的参数 + "\tDim o", + "\tSet o = (New " + className + ")(a, b)", + "\tSet " + className + "Factory = o", + "End Function" + ].join("\r\n")) + VBClassPool[body] = className + } + var ret = window[className + "Factory"](accessors, VBMediator) //得到其产品 + return ret //得到其产品 + } + } +} + +/********************************************************************* + * 监控数组(与ms-each, ms-repeat配合使用) * + **********************************************************************/ + +function arrayFactory(model) { + var array = [] + array.$id = generateID() + array.$model = model //数据模型 + array.$events = {} + array.$events[subscribers] = [] + array._ = modelFactory({ + length: model.length + }) + array._.$watch("length", function (a, b) { + array.$fire("length", a, b) + }) + for (var i in EventBus) { + array[i] = EventBus[i] + } + avalon.mix(array, arrayPrototype) + return array +} + +function mutateArray(method, pos, n, index, method2, pos2, n2) { + var oldLen = this.length, loop = 2 + while (--loop) { + switch (method) { + case "add": + /* jshint ignore:start */ + var array = this.$model.slice(pos, pos + n).map(function (el) { + if (rcomplexType.test(avalon.type(el))) { + return el.$id ? el : modelFactory(el, 0, el) + } else { + return el + } + }) + /* jshint ignore:end */ + _splice.apply(this, [pos, 0].concat(array)) + this._fire("add", pos, n) + break + case "del": + var ret = this._splice(pos, n) + this._fire("del", pos, n) + break + } + if (method2) { + method = method2 + pos = pos2 + n = n2 + loop = 2 + method2 = 0 + } + } + this._fire("index", index) + if (this.length !== oldLen) { + this._.length = this.length + } + return ret +} + +var _splice = ap.splice +var arrayPrototype = { + _splice: _splice, + _fire: function (method, a, b) { + fireDependencies(this.$events[subscribers], method, a, b) + }, + size: function () { //取得数组长度,这个函数可以同步视图,length不能 + return this._.length + }, + pushArray: function (array) { + var m = array.length, n = this.length + if (m) { + ap.push.apply(this.$model, array) + mutateArray.call(this, "add", n, m, Math.max(0, n - 1)) + } + return m + n + }, + push: function () { + //http://jsperf.com/closure-with-arguments + var array = [] + var i, n = arguments.length + for (i = 0; i < n; i++) { + array[i] = arguments[i] + } + return this.pushArray(array) + }, + unshift: function () { + var m = arguments.length, n = this.length + if (m) { + ap.unshift.apply(this.$model, arguments) + mutateArray.call(this, "add", 0, m, 0) + } + return m + n //IE67的unshift不会返回长度 + }, + shift: function () { + if (this.length) { + var el = this.$model.shift() + mutateArray.call(this, "del", 0, 1, 0) + return el //返回被移除的元素 + } + }, + pop: function () { + var n = this.length + if (n) { + var el = this.$model.pop() + mutateArray.call(this, "del", n - 1, 1, Math.max(0, n - 2)) + return el //返回被移除的元素 + } + }, + splice: function (start) { + var m = arguments.length, args = [], change + var removed = _splice.apply(this.$model, arguments) + if (removed.length) { //如果用户删掉了元素 + args.push("del", start, removed.length, 0) + change = true + } + if (m > 2) { //如果用户添加了元素 + if (change) { + args.splice(3, 1, 0, "add", start, m - 2) + } else { + args.push("add", start, m - 2, 0) + } + change = true + } + if (change) { //返回被移除的元素 + return mutateArray.apply(this, args) + } else { + return [] + } + }, + contains: function (el) { //判定是否包含 + return this.indexOf(el) !== -1 + }, + remove: function (el) { //移除第一个等于给定值的元素 + return this.removeAt(this.indexOf(el)) + }, + removeAt: function (index) { //移除指定索引上的元素 + if (index >= 0) { + this.$model.splice(index, 1) + return mutateArray.call(this, "del", index, 1, 0) + } + return [] + }, + clear: function () { + this.$model.length = this.length = this._.length = 0 //清空数组 + this._fire("clear", 0) + return this + }, + removeAll: function (all) { //移除N个元素 + if (Array.isArray(all)) { + for (var i = this.length - 1; i >= 0; i--) { + if (all.indexOf(this[i]) !== -1) { + this.removeAt(i) + } + } + } else if (typeof all === "function") { + for ( i = this.length - 1; i >= 0; i--) { + var el = this[i] + if (all(el, i)) { + this.removeAt(i) + } + } + } else { + this.clear() + } + }, + ensure: function (el) { + if (!this.contains(el)) { //只有不存在才push + this.push(el) + } + return this + }, + set: function (index, val) { + if (index >= 0) { + var valueType = avalon.type(val) + if (val && val.$model) { + val = val.$model + } + var target = this[index] + if (valueType === "object") { + for (var i in val) { + if (target.hasOwnProperty(i)) { + target[i] = val[i] + } + } + } else if (valueType === "array") { + target.clear().push.apply(target, val) + } else if (target !== val) { + this[index] = val + this.$model[index] = val + this._fire("set", index, val) + } + } + return this + } +} +//相当于原来bindingExecutors.repeat 的index分支 +function resetIndex(array, pos) { + var last = array.length - 1 + for (var el; el = array[pos]; pos++) { + el.$index = pos + el.$first = pos === 0 + el.$last = pos === last + } +} + +function sortByIndex(array, indexes) { + var map = {}; + for (var i = 0, n = indexes.length; i < n; i++) { + map[i] = array[i] // preserve + var j = indexes[i] + if (j in map) { + array[i] = map[j] + delete map[j] + } else { + array[i] = array[j] + } + } +} + +"sort,reverse".replace(rword, function (method) { + arrayPrototype[method] = function () { + var newArray = this.$model//这是要排序的新数组 + var oldArray = newArray.concat() //保持原来状态的旧数组 + var mask = Math.random() + var indexes = [] + var hasSort + ap[method].apply(newArray, arguments) //排序 + for (var i = 0, n = oldArray.length; i < n; i++) { + var neo = newArray[i] + var old = oldArray[i] + if (isEqual(neo, old)) { + indexes.push(i) + } else { + var index = oldArray.indexOf(neo) + indexes.push(index)//得到新数组的每个元素在旧数组对应的位置 + oldArray[index] = mask //屏蔽已经找过的元素 + hasSort = true + } + } + if (hasSort) { + sortByIndex(this, indexes) + // sortByIndex(this.$proxy, indexes) + this._fire("move", indexes) + this._fire("index", 0) + } + return this + } +}) + + +/********************************************************************* + * 依赖调度系统 * + **********************************************************************/ +//检测两个对象间的依赖关系 +var dependencyDetection = (function () { + var outerFrames = [] + var currentFrame + return { + begin: function (accessorObject) { + //accessorObject为一个拥有callback的对象 + outerFrames.push(currentFrame) + currentFrame = accessorObject + }, + end: function () { + currentFrame = outerFrames.pop() + }, + collectDependency: function (vmodel, accessor) { + if (currentFrame) { + //被dependencyDetection.begin调用 + currentFrame.callback(vmodel, accessor); + } + } + }; +})() +//将绑定对象注入到其依赖项的订阅数组中 +var ronduplex = /^(duplex|on)$/ +avalon.injectBinding = function (data) { + var valueFn = data.evaluator + if (valueFn) { //如果是求值函数 + dependencyDetection.begin({ + callback: function (vmodel, dependency) { + injectDependency(vmodel.$events[dependency._name], data) + } + }) + try { + var value = ronduplex.test(data.type) ? data : valueFn.apply(0, data.args) + if(value === void 0){ + delete data.evaluator + } + data.handler(value, data.element, data) + } catch (e) { + //log("warning:exception throwed in [avalon.injectBinding] " + e) + delete data.evaluator + var node = data.element + if (node.nodeType === 3) { + var parent = node.parentNode + if (kernel.commentInterpolate) { + parent.replaceChild(DOC.createComment(data.value), node) + } else { + node.data = openTag + (data.oneTime ? "::" : "") + data.value + closeTag + } + } + } finally { + dependencyDetection.end() + } + } +} + +//将依赖项(比它高层的访问器或构建视图刷新函数的绑定对象)注入到订阅者数组 +function injectDependency(list, data) { + if (data.oneTime) + return + if (list && avalon.Array.ensure(list, data) && data.element) { + injectDisposeQueue(data, list) + } +} + +//通知依赖于这个访问器的订阅者更新自身 +function fireDependencies(list) { + if (list && list.length) { + if (new Date() - beginTime > 444 && typeof list[0] === "object") { + rejectDisposeQueue() + } + var args = aslice.call(arguments, 1) + for (var i = list.length, fn; fn = list[--i]; ) { + var el = fn.element + if (el && el.parentNode) { + try { + var valueFn = fn.evaluator + if (fn.$repeat) { + fn.handler.apply(fn, args) //处理监控数组的方法 + }else if("$repeat" in fn || !valueFn ){//如果没有eval,先eval + bindingHandlers[fn.type](fn, fn.vmodels) + } else if (fn.type !== "on") { //事件绑定只能由用户触发,不能由程序触发 + var value = valueFn.apply(0, fn.args || []) + fn.handler(value, el, fn) + } + } catch (e) { } + } + } + } +} +/********************************************************************* + * 定时GC回收机制 * + **********************************************************************/ +var disposeCount = 0 +var disposeQueue = avalon.$$subscribers = [] +var beginTime = new Date() +var oldInfo = {} +var uuid2Node = {} +function getUid(obj, makeID) { //IE9+,标准浏览器 + if (!obj.uuid && !makeID) { + obj.uuid = ++disposeCount + uuid2Node[obj.uuid] = obj + } + return obj.uuid +} +function getNode(uuid) { + return uuid2Node[uuid] +} +//添加到回收列队中 +function injectDisposeQueue(data, list) { + var elem = data.element + if (!data.uuid) { + if (elem.nodeType !== 1) { + data.uuid = data.type + (data.pos || 0) + "-" + getUid(elem.parentNode) + } else { + data.uuid = data.name + "-" + getUid(elem) + } + } + var lists = data.lists || (data.lists = []) + avalon.Array.ensure(lists, list) + list.$uuid = list.$uuid || generateID() + if (!disposeQueue[data.uuid]) { + disposeQueue[data.uuid] = 1 + disposeQueue.push(data) + } +} + +function rejectDisposeQueue(data) { + if (avalon.optimize) + return + var i = disposeQueue.length + var n = i + var allTypes = [] + var iffishTypes = {} + var newInfo = {} + //对页面上所有绑定对象进行分门别类, 只检测个数发生变化的类型 + while (data = disposeQueue[--i]) { + var type = data.type + if (newInfo[type]) { + newInfo[type]++ + } else { + newInfo[type] = 1 + allTypes.push(type) + } + } + var diff = false + allTypes.forEach(function (type) { + if (oldInfo[type] !== newInfo[type]) { + iffishTypes[type] = 1 + diff = true + } + }) + i = n + if (diff) { + while (data = disposeQueue[--i]) { + if (!data.element) + continue + if (iffishTypes[data.type] && shouldDispose(data.element)) { //如果它没有在DOM树 + disposeQueue.splice(i, 1) + delete disposeQueue[data.uuid] + delete uuid2Node[data.element.uuid] + var lists = data.lists + for (var k = 0, list; list = lists[k++]; ) { + avalon.Array.remove(lists, list) + avalon.Array.remove(list, data) + } + disposeData(data) + } + } + } + oldInfo = newInfo + beginTime = new Date() +} + +function disposeData(data) { + data.element = null + data.rollback && data.rollback() + for (var key in data) { + data[key] = null + } +} + +function shouldDispose(el) { + try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错 + if (!el.parentNode) { + return true + } + } catch (e) { + return true + } + + return el.msRetain ? 0 : (el.nodeType === 1 ? !root.contains(el) : !avalon.contains(root, el)) +} + +/************************************************************************ + * HTML处理(parseHTML, innerHTML, clearHTML) * + ************************************************************************/ +// We have to close these tags to support XHTML +var tagHooks = { + area: [1, "", ""], + param: [1, "", ""], + col: [2, "", "
"], + legend: [1, "
", "
"], + option: [1, ""], + thead: [1, "", "
"], + tr: [2, "", "
"], + td: [3, "", "
"], + g: [1, '', ''], + //IE6-8在用innerHTML生成节点时,不能直接创建no-scope元素与HTML5的新标签 + _default: W3C ? [0, "", ""] : [1, "X
", "
"] //div可以不用闭合 +} +tagHooks.th = tagHooks.td +tagHooks.optgroup = tagHooks.option +tagHooks.tbody = tagHooks.tfoot = tagHooks.colgroup = tagHooks.caption = tagHooks.thead +String("circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use").replace(rword, function (tag) { + tagHooks[tag] = tagHooks.g //处理SVG +}) +var rtagName = /<([\w:]+)/ //取得其tagName +var rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig +var rcreate = W3C ? /[^\d\D]/ : /(<(?:script|link|style|meta|noscript))/ig +var scriptTypes = oneObject(["", "text/javascript", "text/ecmascript", "application/ecmascript", "application/javascript"]) +var rnest = /<(?:tb|td|tf|th|tr|col|opt|leg|cap|area)/ //需要处理套嵌关系的标签 +var script = DOC.createElement("script") +var rhtml = /<|&#?\w+;/ +avalon.parseHTML = function (html) { + var fragment = avalonFragment.cloneNode(false) + if (typeof html !== "string") { + return fragment + } + if (!rhtml.test(html)) { + fragment.appendChild(DOC.createTextNode(html)) + return fragment + } + html = html.replace(rxhtml, "<$1>").trim() + var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(), + //取得其标签名 + wrap = tagHooks[tag] || tagHooks._default, + wrapper = cinerator, + firstChild, neo + if (!W3C) { //fix IE + html = html.replace(rcreate, "
$1") //在link style script等标签之前添加一个补丁 + } + wrapper.innerHTML = wrap[1] + html + wrap[2] + var els = wrapper.getElementsByTagName("script") + if (els.length) { //使用innerHTML生成的script节点不会发出请求与执行text属性 + for (var i = 0, el; el = els[i++]; ) { + if (scriptTypes[el.type]) { + //以偷龙转凤方式恢复执行脚本功能 + neo = script.cloneNode(false) //FF不能省略参数 + ap.forEach.call(el.attributes, function (attr) { + if (attr && attr.specified) { + neo[attr.name] = attr.value //复制其属性 + neo.setAttribute(attr.name, attr.value) + } + }) // jshint ignore:line + neo.text = el.text + el.parentNode.replaceChild(neo, el) //替换节点 + } + } + } + if (!W3C) { //fix IE + var target = wrap[1] === "X
" ? wrapper.lastChild.firstChild : wrapper.lastChild + if (target && target.tagName === "TABLE" && tag !== "tbody") { + //IE6-7处理 --> , + // --> , + // -->
+ for (els = target.childNodes, i = 0; el = els[i++]; ) { + if (el.tagName === "TBODY" && !el.innerHTML) { + target.removeChild(el) + break + } + } + } + els = wrapper.getElementsByTagName("br") + var n = els.length + while (el = els[--n]) { + if (el.className === "msNoScope") { + el.parentNode.removeChild(el) + } + } + for (els = wrapper.all, i = 0; el = els[i++]; ) { //fix VML + if (isVML(el)) { + fixVML(el) + } + } + } + //移除我们为了符合套嵌关系而添加的标签 + for (i = wrap[0]; i--; wrapper = wrapper.lastChild) { + } + while (firstChild = wrapper.firstChild) { // 将wrapper上的节点转移到文档碎片上! + fragment.appendChild(firstChild) + } + return fragment +} + +function isVML(src) { + var nodeName = src.nodeName + return nodeName.toLowerCase() === nodeName && src.scopeName && src.outerText === "" +} + +function fixVML(node) { + if (node.currentStyle.behavior !== "url(#default#VML)") { + node.style.behavior = "url(#default#VML)" + node.style.display = "inline-block" + node.style.zoom = 1 //hasLayout + } +} +avalon.innerHTML = function (node, html) { + if (!W3C && (!rcreate.test(html) && !rnest.test(html))) { + try { + node.innerHTML = html + return + } catch (e) { + } + } + var a = this.parseHTML(html) + this.clearHTML(node).appendChild(a) +} +avalon.clearHTML = function (node) { + node.textContent = "" + while (node.firstChild) { + node.removeChild(node.firstChild) + } + return node +} + +/********************************************************************* + * avalon的原型方法定义区 * + **********************************************************************/ + +function hyphen(target) { + //转换为连字符线风格 + return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase() +} + +function camelize(target) { + //提前判断,提高getStyle等的效率 + if (!target || target.indexOf("-") < 0 && target.indexOf("_") < 0) { + return target + } + //转换为驼峰风格 + return target.replace(/[-_][^-_]/g, function(match) { + return match.charAt(1).toUpperCase() + }) +} + +var fakeClassListMethods = { + _toString: function() { + var node = this.node + var cls = node.className + var str = typeof cls === "string" ? cls : cls.baseVal + return str.split(/\s+/).join(" ") + }, + _contains: function(cls) { + return (" " + this + " ").indexOf(" " + cls + " ") > -1 + }, + _add: function(cls) { + if (!this.contains(cls)) { + this._set(this + " " + cls) + } + }, + _remove: function(cls) { + this._set((" " + this + " ").replace(" " + cls + " ", " ")) + }, + __set: function(cls) { + cls = cls.trim() + var node = this.node + if (rsvg.test(node)) { + //SVG元素的className是一个对象 SVGAnimatedString { baseVal="", animVal=""},只能通过set/getAttribute操作 + node.setAttribute("class", cls) + } else { + node.className = cls + } + } //toggle存在版本差异,因此不使用它 +} + + function fakeClassList(node) { + if (!("classList" in node)) { + node.classList = { + node: node + } + for (var k in fakeClassListMethods) { + node.classList[k.slice(1)] = fakeClassListMethods[k] + } + } + return node.classList + } + + + "add,remove".replace(rword, function(method) { + avalon.fn[method + "Class"] = function(cls) { + var el = this[0] + //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26 + if (cls && typeof cls === "string" && el && el.nodeType === 1) { + cls.replace(/\S+/g, function(c) { + fakeClassList(el)[method](c) + }) + } + return this + } + }) + avalon.fn.mix({ + hasClass: function(cls) { + var el = this[0] || {} + return el.nodeType === 1 && fakeClassList(el).contains(cls) + }, + toggleClass: function(value, stateVal) { + var className, i = 0 + var classNames = String(value).split(/\s+/) + var isBool = typeof stateVal === "boolean" + while ((className = classNames[i++])) { + var state = isBool ? stateVal : !this.hasClass(className) + this[state ? "addClass" : "removeClass"](className) + } + return this + }, + attr: function(name, value) { + if (arguments.length === 2) { + this[0].setAttribute(name, value) + return this + } else { + return this[0].getAttribute(name) + } + }, + data: function(name, value) { + name = "data-" + hyphen(name || "") + switch (arguments.length) { + case 2: + this.attr(name, value) + return this + case 1: + var val = this.attr(name) + return parseData(val) + case 0: + var ret = {} + ap.forEach.call(this[0].attributes, function(attr) { + if (attr) { + name = attr.name + if (!name.indexOf("data-")) { + name = camelize(name.slice(5)) + ret[name] = parseData(attr.value) + } + } + }) + return ret + } + }, + removeData: function(name) { + name = "data-" + hyphen(name) + this[0].removeAttribute(name) + return this + }, + css: function(name, value) { + if (avalon.isPlainObject(name)) { + for (var i in name) { + avalon.css(this, i, name[i]) + } + } else { + var ret = avalon.css(this, name, value) + } + return ret !== void 0 ? ret : this + }, + position: function() { + var offsetParent, offset, + elem = this[0], + parentOffset = { + top: 0, + left: 0 + } + if (!elem) { + return + } + if (this.css("position") === "fixed") { + offset = elem.getBoundingClientRect() + } else { + offsetParent = this.offsetParent() //得到真正的offsetParent + offset = this.offset() // 得到正确的offsetParent + if (offsetParent[0].tagName !== "HTML") { + parentOffset = offsetParent.offset() + } + parentOffset.top += avalon.css(offsetParent[0], "borderTopWidth", true) + parentOffset.left += avalon.css(offsetParent[0], "borderLeftWidth", true) + + // Subtract offsetParent scroll positions + parentOffset.top -= offsetParent.scrollTop() + parentOffset.left -= offsetParent.scrollLeft() + } + return { + top: offset.top - parentOffset.top - avalon.css(elem, "marginTop", true), + left: offset.left - parentOffset.left - avalon.css(elem, "marginLeft", true) + } + }, + offsetParent: function() { + var offsetParent = this[0].offsetParent + while (offsetParent && avalon.css(offsetParent, "position") === "static") { + offsetParent = offsetParent.offsetParent; + } + return avalon(offsetParent || root) + }, + bind: function(type, fn, phase) { + if (this[0]) { //此方法不会链 + return avalon.bind(this[0], type, fn, phase) + } + }, + unbind: function(type, fn, phase) { + if (this[0]) { + avalon.unbind(this[0], type, fn, phase) + } + return this + }, + val: function(value) { + var node = this[0] + if (node && node.nodeType === 1) { + var get = arguments.length === 0 + var access = get ? ":get" : ":set" + var fn = valHooks[getValType(node) + access] + if (fn) { + var val = fn(node, value) + } else if (get) { + return (node.value || "").replace(/\r/g, "") + } else { + node.value = value + } + } + return get ? val : this + } + }) + + function parseData(data) { + try { + if (typeof data === "object") + return data + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? avalon.parseJSON(data) : data + } catch (e) {} + return data + } +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g +avalon.parseJSON = window.JSON ? JSON.parse : function(data) { + if (typeof data === "string") { + data = data.trim(); + if (data) { + if (rvalidchars.test(data.replace(rvalidescape, "@") + .replace(rvalidtokens, "]") + .replace(rvalidbraces, ""))) { + return (new Function("return " + data))() // jshint ignore:line + } + } + avalon.error("Invalid JSON: " + data) + } + return data +} + +//生成avalon.fn.scrollLeft, avalon.fn.scrollTop方法 +avalon.each({ + scrollLeft: "pageXOffset", + scrollTop: "pageYOffset" +}, function(method, prop) { + avalon.fn[method] = function(val) { + var node = this[0] || {}, win = getWindow(node), + top = method === "scrollTop" + if (!arguments.length) { + return win ? (prop in win) ? win[prop] : root[method] : node[method] + } else { + if (win) { + win.scrollTo(!top ? val : avalon(win).scrollLeft(), top ? val : avalon(win).scrollTop()) + } else { + node[method] = val + } + } + } +}) + +function getWindow(node) { + return node.window && node.document ? node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false; +} +//=============================css相关======================= +var cssHooks = avalon.cssHooks = {} +var prefixes = ["", "-webkit-", "-o-", "-moz-", "-ms-"] +var cssMap = { + "float": W3C ? "cssFloat" : "styleFloat" +} +avalon.cssNumber = oneObject("columnCount,order,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom") + +avalon.cssName = function(name, host, camelCase) { + if (cssMap[name]) { + return cssMap[name] + } + host = host || root.style + for (var i = 0, n = prefixes.length; i < n; i++) { + camelCase = camelize(prefixes[i] + name) + if (camelCase in host) { + return (cssMap[name] = camelCase) + } + } + return null +} +cssHooks["@:set"] = function(node, name, value) { + try { //node.style.width = NaN;node.style.width = "xxxxxxx";node.style.width = undefine 在旧式IE下会抛异常 + node.style[name] = value + } catch (e) {} +} +if (window.getComputedStyle) { + cssHooks["@:get"] = function(node, name) { + if (!node || !node.style) { + throw new Error("getComputedStyle要求传入一个节点 " + node) + } + var ret, styles = getComputedStyle(node, null) + if (styles) { + ret = name === "filter" ? styles.getPropertyValue(name) : styles[name] + if (ret === "") { + ret = node.style[name] //其他浏览器需要我们手动取内联样式 + } + } + return ret + } + cssHooks["opacity:get"] = function(node) { + var ret = cssHooks["@:get"](node, "opacity") + return ret === "" ? "1" : ret + } +} else { + var rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i + var rposition = /^(top|right|bottom|left)$/ + var ralpha = /alpha\([^)]*\)/i + var ie8 = !! window.XDomainRequest + var salpha = "DXImageTransform.Microsoft.Alpha" + var border = { + thin: ie8 ? '1px' : '2px', + medium: ie8 ? '3px' : '4px', + thick: ie8 ? '5px' : '6px' + } + cssHooks["@:get"] = function(node, name) { + //取得精确值,不过它有可能是带em,pc,mm,pt,%等单位 + var currentStyle = node.currentStyle + var ret = currentStyle[name] + if ((rnumnonpx.test(ret) && !rposition.test(ret))) { + //①,保存原有的style.left, runtimeStyle.left, + var style = node.style, + left = style.left, + rsLeft = node.runtimeStyle.left + //②由于③处的style.left = xxx会影响到currentStyle.left, + //因此把它currentStyle.left放到runtimeStyle.left, + //runtimeStyle.left拥有最高优先级,不会style.left影响 + node.runtimeStyle.left = currentStyle.left + //③将精确值赋给到style.left,然后通过IE的另一个私有属性 style.pixelLeft + //得到单位为px的结果;fontSize的分支见http://bugs.jquery.com/ticket/760 + style.left = name === 'fontSize' ? '1em' : (ret || 0) + ret = style.pixelLeft + "px" + //④还原 style.left,runtimeStyle.left + style.left = left + node.runtimeStyle.left = rsLeft + } + if (ret === "medium") { + name = name.replace("Width", "Style") + //border width 默认值为medium,即使其为0" + if (currentStyle[name] === "none") { + ret = "0px" + } + } + return ret === "" ? "auto" : border[ret] || ret + } + cssHooks["opacity:set"] = function(node, name, value) { + var style = node.style + var opacity = isFinite(value) && value <= 1 ? "alpha(opacity=" + value * 100 + ")" : "" + var filter = style.filter || ""; + style.zoom = 1 + //不能使用以下方式设置透明度 + //node.filters.alpha.opacity = value * 100 + style.filter = (ralpha.test(filter) ? + filter.replace(ralpha, opacity) : + filter + " " + opacity).trim() + if (!style.filter) { + style.removeAttribute("filter") + } + } + cssHooks["opacity:get"] = function(node) { + //这是最快的获取IE透明值的方式,不需要动用正则了! + var alpha = node.filters.alpha || node.filters[salpha], + op = alpha && alpha.enabled ? alpha.opacity : 100 + return (op / 100) + "" //确保返回的是字符串 + } +} + +"top,left".replace(rword, function(name) { + cssHooks[name + ":get"] = function(node) { + var computed = cssHooks["@:get"](node, name) + return /px$/.test(computed) ? computed : + avalon(node).position()[name] + "px" + } +}) + +var cssShow = { + position: "absolute", + visibility: "hidden", + display: "block" +} + +var rdisplayswap = /^(none|table(?!-c[ea]).+)/ + + function showHidden(node, array) { + //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html + if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0 + if (rdisplayswap.test(cssHooks["@:get"](node, "display"))) { + var obj = { + node: node + } + for (var name in cssShow) { + obj[name] = node.style[name] + node.style[name] = cssShow[name] + } + array.push(obj) + } + var parent = node.parentNode + if (parent && parent.nodeType === 1) { + showHidden(parent, array) + } + } + } + "Width,Height".replace(rword, function(name) { //fix 481 + var method = name.toLowerCase(), + clientProp = "client" + name, + scrollProp = "scroll" + name, + offsetProp = "offset" + name + cssHooks[method + ":get"] = function(node, which, override) { + var boxSizing = -4 + if (typeof override === "number") { + boxSizing = override + } + which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"] + var ret = node[offsetProp] // border-box 0 + if (boxSizing === 2) { // margin-box 2 + return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true) + } + if (boxSizing < 0) { // padding-box -2 + ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true) + } + if (boxSizing === -4) { // content-box -4 + ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true) + } + return ret + } + cssHooks[method + "&get"] = function(node) { + var hidden = []; + showHidden(node, hidden); + var val = cssHooks[method + ":get"](node) + for (var i = 0, obj; obj = hidden[i++];) { + node = obj.node + for (var n in obj) { + if (typeof obj[n] === "string") { + node.style[n] = obj[n] + } + } + } + return val; + } + avalon.fn[method] = function(value) { //会忽视其display + var node = this[0] + if (arguments.length === 0) { + if (node.setTimeout) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替 + return node["inner" + name] || node.document.documentElement[clientProp] + } + if (node.nodeType === 9) { //取得页面尺寸 + var doc = node.documentElement + //FF chrome html.scrollHeight< body.scrollHeight + //IE 标准模式 : html.scrollHeight> body.scrollHeight + //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点? + return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp]) + } + return cssHooks[method + "&get"](node) + } else { + return this.css(method, value) + } + } + avalon.fn["inner" + name] = function() { + return cssHooks[method + ":get"](this[0], void 0, -2) + } + avalon.fn["outer" + name] = function(includeMargin) { + return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0) + } + }) + avalon.fn.offset = function() { //取得距离页面左右角的坐标 + var node = this[0], + box = { + left: 0, + top: 0 + } + if (!node || !node.tagName || !node.ownerDocument) { + return box + } + var doc = node.ownerDocument, + body = doc.body, + root = doc.documentElement, + win = doc.defaultView || doc.parentWindow + if (!avalon.contains(root, node)) { + return box + } + //http://hkom.blog1.fc2.com/?mode=m&no=750 body的偏移量是不包含margin的 + //我们可以通过getBoundingClientRect来获得元素相对于client的rect. + //http://msdn.microsoft.com/en-us/library/ms536433.aspx + if (node.getBoundingClientRect) { + box = node.getBoundingClientRect() // BlackBerry 5, iOS 3 (original iPhone) + } + //chrome/IE6: body.scrollTop, firefox/other: root.scrollTop + var clientTop = root.clientTop || body.clientTop, + clientLeft = root.clientLeft || body.clientLeft, + scrollTop = Math.max(win.pageYOffset || 0, root.scrollTop, body.scrollTop), + scrollLeft = Math.max(win.pageXOffset || 0, root.scrollLeft, body.scrollLeft) + // 把滚动距离加到left,top中去。 + // IE一些版本中会自动为HTML元素加上2px的border,我们需要去掉它 + // http://msdn.microsoft.com/en-us/library/ms533564(VS.85).aspx + return { + top: box.top + scrollTop - clientTop, + left: box.left + scrollLeft - clientLeft + } + } + + //==================================val相关============================ + + function getValType(elem) { + var ret = elem.tagName.toLowerCase() + return ret === "input" && /checkbox|radio/.test(elem.type) ? "checked" : ret + } +var roption = /^]+))?)*\s+value[\s=]/i +var valHooks = { + "option:get": IEVersion ? function(node) { + //在IE11及W3C,如果没有指定value,那么node.value默认为node.text(存在trim作),但IE9-10则是取innerHTML(没trim操作) + //specified并不可靠,因此通过分析outerHTML判定用户有没有显示定义value + return roption.test(node.outerHTML) ? node.value : node.text.trim() + } : function(node) { + return node.value + }, + "select:get": function(node, value) { + var option, options = node.options, + index = node.selectedIndex, + getter = valHooks["option:get"], + one = node.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? max : one ? index : 0 + for (; i < max; i++) { + option = options[i] + //旧式IE在reset后不会改变selected,需要改用i === index判定 + //我们过滤所有disabled的option元素,但在safari5下,如果设置select为disable,那么其所有孩子都disable + //因此当一个元素为disable,需要检测其是否显式设置了disable及其父节点的disable情况 + if ((option.selected || i === index) && !option.disabled) { + value = getter(option) + if (one) { + return value + } + //收集所有selected值组成数组返回 + values.push(value) + } + } + return values + }, + "select:set": function(node, values, optionSet) { + values = [].concat(values) //强制转换为数组 + var getter = valHooks["option:get"] + for (var i = 0, el; el = node.options[i++];) { + if ((el.selected = values.indexOf(getter(el)) > -1)) { + optionSet = true + } + } + if (!optionSet) { + node.selectedIndex = -1 + } + } +} + +/********************************************************************* + * 编译系统 * + **********************************************************************/ +var meta = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"': '\\"', + '\\': '\\\\' +} +var quote = window.JSON && JSON.stringify || function(str) { + return '"' + str.replace(/[\\\"\x00-\x1f]/g, function(a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' +} + +var keywords = [ + "break,case,catch,continue,debugger,default,delete,do,else,false", + "finally,for,function,if,in,instanceof,new,null,return,switch,this", + "throw,true,try,typeof,var,void,while,with", /* 关键字*/ + "abstract,boolean,byte,char,class,const,double,enum,export,extends", + "final,float,goto,implements,import,int,interface,long,native", + "package,private,protected,public,short,static,super,synchronized", + "throws,transient,volatile", /*保留字*/ + "arguments,let,yield,undefined" /* ECMA 5 - use strict*/].join(",") +var rrexpstr = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g +var rsplit = /[^\w$]+/g +var rkeywords = new RegExp(["\\b" + keywords.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g') +var rnumber = /\b\d[^,]*/g +var rcomma = /^,+|,+$/g +var variablePool = new Cache(512) +var getVariables = function (code) { + var key = "," + code.trim() + var ret = variablePool.get(key) + if (ret) { + return ret + } + var match = code + .replace(rrexpstr, "") + .replace(rsplit, ",") + .replace(rkeywords, "") + .replace(rnumber, "") + .replace(rcomma, "") + .split(/^$|,+/) + return variablePool.put(key, uniqSet(match)) +} +/*添加赋值语句*/ + +function addAssign(vars, scope, name, data) { + var ret = [], + prefix = " = " + name + "." + for (var i = vars.length, prop; prop = vars[--i]; ) { + if (scope.hasOwnProperty(prop)) { + ret.push(prop + prefix + prop) + data.vars.push(prop) + if (data.type === "duplex") { + vars.get = name + "." + prop + } + vars.splice(i, 1) + } + } + return ret +} + +function uniqSet(array) { + var ret = [], + unique = {} + for (var i = 0; i < array.length; i++) { + var el = array[i] + var id = el && typeof el.$id === "string" ? el.$id : el + if (!unique[id]) { + unique[id] = ret.push(el) + } + } + return ret +} +//缓存求值函数,以便多次利用 +var evaluatorPool = new Cache(128) +//取得求值函数及其传参 +var rduplex = /\w\[.*\]|\w\.\w/ +var rproxy = /(\$proxy\$[a-z]+)\d+$/ +var rthimRightParentheses = /\)\s*$/ +var rthimOtherParentheses = /\)\s*\|/g +var rquoteFilterName = /\|\s*([$\w]+)/g +var rpatchBracket = /"\s*\["/g +var rthimLeftParentheses = /"\s*\(/g +function parseFilter(val, filters) { + filters = filters + .replace(rthimRightParentheses, "")//处理最后的小括号 + .replace(rthimOtherParentheses, function () {//处理其他小括号 + return "],|" + }) + .replace(rquoteFilterName, function (a, b) { //处理|及它后面的过滤器的名字 + return "[" + quote(b) + }) + .replace(rpatchBracket, function () { + return '"],["' + }) + .replace(rthimLeftParentheses, function () { + return '",' + }) + "]" + return "return avalon.filters.$filter(" + val + ", " + filters + ")" +} + +function parseExpr(code, scopes, data) { + var dataType = data.type + var filters = data.filters || "" + var exprId = scopes.map(function (el) { + return String(el.$id).replace(rproxy, "$1") + }) + code + dataType + filters + var vars = getVariables(code).concat(), + assigns = [], + names = [], + args = [], + prefix = "" + //args 是一个对象数组, names 是将要生成的求值函数的参数 + scopes = uniqSet(scopes) + data.vars = [] + for (var i = 0, sn = scopes.length; i < sn; i++) { + if (vars.length) { + var name = "vm" + expose + "_" + i + names.push(name) + args.push(scopes[i]) + assigns.push.apply(assigns, addAssign(vars, scopes[i], name, data)) + } + } + if (!assigns.length && dataType === "duplex") { + return + } + if (dataType !== "duplex" && (code.indexOf("||") > -1 || code.indexOf("&&") > -1)) { + //https://github.com/RubyLouvre/avalon/issues/583 + data.vars.forEach(function (v) { + var reg = new RegExp("\\b" + v + "(?:\\.\\w+|\\[\\w+\\])+", "ig") + code = code.replace(reg, function (_) { + var c = _.charAt(v.length) + var r = IEVersion ? code.slice(arguments[1] + _.length) : RegExp.rightContext + var method = /^\s*\(/.test(r) + if (c === "." || c === "[" || method) {//比如v为aa,我们只匹配aa.bb,aa[cc],不匹配aaa.xxx + var name = "var" + String(Math.random()).replace(/^0\./, "") + if (method) {//array.size() + var array = _.split(".") + if (array.length > 2) { + var last = array.pop() + assigns.push(name + " = " + array.join(".")) + return name + "." + last + } else { + return _ + } + } + assigns.push(name + " = " + _) + return name + } else { + return _ + } + }) + }) + } + //---------------args---------------- + data.args = args + //---------------cache---------------- + delete data.vars + var fn = evaluatorPool.get(exprId) //直接从缓存,免得重复生成 + if (fn) { + data.evaluator = fn + return + } + prefix = assigns.join(", ") + if (prefix) { + prefix = "var " + prefix + } + if (/\S/.test(filters)) { //文本绑定,双工绑定才有过滤器 + if (!/text|html/.test(data.type)) { + throw Error("ms-" + data.type + "不支持过滤器") + } + code = "\nvar ret" + expose + " = " + code + ";\r\n" + code += parseFilter("ret" + expose, filters) + } else if (dataType === "duplex") { //双工绑定 + var _body = "'use strict';\nreturn function(vvv){\n\t" + + prefix + + ";\n\tif(!arguments.length){\n\t\treturn " + + code + + "\n\t}\n\t" + (!rduplex.test(code) ? vars.get : code) + + "= vvv;\n} " + try { + fn = Function.apply(noop, names.concat(_body)) + data.evaluator = evaluatorPool.put(exprId, fn) + } catch (e) { + log("debug: parse error," + e.message) + } + return + } else if (dataType === "on") { //事件绑定 + if (code.indexOf("(") === -1) { + code += ".call(this, $event)" + } else { + code = code.replace("(", ".call(this,") + } + names.push("$event") + code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;") + var lastIndex = code.lastIndexOf("\nreturn") + var header = code.slice(0, lastIndex) + var footer = code.slice(lastIndex) + code = header + "\n" + footer + } else { //其他绑定 + code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;") + } + try { + fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code)) + data.evaluator = evaluatorPool.put(exprId, fn) + } catch (e) { + log("debug: parse error," + e.message) + } finally { + vars = assigns = names = null //释放内存 + } +} + + +//parseExpr的智能引用代理 + +function parseExprProxy(code, scopes, data, tokens, noRegister) { + if (Array.isArray(tokens)) { + code = tokens.map(function (el) { + return el.expr ? "(" + el.value + ")" : quote(el.value) + }).join(" + ") + } + parseExpr(code, scopes, data) + if (data.evaluator && !noRegister) { + data.handler = bindingExecutors[data.handlerName || data.type] + //方便调试 + //这里非常重要,我们通过判定视图刷新函数的element是否在DOM树决定 + //将它移出订阅者列表 + avalon.injectBinding(data) + } +} +avalon.parseExprProxy = parseExprProxy +/********************************************************************* + * 扫描系统 * + **********************************************************************/ + +avalon.scan = function(elem, vmodel) { + elem = elem || root + var vmodels = vmodel ? [].concat(vmodel) : [] + scanTag(elem, vmodels) +} + +//http://www.w3.org/TR/html5/syntax.html#void-elements +var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase()) + +function checkScan(elem, callback, innerHTML) { + var id = setTimeout(function() { + var currHTML = elem.innerHTML + clearTimeout(id) + if (currHTML === innerHTML) { + callback() + } else { + checkScan(elem, callback, currHTML) + } + }) +} + + +function createSignalTower(elem, vmodel) { + var id = elem.getAttribute("avalonctrl") || vmodel.$id + elem.setAttribute("avalonctrl", id) + vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]' +} + +var getBindingCallback = function(elem, name, vmodels) { + var callback = elem.getAttribute(name) + if (callback) { + for (var i = 0, vm; vm = vmodels[i++]; ) { + if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") { + return vm[callback] + } + } + } +} + +function executeBindings(bindings, vmodels) { + for (var i = 0, data; data = bindings[i++]; ) { + data.vmodels = vmodels + bindingHandlers[data.type](data, vmodels) + if (data.evaluator && data.element && data.element.nodeType === 1) { //移除数据绑定,防止被二次解析 + //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99 + data.element.removeAttribute(data.name) + } + } + bindings.length = 0 +} + +//https://github.com/RubyLouvre/avalon/issues/636 +var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) { + var node = elem.firstChild, text + while (node) { + var aaa = node.nextSibling + if (node.nodeType === 3) { + if (text) { + text.nodeValue += node.nodeValue + elem.removeChild(node) + } else { + text = node + } + } else { + text = null + } + node = aaa + } +} : 0 +var roneTime = /^\s*::/ +var rmsAttr = /ms-(\w+)-?(.*)/ +var priorityMap = { + "if": 10, + "repeat": 90, + "data": 100, + "widget": 110, + "each": 1400, + "with": 1500, + "duplex": 2000, + "on": 3000 +} + +var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit") +var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled") +function bindingSorter(a, b) { + return a.priority - b.priority +} + +function scanAttr(elem, vmodels, match) { + var scanNode = true + if (vmodels.length) { + var attributes = getAttributes ? getAttributes(elem) : elem.attributes + var bindings = [] + var fixAttrs = [] + var msData = {} + for (var i = 0, attr; attr = attributes[i++]; ) { + if (attr.specified) { + if (match = attr.name.match(rmsAttr)) { + //如果是以指定前缀命名的 + var type = match[1] + var param = match[2] || "" + var value = attr.value + var name = attr.name + if (events[type]) { + param = type + type = "on" + } else if (obsoleteAttrs[type]) { + if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替 + log("warning!ms-enabled或ms-attr-enabled已经被废弃") + type = "disabled" + value = "!(" + value + ")" + } + param = type + type = "attr" + name = "ms-" + type + "-"+ param + fixAttrs.push([attr.name, name, value]) + } + msData[name] = value + if (typeof bindingHandlers[type] === "function") { + var newValue = value.replace(roneTime, "") + var oneTime = value !== newValue + var binding = { + type: type, + param: param, + element: elem, + name: name, + value: newValue, + oneTime: oneTime, + uuid: name+"-"+getUid(elem), + //chrome与firefox下Number(param)得到的值不一样 #855 + priority: (priorityMap[type] || type.charCodeAt(0) * 10 )+ (Number(param.replace(/\D/g, "")) || 0) + } + if (type === "html" || type === "text") { + var token = getToken(value) + avalon.mix(binding, token) + binding.filters = binding.filters.replace(rhasHtml, function () { + binding.type = "html" + binding.group = 1 + return "" + })// jshint ignore:line + } else if (type === "duplex") { + var hasDuplex = name + } else if (name === "ms-if-loop") { + binding.priority += 100 + } + bindings.push(binding) + if (type === "widget") { + elem.msData = elem.msData || msData + } + } + } + } + } + if (bindings.length) { + bindings.sort(bindingSorter) + fixAttrs.forEach(function (arr) { + log("warning!请改用" + arr[1] + "代替" + arr[0] + "!") + elem.removeAttribute(arr[0]) + elem.setAttribute(arr[1], arr[2]) + }) + //http://bugs.jquery.com/ticket/7071 + //在IE下对VML读取type属性,会让此元素所有属性都变成 + if (hasDuplex) { + if (msData["ms-attr-checked"]) { + log("warning!一个控件不能同时定义ms-attr-checked与" + hasDuplex) + } + if (msData["ms-attr-value"]) { + log("warning!一个控件不能同时定义ms-attr-value与" + hasDuplex) + } + } + for (i = 0; binding = bindings[i]; i++) { + type = binding.type + if (rnoscanAttrBinding.test(type)) { + return executeBindings(bindings.slice(0, i + 1), vmodels) + } else if (scanNode) { + scanNode = !rnoscanNodeBinding.test(type) + } + } + executeBindings(bindings, vmodels) + } + } + if (scanNode && !stopScan[elem.tagName] && rbind.test(elem.innerHTML.replace(rlt, "<").replace(rgt, ">"))) { + mergeTextNodes && mergeTextNodes(elem) + scanNodeList(elem, vmodels) //扫描子孙元素 + } +} +var rnoscanAttrBinding = /^if|widget|repeat$/ +var rnoscanNodeBinding = /^each|with|html|include$/ +//IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支, +//但如果我们去掉scanAttr中的attr.specified检测,一个元素会有80+个特性节点(因为它不区分固有属性与自定义属性),很容易卡死页面 +if (!"1" [0]) { + var attrPool = new Cache(512) + var rattrs = /\s+(ms-[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g, + rquote = /^['"]/, + rtag = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/i, + ramp = /&/g + //IE6-8解析HTML5新标签,会将它分解两个元素节点与一个文本节点 + //
ddd
+ // window.onload = function() { + // var body = document.body + // for (var i = 0, el; el = body.children[i++]; ) { + // avalon.log(el.outerHTML) + // } + // } + //依次输出
,
+ var getAttributes = function (elem) { + var html = elem.outerHTML + //处理IE6-8解析HTML5新标签的情况,及
等半闭合标签outerHTML为空的情况 + if (html.slice(0, 2) === " ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100) + //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后 + var a = elem.getAttribute("ms-skip") + //#360 在旧式IE中 Object标签在引入Flash等资源时,可能出现没有getAttributeNode,innerHTML的情形 + if (!elem.getAttributeNode) { + return log("warning " + elem.tagName + " no getAttributeNode method") + } + var b = elem.getAttributeNode("ms-important") + var c = elem.getAttributeNode("ms-controller") + if (typeof a === "string") { + return + } else if (node = b || c) { + var newVmodel = avalon.vmodels[node.value] + if (!newVmodel) { + return + } + //ms-important不包含父VM,ms-controller相反 + vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels) + var name = node.name + elem.removeAttribute(name) //removeAttributeNode不会刷新[ms-controller]样式规则 + avalon(elem).removeClass(name) + createSignalTower(elem, newVmodel) + } + scanAttr(elem, vmodels) //扫描特性节点 +} +var rhasHtml = /\|\s*html(?:\b|$)/, + r11a = /\|\|/g, + rlt = /</g, + rgt = />/g, + rstringLiteral = /(['"])(\\\1|.)+?\1/g +function getToken(value) { + if (value.indexOf("|") > 0) { + var scapegoat = value.replace(rstringLiteral, function (_) { + return Array(_.length + 1).join("1")// jshint ignore:line + }) + var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或 + if (index > -1) { + return { + filters: value.slice(index), + value: value.slice(0, index), + expr: true + } + } + } + return { + value: value, + filters: "", + expr: true + } +} + +function scanExpr(str) { + var tokens = [], + value, start = 0, + stop + do { + stop = str.indexOf(openTag, start) + if (stop === -1) { + break + } + value = str.slice(start, stop) + if (value) { // {{ 左边的文本 + tokens.push({ + value: value, + filters: "", + expr: false + }) + } + start = stop + openTag.length + stop = str.indexOf(closeTag, start) + if (stop === -1) { + break + } + value = str.slice(start, stop) + if (value) { //处理{{ }}插值表达式 + tokens.push(getToken(value, start)) + } + start = stop + closeTag.length + } while (1) + value = str.slice(start) + if (value) { //}} 右边的文本 + tokens.push({ + value: value, + expr: false, + filters: "" + }) + } + return tokens +} + +function scanText(textNode, vmodels, index) { + var bindings = [] + tokens = scanExpr(textNode.data) + if (tokens.length) { + for (var i = 0; token = tokens[i++]; ) { + var node = DOC.createTextNode(token.value) //将文本转换为文本节点,并替换原来的文本节点 + if (token.expr) { + token.value = token.value.replace(roneTime, function () { + token.oneTime = true + return "" + }) + token.type = "text" + token.element = node + token.filters = token.filters.replace(rhasHtml, function (a, b,c) { + token.type = "html" + return "" + })// jshint ignore:line + token.pos = index * 1000 + i + bindings.push(token) //收集带有插值表达式的文本 + } + avalonFragment.appendChild(node) + } + textNode.parentNode.replaceChild(avalonFragment, textNode) + if (bindings.length) + executeBindings(bindings, vmodels) + } +} + +var bools = ["autofocus,autoplay,async,allowTransparency,checked,controls", + "declare,disabled,defer,defaultChecked,defaultSelected", + "contentEditable,isMap,loop,multiple,noHref,noResize,noShade", + "open,readOnly,selected" +].join(",") +var boolMap = {} +bools.replace(rword, function(name) { + boolMap[name.toLowerCase()] = name +}) + +var propMap = { //属性名映射 + "accept-charset": "acceptCharset", + "char": "ch", + "charoff": "chOff", + "class": "className", + "for": "htmlFor", + "http-equiv": "httpEquiv" +} + +var anomaly = ["accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan", + "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight", + "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign" +].join(",") +anomaly.replace(rword, function(name) { + propMap[name.toLowerCase()] = name +}) + +var rnoscripts = /(?:[\s\S]+?)<\/noscript>/img +var rnoscriptText = /([\s\S]+?)<\/noscript>/im + +var getXHR = function() { + return new(window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP") // jshint ignore:line +} + +var templatePool = avalon.templateCache = {} + +bindingHandlers.attr = function(data, vmodels) { + var text = data.value.trim(), + simple = true + if (text.indexOf(openTag) > -1 && text.indexOf(closeTag) > 2) { + simple = false + if (rexpr.test(text) && RegExp.rightContext === "" && RegExp.leftContext === "") { + simple = true + text = RegExp.$1 + } + } + if (data.type === "include") { + var elem = data.element + data.includeRendered = getBindingCallback(elem, "data-include-rendered", vmodels) + data.includeLoaded = getBindingCallback(elem, "data-include-loaded", vmodels) + var outer = data.includeReplace = !! avalon(elem).data("includeReplace") + if (avalon(elem).data("includeCache")) { + data.templateCache = {} + } + data.startInclude = DOC.createComment("ms-include") + data.endInclude = DOC.createComment("ms-include-end") + if (outer) { + data.element = data.startInclude + elem.parentNode.insertBefore(data.startInclude, elem) + elem.parentNode.insertBefore(data.endInclude, elem.nextSibling) + } else { + elem.insertBefore(data.startInclude, elem.firstChild) + elem.appendChild(data.endInclude) + } + } + data.handlerName = "attr" //handleName用于处理多种绑定共用同一种bindingExecutor的情况 + parseExprProxy(text, vmodels, data, (simple ? 0 : scanExpr(data.value))) +} + +bindingExecutors.attr = function(val, elem, data) { + var method = data.type, + attrName = data.param + if (method === "css") { + avalon(elem).css(attrName, val) + } else if (method === "attr") { + + // ms-attr-class="xxx" vm.xxx="aaa bbb ccc"将元素的className设置为aaa bbb ccc + // ms-attr-class="xxx" vm.xxx=false 清空元素的所有类名 + // ms-attr-name="yyy" vm.yyy="ooo" 为元素设置name属性 + var toRemove = (val === false) || (val === null) || (val === void 0) + + if (!W3C && propMap[attrName]) { //旧式IE下需要进行名字映射 + attrName = propMap[attrName] + } + var bool = boolMap[attrName] + if (typeof elem[bool] === "boolean") { + elem[bool] = !! val //布尔属性必须使用el.xxx = true|false方式设值 + if (!val) { //如果为false, IE全系列下相当于setAttribute(xxx,''),会影响到样式,需要进一步处理 + toRemove = true + } + } + if (toRemove) { + return elem.removeAttribute(attrName) + } + //SVG只能使用setAttribute(xxx, yyy), VML只能使用elem.xxx = yyy ,HTML的固有属性必须elem.xxx = yyy + var isInnate = rsvg.test(elem) ? false : (DOC.namespaces && isVML(elem)) ? true : attrName in elem.cloneNode(false) + if (isInnate) { + elem[attrName] = val+"" + } else { + elem.setAttribute(attrName, val) + } + } else if (method === "include" && val) { + var vmodels = data.vmodels + var rendered = data.includeRendered + var loaded = data.includeLoaded + var replace = data.includeReplace + var target = replace ? elem.parentNode : elem + var scanTemplate = function(text) { + if (loaded) { + var newText = loaded.apply(target, [text].concat(vmodels)) + if (typeof newText === "string") + text = newText + } + if (rendered) { + checkScan(target, function() { + rendered.call(target) + }, NaN) + } + var lastID = data.includeLastID + if (data.templateCache && lastID && lastID !== val) { + var lastTemplate = data.templateCache[lastID] + if (!lastTemplate) { + lastTemplate = data.templateCache[lastID] = DOC.createElement("div") + ifGroup.appendChild(lastTemplate) + } + } + data.includeLastID = val + while (true) { + var node = data.startInclude.nextSibling + if (node && node !== data.endInclude) { + target.removeChild(node) + if (lastTemplate) + lastTemplate.appendChild(node) + } else { + break + } + } + var dom = getTemplateNodes(data, val, text) + var nodes = avalon.slice(dom.childNodes) + target.insertBefore(dom, data.endInclude) + scanNodeArray(nodes, vmodels) + } + + if (data.param === "src") { + if (typeof templatePool[val] === "string") { + avalon.nextTick(function() { + scanTemplate(templatePool[val]) + }) + } else if (Array.isArray(templatePool[val])) { //#805 防止在循环绑定中发出许多相同的请求 + templatePool[val].push(scanTemplate) + } else { + var xhr = getXHR() + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + var s = xhr.status + if (s >= 200 && s < 300 || s === 304 || s === 1223) { + var text = xhr.responseText + for (var f = 0, fn; fn = templatePool[val][f++];) { + fn(text) + } + templatePool[val] = text + } + } + } + templatePool[val] = [scanTemplate] + xhr.open("GET", val, true) + if ("withCredentials" in xhr) { + xhr.withCredentials = true + } + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest") + xhr.send(null) + } + } else { + //IE系列与够新的标准浏览器支持通过ID取得元素(firefox14+) + //http://tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/ + var el = val && val.nodeType === 1 ? val : DOC.getElementById(val) + if (el) { + if (el.tagName === "NOSCRIPT" && !(el.innerHTML || el.fixIE78)) { //IE7-8 innerText,innerHTML都无法取得其内容,IE6能取得其innerHTML + xhr = getXHR() //IE9-11与chrome的innerHTML会得到转义的内容,它们的innerText可以 + xhr.open("GET", location, false) //谢谢Nodejs 乱炖群 深圳-纯属虚构 + xhr.send(null) + //http://bbs.csdn.net/topics/390349046?page=1#post-393492653 + var noscripts = DOC.getElementsByTagName("noscript") + var array = (xhr.responseText || "").match(rnoscripts) || [] + var n = array.length + for (var i = 0; i < n; i++) { + var tag = noscripts[i] + if (tag) { //IE6-8中noscript标签的innerHTML,innerText是只读的 + tag.style.display = "none" //http://haslayout.net/css/noscript-Ghost-Bug + tag.fixIE78 = (array[i].match(rnoscriptText) || ["", " "])[1] + } + } + } + avalon.nextTick(function() { + scanTemplate(el.fixIE78 || el.value || el.innerText || el.innerHTML) + }) + } + } + } else { + if (!root.hasAttribute && typeof val === "string" && (method === "src" || method === "href")) { + val = val.replace(/&/g, "&") //处理IE67自动转义的问题 + } + elem[method] = val + if (window.chrome && elem.tagName === "EMBED") { + var parent = elem.parentNode //#525 chrome1-37下embed标签动态设置src不能发生请求 + var comment = document.createComment("ms-src") + parent.replaceChild(comment, elem) + parent.replaceChild(elem, comment) + } + } +} + +function getTemplateNodes(data, id, text) { + var div = data.templateCache && data.templateCache[id] + if (div) { + var dom = DOC.createDocumentFragment(), + firstChild + while (firstChild = div.firstChild) { + dom.appendChild(firstChild) + } + return dom + } + return avalon.parseHTML(text) +} + +//这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html" +"title,alt,src,value,css,include,href".replace(rword, function(name) { + bindingHandlers[name] = bindingHandlers.attr +}) +//根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag" +//http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html +bindingHandlers["class"] = function(data, vmodels) { + var oldStyle = data.param, + text = data.value, + rightExpr + data.handlerName = "class" + if (!oldStyle || isFinite(oldStyle)) { + data.param = "" //去掉数字 + var noExpr = text.replace(rexprg, function(a) { + return a.replace(/./g, "0") + //return Math.pow(10, a.length - 1) //将插值表达式插入10的N-1次方来占位 + }) + var colonIndex = noExpr.indexOf(":") //取得第一个冒号的位置 + if (colonIndex === -1) { // 比如 ms-class="aaa bbb ccc" 的情况 + var className = text + } else { // 比如 ms-class-1="ui-state-active:checked" 的情况 + className = text.slice(0, colonIndex) + rightExpr = text.slice(colonIndex + 1) + parseExpr(rightExpr, vmodels, data) //决定是添加还是删除 + if (!data.evaluator) { + log("debug: ms-class '" + (rightExpr || "").trim() + "' 不存在于VM中") + return false + } else { + data._evaluator = data.evaluator + data._args = data.args + } + } + var hasExpr = rexpr.test(className) //比如ms-class="width{{w}}"的情况 + if (!hasExpr) { + data.immobileClass = className + } + parseExprProxy("", vmodels, data, (hasExpr ? scanExpr(className) : 0)) + } else { + data.immobileClass = data.oldStyle = data.param + parseExprProxy(text, vmodels, data) + } +} + +bindingExecutors["class"] = function(val, elem, data) { + var $elem = avalon(elem), + method = data.type + if (method === "class" && data.oldStyle) { //如果是旧风格 + $elem.toggleClass(data.oldStyle, !! val) + } else { + //如果存在冒号就有求值函数 + data.toggleClass = data._evaluator ? !! data._evaluator.apply(elem, data._args) : true + data.newClass = data.immobileClass || val + if (data.oldClass && data.newClass !== data.oldClass) { + $elem.removeClass(data.oldClass) + } + data.oldClass = data.newClass + switch (method) { + case "class": + $elem.toggleClass(data.newClass, data.toggleClass) + break + case "hover": + case "active": + if (!data.hasBindEvent) { //确保只绑定一次 + var activate = "mouseenter" //在移出移入时切换类名 + var abandon = "mouseleave" + if (method === "active") { //在聚焦失焦中切换类名 + elem.tabIndex = elem.tabIndex || -1 + activate = "mousedown" + abandon = "mouseup" + var fn0 = $elem.bind("mouseleave", function() { + data.toggleClass && $elem.removeClass(data.newClass) + }) + } + var fn1 = $elem.bind(activate, function() { + data.toggleClass && $elem.addClass(data.newClass) + }) + var fn2 = $elem.bind(abandon, function() { + data.toggleClass && $elem.removeClass(data.newClass) + }) + data.rollback = function() { + $elem.unbind("mouseleave", fn0) + $elem.unbind(activate, fn1) + $elem.unbind(abandon, fn2) + } + data.hasBindEvent = true + } + break; + } + } +} + +"hover,active".replace(rword, function(method) { + bindingHandlers[method] = bindingHandlers["class"] +}) +//ms-controller绑定已经在scanTag 方法中实现 +//ms-css绑定已由ms-attr绑定实现 + + +// bindingHandlers.data 定义在if.js +bindingExecutors.data = function(val, elem, data) { + var key = "data-" + data.param + if (val && typeof val === "object") { + elem[key] = val + } else { + elem.setAttribute(key, String(val)) + } +} +//双工绑定 +var duplexBinding = bindingHandlers.duplex = function(data, vmodels) { + var elem = data.element, + hasCast + parseExprProxy(data.value, vmodels, data, 0, 1) + + data.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop + if (data.evaluator && data.args) { + var params = [] + var casting = oneObject("string,number,boolean,checked") + if (elem.type === "radio" && data.param === "") { + data.param = "checked" + } + if (elem.msData) { + elem.msData["ms-duplex"] = data.value + } + data.param.replace(/\w+/g, function(name) { + if (/^(checkbox|radio)$/.test(elem.type) && /^(radio|checked)$/.test(name)) { + if (name === "radio") + log("ms-duplex-radio已经更名为ms-duplex-checked") + name = "checked" + data.isChecked = true + } + if (name === "bool") { + name = "boolean" + log("ms-duplex-bool已经更名为ms-duplex-boolean") + } else if (name === "text") { + name = "string" + log("ms-duplex-text已经更名为ms-duplex-string") + } + if (casting[name]) { + hasCast = true + } + avalon.Array.ensure(params, name) + }) + if (!hasCast) { + params.push("string") + } + data.param = params.join("-") + data.bound = function(type, callback) { + if (elem.addEventListener) { + elem.addEventListener(type, callback, false) + } else { + elem.attachEvent("on" + type, callback) + } + var old = data.rollback + data.rollback = function() { + elem.avalonSetter = null + avalon.unbind(elem, type, callback) + old && old() + } + } + for (var i in avalon.vmodels) { + var v = avalon.vmodels[i] + v.$fire("avalon-ms-duplex-init", data) + } + var cpipe = data.pipe || (data.pipe = pipe) + cpipe(null, data, "init") + var tagName = elem.tagName + duplexBinding[tagName] && duplexBinding[tagName](elem, data.evaluator.apply(null, data.args), data) + } +} +//不存在 bindingExecutors.duplex + + function fixNull(val) { + return val == null ? "" : val + } +avalon.duplexHooks = { + checked: { + get: function(val, data) { + return !data.element.oldValue + } + }, + string: { + get: function(val) { //同步到VM + return val + }, + set: fixNull + }, + "boolean": { + get: function(val) { + return val === "true" + }, + set: fixNull + }, + number: { + get: function(val, data) { + var number = parseFloat(val) + if (-val === -number) { + return number + } + var arr = /strong|medium|weak/.exec(data.element.getAttribute("data-duplex-number")) || ["medium"] + switch (arr[0]) { + case "strong": + return 0 + case "medium": + return val === "" ? "" : 0 + case "weak": + return val + } + }, + set: fixNull + } +} + +function pipe(val, data, action, e) { + data.param.replace(/\w+/g, function(name) { + var hook = avalon.duplexHooks[name] + if (hook && typeof hook[action] === "function") { + val = hook[action](val, data) + } + }) + return val +} + +var TimerID, ribbon = [] + + avalon.tick = function(fn) { + if (ribbon.push(fn) === 1) { + TimerID = setInterval(ticker, 60) + } + } + + function ticker() { + for (var n = ribbon.length - 1; n >= 0; n--) { + var el = ribbon[n] + if (el() === false) { + ribbon.splice(n, 1) + } + } + if (!ribbon.length) { + clearInterval(TimerID) + } + } + +var watchValueInTimer = noop +var rmsinput = /text|password|hidden/ +new function() { // jshint ignore:line + try { //#272 IE9-IE11, firefox + var setters = {} + var aproto = HTMLInputElement.prototype + var bproto = HTMLTextAreaElement.prototype + function newSetter(value) { // jshint ignore:line + setters[this.tagName].call(this, value) + if (rmsinput.test(this.type) && !this.msFocus && this.avalonSetter) { + this.avalonSetter() + } + } + var inputProto = HTMLInputElement.prototype + Object.getOwnPropertyNames(inputProto) //故意引发IE6-8等浏览器报错 + setters["INPUT"] = Object.getOwnPropertyDescriptor(aproto, "value").set + + Object.defineProperty(aproto, "value", { + set: newSetter + }) + setters["TEXTAREA"] = Object.getOwnPropertyDescriptor(bproto, "value").set + Object.defineProperty(bproto, "value", { + set: newSetter + }) + } catch (e) { + //在chrome 43中 ms-duplex终于不需要使用定时器实现双向绑定了 + // http://updates.html5rocks.com/2015/04/DOM-attributes-now-on-the-prototype + // https://docs.google.com/document/d/1jwA8mtClwxI-QJuHT7872Z0pxpZz8PBkf2bGAbsUtqs/edit?pli=1 + watchValueInTimer = avalon.tick + } +} // jshint ignore:line +if (IEVersion) { + avalon.bind(DOC, "selectionchange", function(e) { + var el = DOC.activeElement + if (el && typeof el.avalonSetter === "function") { + el.avalonSetter() + } + }) +} + +//处理radio, checkbox, text, textarea, password +duplexBinding.INPUT = function(element, evaluator, data) { + var $type = element.type, + bound = data.bound, + $elem = avalon(element), + composing = false + + function callback(value) { + data.changed.call(this, value, data) + } + + function compositionStart() { + composing = true + } + + function compositionEnd() { + composing = false + } + //当value变化时改变model的值 + var updateVModel = function() { + if (composing) //处理中文输入法在minlengh下引发的BUG + return + var val = element.oldValue = element.value //防止递归调用形成死循环 + var lastValue = data.pipe(val, data, "get") + if ($elem.data("duplexObserve") !== false) { + evaluator(lastValue) + callback.call(element, lastValue) + if ($elem.data("duplex-focus")) { + avalon.nextTick(function() { + element.focus() + }) + } + } + } + //当model变化时,它就会改变value的值 + data.handler = function() { + var val = data.pipe(evaluator(), data, "set") + "" //fix #673 + if (val !== element.oldValue) { + element.value = val + } + } + if (data.isChecked || $type === "radio") { + var IE6 = IEVersion === 6 + updateVModel = function() { + if ($elem.data("duplexObserve") !== false) { + var lastValue = data.pipe(element.value, data, "get") + evaluator(lastValue) + callback.call(element, lastValue) + } + } + data.handler = function() { + var val = evaluator() + var checked = data.isChecked ? !! val : val + "" === element.value + element.oldValue = checked + if (IE6) { + setTimeout(function() { + //IE8 checkbox, radio是使用defaultChecked控制选中状态, + //并且要先设置defaultChecked后设置checked + //并且必须设置延迟 + element.defaultChecked = checked + element.checked = checked + }, 31) + } else { + element.checked = checked + } + } + bound("click", updateVModel) + } else if ($type === "checkbox") { + updateVModel = function() { + if ($elem.data("duplexObserve") !== false) { + var method = element.checked ? "ensure" : "remove" + var array = evaluator() + if (!Array.isArray(array)) { + log("ms-duplex应用于checkbox上要对应一个数组") + array = [array] + } + var val = data.pipe(element.value, data, "get") + avalon.Array[method](array, val) + callback.call(element, array) + } + } + + data.handler = function() { + var array = [].concat(evaluator()) //强制转换为数组 + var val = data.pipe(element.value, data, "get") + element.checked = array.indexOf(val) > -1 + } + bound(W3C ? "change" : "click", updateVModel) + } else { + var events = element.getAttribute("data-duplex-event") || "input" + if (element.attributes["data-event"]) { + log("data-event指令已经废弃,请改用data-duplex-event") + } + + function delay(e) { // jshint ignore:line + setTimeout(function() { + updateVModel(e) + }) + } + events.replace(rword, function(name) { + switch (name) { + case "input": + if (!IEVersion) { // W3C + bound("input", updateVModel) + //非IE浏览器才用这个 + bound("compositionstart", compositionStart) + bound("compositionend", compositionEnd) + bound("DOMAutoComplete", updateVModel) + } else { //onpropertychange事件无法区分是程序触发还是用户触发 + // IE下通过selectionchange事件监听IE9+点击input右边的X的清空行为,及粘贴,剪切,删除行为 + if (IEVersion > 8) { + bound("input", updateVModel) //IE9使用propertychange无法监听中文输入改动 + } else { + bound("propertychange", function(e) { //IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正 + if (e.propertyName === "value") { + updateVModel() + } + }) + } + bound("dragend", delay) + //http://www.cnblogs.com/rubylouvre/archive/2013/02/17/2914604.html + //http://www.matts411.com/post/internet-explorer-9-oninput/ + } + break + default: + bound(name, updateVModel) + break + } + }) + bound("focus", function() { + element.msFocus = true + }) + bound("blur", function() { + element.msFocus = false + }) + + if (rmsinput.test($type)) { + watchValueInTimer(function() { + if (root.contains(element)) { + if (!element.msFocus && element.oldValue !== element.value) { + updateVModel() + } + } else if (!element.msRetain) { + return false + } + }) + } + + element.avalonSetter = updateVModel //#765 + } + + element.oldValue = element.value + avalon.injectBinding(data) + callback.call(element, element.value) +} +duplexBinding.TEXTAREA = duplexBinding.INPUT +duplexBinding.SELECT = function(element, evaluator, data) { + var $elem = avalon(element) + + function updateVModel() { + if ($elem.data("duplexObserve") !== false) { + var val = $elem.val() //字符串或字符串数组 + if (Array.isArray(val)) { + val = val.map(function(v) { + return data.pipe(v, data, "get") + }) + } else { + val = data.pipe(val, data, "get") + } + if (val + "" !== element.oldValue) { + evaluator(val) + } + data.changed.call(element, val, data) + } + } + data.handler = function() { + var val = evaluator() + val = val && val.$model || val + if (Array.isArray(val)) { + if (!element.multiple) { + log("ms-duplex在不能对应一个数组") + } + } + //必须变成字符串后才能比较 + val = Array.isArray(val) ? val.map(String) : val + "" + if (val + "" !== element.oldValue) { + $elem.val(val) + element.oldValue = val + "" + } + } + data.bound("change", updateVModel) + element.msCallback = function() { + avalon.injectBinding(data) + data.changed.call(element, evaluator(), data) + } +} +// bindingHandlers.html 定义在if.js +bindingExecutors.html = function (val, elem, data) { + var isHtmlFilter = elem.nodeType !== 1 + var parent = isHtmlFilter ? elem.parentNode : elem + if (!parent) + return + val = val == null ? "" : val + if (data.oldText !== val) { + data.oldText = val + } else { + return + } + if (elem.nodeType === 3) { + var signature = generateID("html") + parent.insertBefore(DOC.createComment(signature), elem) + data.element = DOC.createComment(signature + ":end") + parent.replaceChild(data.element, elem) + elem = data.element + } + if (typeof val !== "object") {//string, number, boolean + var fragment = avalon.parseHTML(String(val)) + } else if (val.nodeType === 11) { //将val转换为文档碎片 + fragment = val + } else if (val.nodeType === 1 || val.item) { + var nodes = val.nodeType === 1 ? val.childNodes : val.item + fragment = avalonFragment.cloneNode(true) + while (nodes[0]) { + fragment.appendChild(nodes[0]) + } + } + + nodes = avalon.slice(fragment.childNodes) + //插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空 + if (isHtmlFilter) { + var endValue = elem.nodeValue.slice(0, -4) + while (true) { + var node = elem.previousSibling + if (!node || node.nodeType === 8 && node.nodeValue === endValue) { + break + } else { + parent.removeChild(node) + } + } + parent.insertBefore(fragment, elem) + } else { + avalon.clearHTML(elem).appendChild(fragment) + } + scanNodeArray(nodes, data.vmodels) +} +bindingHandlers["if"] = + bindingHandlers.data = + bindingHandlers.text = + bindingHandlers.html = + function(data, vmodels) { + parseExprProxy(data.value, vmodels, data) +} + +bindingExecutors["if"] = function(val, elem, data) { + try { + if(!elem.parentNode) return + } catch(e) {return} + if (val) { //插回DOM树 + if (elem.nodeType === 8) { + elem.parentNode.replaceChild(data.template, elem) + // animate.enter(data.template, elem.parentNode) + elem = data.element = data.template //这时可能为null + } + if (elem.getAttribute(data.name)) { + elem.removeAttribute(data.name) + scanAttr(elem, data.vmodels) + } + data.rollback = null + } else { //移出DOM树,并用注释节点占据原位置 + if (elem.nodeType === 1) { + var node = data.element = DOC.createComment("ms-if") + elem.parentNode.replaceChild(node, elem) + // animate.leave(elem, node.parentNode, node) + data.template = elem //元素节点 + ifGroup.appendChild(elem) + data.rollback = function() { + if (elem.parentNode === ifGroup) { + ifGroup.removeChild(elem) + } + } + } + } +} +//ms-important绑定已经在scanTag 方法中实现 +//ms-include绑定已由ms-attr绑定实现 + +var rdash = /\(([^)]*)\)/ +bindingHandlers.on = function(data, vmodels) { + var value = data.value + data.type = "on" + var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10 + if (typeof bindingHandlers.on[eventType + "Hook"] === "function") { + bindingHandlers.on[eventType + "Hook"](data) + } + if (value.indexOf("(") > 0 && value.indexOf(")") > -1) { + var matched = (value.match(rdash) || ["", ""])[1].trim() + if (matched === "" || matched === "$event") { // aaa() aaa($event)当成aaa处理 + value = value.replace(rdash, "") + } + } + parseExprProxy(value, vmodels, data) +} + +bindingExecutors.on = function(callback, elem, data) { + callback = function(e) { + var fn = data.evaluator || noop + return fn.apply(this, data.args.concat(e)) + } + var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10 + if (eventType === "scan") { + callback.call(elem, { + type: eventType + }) + } else if (typeof data.specialBind === "function") { + data.specialBind(elem, callback) + } else { + var removeFn = avalon.bind(elem, eventType, callback) + } + data.rollback = function() { + if (typeof data.specialUnbind === "function") { + data.specialUnbind() + } else { + avalon.unbind(elem, eventType, removeFn) + } + } +} +bindingHandlers.repeat = function (data, vmodels) { + var type = data.type + parseExprProxy(data.value, vmodels, data, 0, 1) + data.proxies = [] + var freturn = false + try { + var $repeat = data.$repeat = data.evaluator.apply(0, data.args || []) + var xtype = avalon.type($repeat) + if (xtype !== "object" && xtype !== "array") { + freturn = true + avalon.log("warning:" + data.value + "只能是对象或数组") + } + } catch (e) { + freturn = true + } + var arr = data.value.split(".") || [] + if (arr.length > 1) { + arr.pop() + var n = arr[0] + for (var i = 0, v; v = vmodels[i++]; ) { + if (v && v.hasOwnProperty(n)) { + var events = v[n].$events || {} + events[subscribers] = events[subscribers] || [] + events[subscribers].push(data) + break + } + } + } + + var elem = data.element + if (elem.nodeType === 1) { + elem.removeAttribute(data.name) + data.sortedCallback = getBindingCallback(elem, "data-with-sorted", vmodels) + data.renderedCallback = getBindingCallback(elem, "data-" + type + "-rendered", vmodels) + var signature = generateID(type) + var start = DOC.createComment(signature) + var end = DOC.createComment(signature + ":end") + data.signature = signature + data.template = avalonFragment.cloneNode(false) + if (type === "repeat") { + var parent = elem.parentNode + parent.replaceChild(end, elem) + parent.insertBefore(start, end) + data.template.appendChild(elem) + } else { + while (elem.firstChild) { + data.template.appendChild(elem.firstChild) + } + elem.appendChild(start) + elem.appendChild(end) + } + data.element = end + data.handler = bindingExecutors.repeat + data.rollback = function () { + var elem = data.element + if (!elem) + return + data.handler("clear") + } + } + + if (freturn) { + return + } + + data.$outer = {} + var check0 = "$key" + var check1 = "$val" + if (Array.isArray($repeat)) { + check0 = "$first" + check1 = "$last" + } + + for (i = 0; v = vmodels[i++]; ) { + if (v.hasOwnProperty(check0) && v.hasOwnProperty(check1)) { + data.$outer = v + break + } + } + var $events = $repeat.$events + var $list = ($events || {})[subscribers] + injectDependency($list, data) + if (xtype === "object") { + data.$with = true + $repeat.$proxy || ($repeat.$proxy = {}) + data.handler("append", $repeat) + } else if ($repeat.length) { + data.handler("add", 0, $repeat.length) + } +} + +bindingExecutors.repeat = function (method, pos, el) { + if (!method && this.$with) { + method = "append" + var flag = "update" + } + if (method) { + var data = this, start, fragment + var end = data.element + var comments = getComments(data) + var parent = end.parentNode + var proxies = data.proxies + var transation = avalonFragment.cloneNode(false) + switch (method) { + case "add": //在pos位置后添加el数组(pos为插入位置,el为要插入的个数) + var n = pos + el + var fragments = [] + for (var i = pos; i < n; i++) { + var proxy = eachProxyAgent(i, data) + proxies.splice(i, 0, proxy) + shimController(data, transation, proxy, fragments) + } + var now = new Date() - 0 + avalon.optimize = avalon.optimize || now + for (i = 0; fragment = fragments[i++]; ) { + scanNodeArray(fragment.nodes, fragment.vmodels) + fragment.nodes = fragment.vmodels = null + } + if (avalon.optimize === now) { + avalon.optimize = null + } + parent.insertBefore(transation, comments[pos] || end) + avalon.profile("插入操作花费了 " + (new Date - now)) + break + case "del": //将pos后的el个元素删掉(pos, el都是数字) + sweepNodes(comments[pos], comments[pos + el] || end) + var removed = proxies.splice(pos, el) + recycleProxies(removed, "each") + break + case "clear": + start = comments[0] + if (start) { + sweepNodes(start, end) + if (data.$with) { + parent.insertBefore(start, end) + } + } + recycleProxies(proxies, "each") + break + case "move": + start = comments[0] + if (start) { + var signature = start.nodeValue + var rooms = [] + var room = [], + node + sweepNodes(start, end, function () { + room.unshift(this) + if (this.nodeValue === signature) { + rooms.unshift(room) + room = [] + } + }) + sortByIndex(rooms, pos) + sortByIndex(proxies, pos) + while (room = rooms.shift()) { + while (node = room.shift()) { + transation.appendChild(node) + } + } + parent.insertBefore(transation, end) + } + break + case "index": //将proxies中的第pos个起的所有元素重新索引 + var last = proxies.length - 1 + for (; el = proxies[pos]; pos++) { + el.$index = pos + el.$first = pos === 0 + el.$last = pos === last + } + return + case "set": //将proxies中的第pos个元素的VM设置为el(pos为数字,el任意) + proxy = proxies[pos] + if (proxy) { + fireDependencies(proxy.$events[data.param || "el"]) + } + break + case "append": + var object = data.$repeat //原来第2参数, 被循环对象 + var oldProxy = object.$proxy //代理对象组成的hash + var keys = [] + now = new Date() - 0 + avalon.optimize = avalon.optimize || now + if (flag === "update") { + if (!data.evaluator) { + parseExprProxy(data.value, data.vmodels, data, 0, 1) + } + object = data.$repeat = data.evaluator.apply(0, data.args || []) + object.$proxy = oldProxy + } + var pool = object.$proxy || {} + removed = [] + var nodes = data.element.parentNode.childNodes + var add = false + for (i = 0; node = nodes[i++]; ) { + if (node.nodeValue === data.signature) { + add = true + } else if (node.nodeValue === data.signature + ":end") { + add = false + } + if (add) { + removed.push(node) + } + } + + var indexNode = [], item + var keyIndex = data.keyIndex || (data.keyIndex = {}) + //将现有的节点全部移出DOM树 + for ( i = 0; i < removed.length; i++) { + el = removed[i] + if (el.nodeValue === data.signature) { + item = avalonFragment.cloneNode(false) + indexNode.push(item) + } + item.appendChild(el) + } + + + for (var key in object) { //当前对象的所有键名 + if (object.hasOwnProperty(key) && key !== "hasOwnProperty" && key !== "$proxy") { + keys.push(key) + } + } + + for (var i = 0; key = keys[i++]; ) { + if (!pool.hasOwnProperty(key)) {//添加缺失的代理VM + pool[key] = withProxyAgent(pool[key], key, data) + } else { + pool[key].$val = object[key] + } + } + + for ( key in pool) { + if (keys.indexOf(key) === -1) {//删除没用的代理VM + proxyRecycler(pool[key], withProxyPool) //去掉之前的代理VM + delete pool[key] + } + } + var fragments = [] + var renderKeys = keys //需要渲染到DOM树去的键名 + var end = data.element + if (data.sortedCallback) { //如果有回调,则让它们排序 + var keys2 = data.sortedCallback.call(parent, keys) + if (keys2 && Array.isArray(keys2)) { + renderKeys = keys2 + } + } + + for (i = 0; i < renderKeys.length; i++) { + key = renderKeys[i] + if (typeof keyIndex[key] === "number") { + transation.appendChild(indexNode[keyIndex[key]]) + fragments.push({}) + } else { + shimController(data, transation, pool[key], fragments) + } + } + + for (i = 0; i < renderKeys.length; i++) { + keyIndex[renderKeys[i]] = i + } + + for (i = 0; fragment = fragments[i++]; ) { + if (fragment.nodes) { + scanNodeArray(fragment.nodes, fragment.vmodels) + fragment.nodes = fragment.vmodels = null + } + } + if (avalon.optimize === now) { + avalon.optimize = null + } + parent.insertBefore(transation, end) + avalon.profile("插入操作花费了 " + (new Date - now)) + break + } + if (!data.$repeat || data.$repeat.hasOwnProperty("$lock")) //IE6-8 VBScript对象会报错, 有时候data.$repeat不存在 + return + if (method === "clear") + method = "del" + var callback = data.renderedCallback || noop, + args = arguments + if (parent.oldValue && parent.tagName === "SELECT") { //fix #503 + avalon(parent).val(parent.oldValue.split(",")) + } + callback.apply(parent, args) + } +} +"with,each".replace(rword, function (name) { + bindingHandlers[name] = bindingHandlers.repeat +}) + +function shimController(data, transation, proxy, fragments) { + var content = data.template.cloneNode(true) + var nodes = avalon.slice(content.childNodes) + content.insertBefore(DOC.createComment(data.signature), content.firstChild) + transation.appendChild(content) + var nv = [proxy].concat(data.vmodels) + var fragment = { + nodes: nodes, + vmodels: nv + } + fragments.push(fragment) +} + +function getComments(data) { + var ret = [] + var nodes = data.element.parentNode.childNodes + for(var i= 0, node; node = nodes[i++];){ + if(node.nodeValue === data.signature){ + ret.push( node ) + }else if(node.nodeValue === data.signature+":end"){ + break + } + } + return ret +} + + +//移除掉start与end之间的节点(保留end) +function sweepNodes(start, end, callback) { + while (true) { + var node = end.previousSibling + if (!node) + break + node.parentNode.removeChild(node) + callback && callback.call(node) + if (node === start) { + break + } + } +} + +// 为ms-each,ms-with, ms-repeat会创建一个代理VM, +// 通过它们保持一个下上文,让用户能调用$index,$first,$last,$remove,$key,$val,$outer等属性与方法 +// 所有代理VM的产生,消费,收集,存放通过xxxProxyFactory,xxxProxyAgent, recycleProxies,xxxProxyPool实现 +var withProxyPool = [] +function withProxyFactory() { + var proxy = modelFactory({ + $key: "", + $outer: {}, + $host: {}, + $val: { + get: function () { + return this.$host[this.$key] + }, + set: function (val) { + this.$host[this.$key] = val + } + } + }, { + $val: 1 + }) + proxy.$id = generateID("$proxy$with") + return proxy +} + +function withProxyAgent(proxy, key, data) { + proxy = proxy || withProxyPool.pop() + if (!proxy) { + proxy = withProxyFactory() + } else { + proxy.$reinitialize() + } + var host = data.$repeat + proxy.$key = key + proxy.$host = host + proxy.$outer = data.$outer + if (host.$events) { + proxy.$events.$val = host.$events[key] + } else { + proxy.$events = {} + } + return proxy +} + + +function recycleProxies(proxies) { + eachProxyRecycler(proxies) +} +function eachProxyRecycler(proxies) { + proxies.forEach(function (proxy) { + proxyRecycler(proxy, eachProxyPool) + }) + proxies.length = 0 +} + + +var eachProxyPool = [] +function eachProxyFactory(name) { + var source = { + $host: [], + $outer: {}, + $index: 0, + $first: false, + $last: false, + $remove: avalon.noop + } + source[name] = { + get: function () { + var e = this.$events + var array = e.$index + e.$index = e[name] //#817 通过$index为el收集依赖 + try { + return this.$host[this.$index] + } finally { + e.$index = array + } + }, + set: function (val) { + try { + var e = this.$events + var array = e.$index + e.$index = [] + this.$host.set(this.$index, val) + } finally { + e.$index = array + } + } + } + var second = { + $last: 1, + $first: 1, + $index: 1 + } + var proxy = modelFactory(source, second) + proxy.$id = generateID("$proxy$each") + return proxy +} + +function eachProxyAgent(index, data) { + var param = data.param || "el", + proxy + for (var i = 0, n = eachProxyPool.length; i < n; i++) { + var candidate = eachProxyPool[i] + if (candidate && candidate.hasOwnProperty(param)) { + proxy = candidate + eachProxyPool.splice(i, 1) + } + } + if (!proxy) { + proxy = eachProxyFactory(param) + } + var host = data.$repeat + var last = host.length - 1 + proxy.$index = index + proxy.$first = index === 0 + proxy.$last = index === last + proxy.$host = host + proxy.$outer = data.$outer + proxy.$remove = function () { + return host.removeAt(proxy.$index) + } + return proxy +} + + +function proxyRecycler(proxy, proxyPool) { + for (var i in proxy.$events) { + if (Array.isArray(proxy.$events[i])) { + proxy.$events[i].forEach(function (data) { + if (typeof data === "object") + disposeData(data) + })// jshint ignore:line + proxy.$events[i].length = 0 + } + } + proxy.$host = proxy.$outer = {} + if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) { + proxyPool.pop() + } +} +/********************************************************************* + * 各种指令 * + **********************************************************************/ +//ms-skip绑定已经在scanTag 方法中实现 +// bindingHandlers.text 定义在if.js +bindingExecutors.text = function(val, elem) { + val = val == null ? "" : val //不在页面上显示undefined null + if (elem.nodeType === 3) { //绑定在文本节点上 + try { //IE对游离于DOM树外的节点赋值会报错 + elem.data = val + } catch (e) {} + } else { //绑定在特性节点上 + if ("textContent" in elem) { + elem.textContent = val + } else { + elem.innerText = val + } + } +} +function parseDisplay(nodeName, val) { + //用于取得此类标签的默认display值 + var key = "_" + nodeName + if (!parseDisplay[key]) { + var node = DOC.createElement(nodeName) + root.appendChild(node) + if (W3C) { + val = getComputedStyle(node, null).display + } else { + val = node.currentStyle.display + } + root.removeChild(node) + parseDisplay[key] = val + } + return parseDisplay[key] +} + +avalon.parseDisplay = parseDisplay + +bindingHandlers.visible = function(data, vmodels) { + var elem = data.element + var display = elem.style.display + if(display === "none"){ + display = parseDisplay(elem.nodeName) + } + data.display = display + parseExprProxy(data.value, vmodels, data) +} + +bindingExecutors.visible = function(val, elem, data) { + elem.style.display = val ? data.display : "none" +} +bindingHandlers.widget = function(data, vmodels) { + var args = data.value.match(rword) + var elem = data.element + var widget = args[0] + var id = args[1] + if (!id || id === "$") { //没有定义或为$时,取组件名+随机数 + id = generateID(widget) + } + var optName = args[2] || widget //没有定义,取组件名 + var constructor = avalon.ui[widget] + if (typeof constructor === "function") { //ms-widget="tabs,tabsAAA,optname" + vmodels = elem.vmodels || vmodels + for (var i = 0, v; v = vmodels[i++];) { + if (v.hasOwnProperty(optName) && typeof v[optName] === "object") { + var vmOptions = v[optName] + vmOptions = vmOptions.$model || vmOptions + break + } + } + if (vmOptions) { + var wid = vmOptions[widget + "Id"] + if (typeof wid === "string") { + log("warning!不再支持" + widget + "Id") + id = wid + } + } + //抽取data-tooltip-text、data-tooltip-attr属性,组成一个配置对象 + var widgetData = avalon.getWidgetData(elem, widget) + data.value = [widget, id, optName].join(",") + data[widget + "Id"] = id + data.evaluator = noop + elem.msData["ms-widget-id"] = id + var options = data[widget + "Options"] = avalon.mix({}, constructor.defaults, vmOptions || {}, widgetData) + elem.removeAttribute("ms-widget") + var vmodel = constructor(elem, data, vmodels) || {} //防止组件不返回VM + if (vmodel.$id) { + avalon.vmodels[id] = vmodel + createSignalTower(elem, vmodel) + try { + vmodel.$init(function() { + avalon.scan(elem, [vmodel].concat(vmodels)) + if (typeof options.onInit === "function") { + options.onInit.call(elem, vmodel, options, vmodels) + } + }) + } catch (e) {} + data.rollback = function() { + try { + vmodel.widgetElement = null + vmodel.$remove() + } catch (e) {} + elem.msData = {} + delete avalon.vmodels[vmodel.$id] + } + injectDisposeQueue(data, widgetList) + if (window.chrome) { + elem.addEventListener("DOMNodeRemovedFromDocument", function() { + setTimeout(rejectDisposeQueue) + }) + } + } else { + avalon.scan(elem, vmodels) + } + } else if (vmodels.length) { //如果该组件还没有加载,那么保存当前的vmodels + elem.vmodels = vmodels + } +} +var widgetList = [] +//不存在 bindingExecutors.widget +/********************************************************************* + * 自带过滤器 * + **********************************************************************/ +var rscripts = /]*>([\S\s]*?)<\/script\s*>/gim +var ron = /\s+(on[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g +var ropen = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/ig +var rsanitize = { + a: /\b(href)\=("javascript[^"]*"|'javascript[^']*')/ig, + img: /\b(src)\=("javascript[^"]*"|'javascript[^']*')/ig, + form: /\b(action)\=("javascript[^"]*"|'javascript[^']*')/ig +} +var rsurrogate = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g +var rnoalphanumeric = /([^\#-~| |!])/g; + +function numberFormat(number, decimals, point, thousands) { + //form http://phpjs.org/functions/number_format/ + //number 必需,要格式化的数字 + //decimals 可选,规定多少个小数位。 + //point 可选,规定用作小数点的字符串(默认为 . )。 + //thousands 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。 + number = (number + '') + .replace(/[^0-9+\-Ee.]/g, '') + var n = !isFinite(+number) ? 0 : +number, + prec = !isFinite(+decimals) ? 3 : Math.abs(decimals), + sep = thousands || ",", + dec = point || ".", + s = '', + toFixedFix = function(n, prec) { + var k = Math.pow(10, prec) + return '' + (Math.round(n * k) / k) + .toFixed(prec) + } + // Fix for IE parseFloat(0.55).toFixed(0) = 0; + s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)) + .split('.') + if (s[0].length > 3) { + s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep) + } + if ((s[1] || '') + .length < prec) { + s[1] = s[1] || '' + s[1] += new Array(prec - s[1].length + 1) + .join('0') + } + return s.join(dec) +} + + +var filters = avalon.filters = { + uppercase: function(str) { + return str.toUpperCase() + }, + lowercase: function(str) { + return str.toLowerCase() + }, + truncate: function(str, length, truncation) { + //length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串 + length = length || 30 + truncation = typeof truncation === "string" ? truncation : "..." + return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str) + }, + $filter: function(val) { + for (var i = 1, n = arguments.length; i < n; i++) { + var array = arguments[i] + var fn = avalon.filters[array.shift()] + if (typeof fn === "function") { + var arr = [val].concat(array) + val = fn.apply(null, arr) + } + } + return val + }, + camelize: camelize, + //https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet + // chrome + // chrome + // IE67chrome + // IE67chrome + // IE67chrome + sanitize: function(str) { + return str.replace(rscripts, "").replace(ropen, function(a, b) { + var match = a.toLowerCase().match(/<(\w+)\s/) + if (match) { //处理a标签的href属性,img标签的src属性,form标签的action属性 + var reg = rsanitize[match[1]] + if (reg) { + a = a.replace(reg, function(s, name, value) { + var quote = value.charAt(0) + return name + "=" + quote + "javascript:void(0)" + quote// jshint ignore:line + }) + } + } + return a.replace(ron, " ").replace(/\s+/g, " ") //移除onXXX事件 + }) + }, + escape: function(str) { + //将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 < + return String(str). + replace(/&/g, '&'). + replace(rsurrogate, function(value) { + var hi = value.charCodeAt(0) + var low = value.charCodeAt(1) + return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';' + }). + replace(rnoalphanumeric, function(value) { + return '&#' + value.charCodeAt(0) + ';' + }). + replace(//g, '>') + }, + currency: function(amount, symbol, fractionSize) { + return (symbol || "\uFFE5") + numberFormat(amount, isFinite(fractionSize) ? fractionSize : 2) + }, + number: numberFormat +} +/* + 'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) + 'yy': 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + 'y': 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) + 'MMMM': Month in year (January-December) + 'MMM': Month in year (Jan-Dec) + 'MM': Month in year, padded (01-12) + 'M': Month in year (1-12) + 'dd': Day in month, padded (01-31) + 'd': Day in month (1-31) + 'EEEE': Day in Week,(Sunday-Saturday) + 'EEE': Day in Week, (Sun-Sat) + 'HH': Hour in day, padded (00-23) + 'H': Hour in day (0-23) + 'hh': Hour in am/pm, padded (01-12) + 'h': Hour in am/pm, (1-12) + 'mm': Minute in hour, padded (00-59) + 'm': Minute in hour (0-59) + 'ss': Second in minute, padded (00-59) + 's': Second in minute (0-59) + 'a': am/pm marker + 'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200) + format string can also be one of the following predefined localizable formats: + + 'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm) + 'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm) + 'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010) + 'longDate': equivalent to 'MMMM d, y' for en_US locale (e.g. September 3, 2010 + 'mediumDate': equivalent to 'MMM d, y' for en_US locale (e.g. Sep 3, 2010) + 'shortDate': equivalent to 'M/d/yy' for en_US locale (e.g. 9/3/10) + 'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm) + 'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm) + */ +new function() {// jshint ignore:line + function toInt(str) { + return parseInt(str, 10) || 0 + } + + function padNumber(num, digits, trim) { + var neg = "" + if (num < 0) { + neg = '-' + num = -num + } + num = "" + num + while (num.length < digits) + num = "0" + num + if (trim) + num = num.substr(num.length - digits) + return neg + num + } + + function dateGetter(name, size, offset, trim) { + return function(date) { + var value = date["get" + name]() + if (offset > 0 || value > -offset) + value += offset + if (value === 0 && offset === -12) { + value = 12 + } + return padNumber(value, size, trim) + } + } + + function dateStrGetter(name, shortForm) { + return function(date, formats) { + var value = date["get" + name]() + var get = (shortForm ? ("SHORT" + name) : name).toUpperCase() + return formats[get][value] + } + } + + function timeZoneGetter(date) { + var zone = -1 * date.getTimezoneOffset() + var paddedZone = (zone >= 0) ? "+" : "" + paddedZone += padNumber(Math[zone > 0 ? "floor" : "ceil"](zone / 60), 2) + padNumber(Math.abs(zone % 60), 2) + return paddedZone + } + //取得上午下午 + + function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1] + } + var DATE_FORMATS = { + yyyy: dateGetter("FullYear", 4), + yy: dateGetter("FullYear", 2, 0, true), + y: dateGetter("FullYear", 1), + MMMM: dateStrGetter("Month"), + MMM: dateStrGetter("Month", true), + MM: dateGetter("Month", 2, 1), + M: dateGetter("Month", 1, 1), + dd: dateGetter("Date", 2), + d: dateGetter("Date", 1), + HH: dateGetter("Hours", 2), + H: dateGetter("Hours", 1), + hh: dateGetter("Hours", 2, -12), + h: dateGetter("Hours", 1, -12), + mm: dateGetter("Minutes", 2), + m: dateGetter("Minutes", 1), + ss: dateGetter("Seconds", 2), + s: dateGetter("Seconds", 1), + sss: dateGetter("Milliseconds", 3), + EEEE: dateStrGetter("Day"), + EEE: dateStrGetter("Day", true), + a: ampmGetter, + Z: timeZoneGetter + } + var rdateFormat = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/ + var raspnetjson = /^\/Date\((\d+)\)\/$/ + filters.date = function(date, format) { + var locate = filters.date.locate, + text = "", + parts = [], + fn, match + format = format || "mediumDate" + format = locate[format] || format + if (typeof date === "string") { + if (/^\d+$/.test(date)) { + date = toInt(date) + } else if (raspnetjson.test(date)) { + date = +RegExp.$1 + } else { + var trimDate = date.trim() + var dateArray = [0, 0, 0, 0, 0, 0, 0] + var oDate = new Date(0) + //取得年月日 + trimDate = trimDate.replace(/^(\d+)\D(\d+)\D(\d+)/, function(_, a, b, c) { + var array = c.length === 4 ? [c, a, b] : [a, b, c] + dateArray[0] = toInt(array[0]) //å¹´ + dateArray[1] = toInt(array[1]) - 1 //月 + dateArray[2] = toInt(array[2]) //日 + return "" + }) + var dateSetter = oDate.setFullYear + var timeSetter = oDate.setHours + trimDate = trimDate.replace(/[T\s](\d+):(\d+):?(\d+)?\.?(\d)?/, function(_, a, b, c, d) { + dateArray[3] = toInt(a) //小时 + dateArray[4] = toInt(b) //分钟 + dateArray[5] = toInt(c) //秒 + if (d) { //毫秒 + dateArray[6] = Math.round(parseFloat("0." + d) * 1000) + } + return "" + }) + var tzHour = 0 + var tzMin = 0 + trimDate = trimDate.replace(/Z|([+-])(\d\d):?(\d\d)/, function(z, symbol, c, d) { + dateSetter = oDate.setUTCFullYear + timeSetter = oDate.setUTCHours + if (symbol) { + tzHour = toInt(symbol + c) + tzMin = toInt(symbol + d) + } + return "" + }) + + dateArray[3] -= tzHour + dateArray[4] -= tzMin + dateSetter.apply(oDate, dateArray.slice(0, 3)) + timeSetter.apply(oDate, dateArray.slice(3)) + date = oDate + } + } + if (typeof date === "number") { + date = new Date(date) + } + if (avalon.type(date) !== "date") { + return + } + while (format) { + match = rdateFormat.exec(format) + if (match) { + parts = parts.concat(match.slice(1)) + format = parts.pop() + } else { + parts.push(format) + format = null + } + } + parts.forEach(function(value) { + fn = DATE_FORMATS[value] + text += fn ? fn(date, locate) : value.replace(/(^'|'$)/g, "").replace(/''/g, "'") + }) + return text + } + var locate = { + AMPMS: { + 0: "上午", + 1: "下午" + }, + DAY: { + 0: "星期日", + 1: "星期一", + 2: "星期二", + 3: "星期三", + 4: "星期四", + 5: "星期五", + 6: "星期六" + }, + MONTH: { + 0: "1月", + 1: "2月", + 2: "3月", + 3: "4月", + 4: "5月", + 5: "6月", + 6: "7月", + 7: "8月", + 8: "9月", + 9: "10月", + 10: "11月", + 11: "12月" + }, + SHORTDAY: { + "0": "周日", + "1": "周一", + "2": "周二", + "3": "周三", + "4": "周四", + "5": "周五", + "6": "周六" + }, + fullDate: "yå¹´M月d日EEEE", + longDate: "yå¹´M月d日", + medium: "yyyy-M-d H:mm:ss", + mediumDate: "yyyy-M-d", + mediumTime: "H:mm:ss", + "short": "yy-M-d ah:mm", + shortDate: "yy-M-d", + shortTime: "ah:mm" + } + locate.SHORTMONTH = locate.MONTH + filters.date.locate = locate +}// jshint ignore:line +/********************************************************************* + * AMD加载器 * + **********************************************************************/ +//https://www.devbridge.com/articles/understanding-amd-requirejs/ +//http://maxogden.com/nested-dependencies.html +var modules = avalon.modules = { + "domReady!": { + exports: avalon, + state: 3 + }, + "avalon": { + exports: avalon, + state: 4 + } +} +//Object(modules[id]).state拥有如下值 +// undefined 没有定义 +// 1(send) 已经发出请求 +// 2(loading) 已经被执行但还没有执行完成,在这个阶段define方法会被执行 +// 3(loaded) 执行完毕,通过onload/onreadystatechange回调判定,在这个阶段checkDeps方法会执行 +// 4(execute) 其依赖也执行完毕, 值放到exports对象上,在这个阶段fireFactory方法会执行 +modules.exports = modules.avalon + +new function () {// jshint ignore:line + var loadings = [] //正在加载中的模块列表 + var factorys = [] //放置define方法的factory函数 + var rjsext = /\.js$/i + function makeRequest(name, config) { +//1. 去掉资源前缀 + var res = "js" + name = name.replace(/^(\w+)\!/, function (a, b) { + res = b + return "" + }) + if (res === "ready") { + log("debug: ready!已经被废弃,请使用domReady!") + res = "domReady" + } +//2. 去掉querystring, hash + var query = "" + name = name.replace(rquery, function (a) { + query = a + return "" + }) + //3. 去掉扩展名 + var suffix = "." + res + var ext = /js|css/.test(suffix) ? suffix : "" + name = name.replace(/\.[a-z0-9]+$/g, function (a) { + if (a === suffix) { + ext = a + return "" + } else { + return a + } + }) + var req = avalon.mix({ + query: query, + ext: ext, + res: res, + name: name, + toUrl: toUrl + }, config) + req.toUrl(name) + return req + } + + function fireRequest(req) { + var name = req.name + var res = req.res + //1. 如果该模块已经发出请求,直接返回 + var module = modules[name] + var urlNoQuery = name && req.urlNoQuery + if (module && module.state >= 1) { + return name + } + module = modules[urlNoQuery] + if (module && module.state >= 3) { + innerRequire(module.deps || [], module.factory, urlNoQuery) + return urlNoQuery + } + if (name && !module) { + module = modules[urlNoQuery] = { + id: urlNoQuery, + state: 1 //send + } + var wrap = function (obj) { + resources[res] = obj + obj.load(name, req, function (a) { + if (arguments.length && a !== void 0) { + module.exports = a + } + module.state = 4 + checkDeps() + }) + } + + if (!resources[res]) { + innerRequire([res], wrap) + } else { + wrap(resources[res]) + } + } + return name ? urlNoQuery : res + "!" + } + +//核心API之一 require + var requireQueue = [] + var isUserFirstRequire = false + innerRequire = avalon.require = function (array, factory, parentUrl, defineConfig) { + if (!isUserFirstRequire) { + requireQueue.push(avalon.slice(arguments)) + if (arguments.length <= 2) { + isUserFirstRequire = true + var queue = requireQueue.splice(0, requireQueue.length), args + while (args = queue.shift()) { + innerRequire.apply(null, args) + } + } + return + } + + if (!Array.isArray(array)) { + avalon.error("require方法的第一个参数应为数组 " + array) + } + var deps = [] // 放置所有依赖项的完整路径 + var uniq = {} + var id = parentUrl || "callback" + setTimeout("1")// jshint ignore:line + defineConfig = defineConfig || {} + defineConfig.baseUrl = kernel.baseUrl + var isBuilt = !!defineConfig.built + if (parentUrl) { + defineConfig.parentUrl = parentUrl.substr(0, parentUrl.lastIndexOf("/")) + defineConfig.mapUrl = parentUrl.replace(rjsext, "") + } + if (isBuilt) { + var req = makeRequest(defineConfig.defineName, defineConfig) + id = req.urlNoQuery + } else { + array.forEach(function (name) { + var req = makeRequest(name, defineConfig) + var url = fireRequest(req) //加载资源,并返回该资源的完整地址 + if (url) { + if (!uniq[url]) { + deps.push(url) + uniq[url] = "司徒正美" //去重 + } + } + }) + } + + var module = modules[id] + if (!module || module.state !== 4) { + modules[id] = { + id: id, + deps: isBuilt ? array.concat() : deps, + factory: factory || noop, + state: 3 + } + } + if (!module) { + //如果此模块是定义在另一个JS文件中, 那必须等该文件加载完毕, 才能放到检测列队中 + loadings.push(id) + } + checkDeps() + } + +//核心API之二 require + innerRequire.define = function (name, deps, factory) { //模块名,依赖列表,模块本身 + if (typeof name !== "string") { + factory = deps + deps = name + name = "anonymous" + } + if (!Array.isArray(deps)) { + factory = deps + deps = [] + } + var config = { + built: !isUserFirstRequire, //用r.js打包后,所有define会放到requirejs之前 + defineName: name + } + var args = [deps, factory, config] + factory.require = function (url) { + args.splice(2, 0, url) + if (modules[url]) { + modules[url].state = 3 //loaded + var isCycle = false + try { + isCycle = checkCycle(modules[url].deps, url) + } catch (e) { + } + if (isCycle) { + avalon.error(url + "模块与之前的模块存在循环依赖,请不要直接用script标签引入" + url + "模块") + } + } + delete factory.require //释放内存 + innerRequire.apply(null, args) //0,1,2 --> 1,2,0 + } +//根据标准,所有遵循W3C标准的浏览器,script标签会按标签的出现顺序执行。 +//老的浏览器中,加载也是按顺序的:一个文件下载完成后,才开始下载下一个文件。 +//较新的浏览器中(IE8+ 、FireFox3.5+ 、Chrome4+ 、Safari4+),为了减小请求时间以优化体验, +//下载可以是并行的,但是执行顺序还是按照标签出现的顺序。 +//但如果script标签是动态插入的, 就未必按照先请求先执行的原则了,目测只有firefox遵守 +//唯一比较一致的是,IE10+及其他标准浏览器,一旦开始解析脚本, 就会一直堵在那里,直接脚本解析完毕 +//亦即,先进入loading阶段的script标签(模块)必然会先进入loaded阶段 + var url = config.built ? "unknown" : getCurrentScript() + if (url) { + var module = modules[url] + if (module) { + module.state = 2 + } + factory.require(url) + } else {//合并前后的safari,合并后的IE6-9走此分支 + factorys.push(factory) + } + } +//核心API之三 require.config(settings) + innerRequire.config = kernel + //核心API之四 define.amd 标识其符合AMD规范 + innerRequire.define.amd = modules + + //==========================对用户配置项进行再加工========================== + var allpaths = kernel["orig.paths"] = {} + var allmaps = kernel["orig.map"] = {} + var allpackages = kernel["packages"] = [] + var allargs = kernel["orig.args"] = {} + avalon.mix(plugins, { + paths: function (hash) { + avalon.mix(allpaths, hash) + kernel.paths = makeIndexArray(allpaths) + }, + map: function (hash) { + avalon.mix(allmaps, hash) + var list = makeIndexArray(allmaps, 1, 1) + avalon.each(list, function (_, item) { + item.val = makeIndexArray(item.val) + }) + kernel.map = list + }, + packages: function (array) { + array = array.concat(allpackages) + var uniq = {} + var ret = [] + for (var i = 0, pkg; pkg = array[i++]; ) { + pkg = typeof pkg === "string" ? {name: pkg} : pkg + var name = pkg.name + if (!uniq[name]) { + var url = joinPath(pkg.location || name, pkg.main || "main") + url = url.replace(rjsext, "") + ret.push(pkg) + uniq[name] = pkg.location = url + pkg.reg = makeMatcher(name) + } + } + kernel.packages = ret.sort() + }, + urlArgs: function (hash) { + if (typeof hash === "string") { + hash = {"*": hash} + } + avalon.mix(allargs, hash) + kernel.urlArgs = makeIndexArray(allargs, 1) + }, + baseUrl: function (url) { + if (!isAbsUrl(url)) { + var baseElement = head.getElementsByTagName("base")[0] + if (baseElement) { + head.removeChild(baseElement) + } + var node = DOC.createElement("a") + node.href = url + url = getFullUrl(node, "href") + if (baseElement) { + head.insertBefore(baseElement, head.firstChild) + } + } + if (url.length > 3) + kernel.baseUrl = url + }, + shim: function (obj) { + for (var i in obj) { + var value = obj[i] + if (Array.isArray(value)) { + value = obj[i] = { + deps: value + } + } + if (!value.exportsFn && (value.exports || value.init)) { + value.exportsFn = makeExports(value) + } + } + kernel.shim = obj + } + + }) + + + //==============================内部方法================================= + function checkCycle(deps, nick) { + //检测是否存在循环依赖 + for (var i = 0, id; id = deps[i++]; ) { + if (modules[id].state !== 4 && + (id === nick || checkCycle(modules[id].deps, nick))) { + return true + } + } + } + + function checkFail(node, onError, fuckIE) { + var id = trimQuery(node.src) //检测是否死链 + node.onload = node.onreadystatechange = node.onerror = null + if (onError || (fuckIE && modules[id] && !modules[id].state)) { + setTimeout(function () { + head.removeChild(node) + node = null // 处理旧式IE下的循环引用问题 + }) + log("debug: 加载 " + id + " 失败" + onError + " " + (!modules[id].state)) + } else { + return true + } + } + + function checkDeps() { + //检测此JS模块的依赖是否都已安装完毕,是则安装自身 + loop: for (var i = loadings.length, id; id = loadings[--i]; ) { + var obj = modules[id], + deps = obj.deps + if (!deps) + continue + for (var j = 0, key; key = deps[j]; j++) { + if (Object(modules[key]).state !== 4) { + continue loop + } + } + //如果deps是空对象或者其依赖的模块的状态都是2 + if (obj.state !== 4) { + loadings.splice(i, 1) //必须先移除再安装,防止在IE下DOM树建完后手动刷新页面,会多次执行它 + fireFactory(obj.id, obj.deps, obj.factory) + checkDeps() //如果成功,则再执行一次,以防有些模块就差本模块没有安装好 + } + } + } + + var rreadyState = /complete|loaded/ + function loadJS(url, id, callback) { + //通过script节点加载目标模块 + var node = DOC.createElement("script") + node.className = subscribers //让getCurrentScript只处理类名为subscribers的script节点 + var supportLoad = "onload" in node + var onEvent = supportLoad ? "onload" : "onreadystatechange" + function onload() { + var factory = factorys.pop() + factory && factory.require(id) + if (callback) { + callback() + } + if (checkFail(node, false, !supportLoad)) { + log("debug: 已成功加载 " + url) + id && loadings.push(id) + checkDeps() + } + } + var index = 0, loadID + node[onEvent] = supportLoad ? onload : function () { + if (rreadyState.test(node.readyState)) { + ++index + if (index === 1) { + loadID = setTimeout(onload, 500) + } else { + clearTimeout(loadID) + onload() + } + } + } + node.onerror = function () { + checkFail(node, true) + } + + head.insertBefore(node, head.firstChild) //chrome下第二个参数不能为null + node.src = url //插入到head的第一个节点前,防止IE6下head标签没闭合前使用appendChild抛错 + log("debug: 正准备加载 " + url) //更重要的是IE6下可以收窄getCurrentScript的寻找范围 + } + + var resources = innerRequire.plugins = { + //三大常用资源插件 js!, css!, text!, ready! + ready: { + load: noop + }, + js: { + load: function (name, req, onLoad) { + var url = req.url + var id = req.urlNoQuery + var shim = kernel.shim[name.replace(rjsext, "")] + if (shim) { //shim机制 + innerRequire(shim.deps || [], function () { + var args = avalon.slice(arguments) + loadJS(url, id, function () { + onLoad(shim.exportsFn ? shim.exportsFn.apply(0, args) : void 0) + }) + }) + } else { + loadJS(url, id) + } + } + }, + css: { + load: function (name, req, onLoad) { + var url = req.url + var node = DOC.createElement("link") + node.rel = "stylesheet" + node.href = url + head.insertBefore(node, head.firstChild) + log("debug: 已成功加载 " + url) + onLoad() + } + }, + text: { + load: function (name, req, onLoad) { + var url = req.url + var xhr = getXHR() + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + var status = xhr.status; + if (status > 399 && status < 600) { + avalon.error(url + " 对应资源不存在或没有开启 CORS") + } else { + log("debug: 已成功加载 " + url) + onLoad(xhr.responseText) + } + } + } + var time = "_=" + (new Date() - 0) + var _url = url.indexOf("?") === -1 ? url + "?" + time : url + "&" + time + xhr.open("GET", _url, true) + if ("withCredentials" in xhr) {//这是处理跨域 + xhr.withCredentials = true + } + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")//告诉后端这是AJAX请求 + xhr.send() + log("debug: 正准备加载 " + url) + } + } + } + innerRequire.checkDeps = checkDeps + + var rquery = /(\?[^#]*)$/ + function trimQuery(url) { + return (url || "").replace(rquery, "") + } + + function isAbsUrl(path) { + //http://stackoverflow.com/questions/10687099/how-to-test-if-a-url-string-is-absolute-or-relative + return /^(?:[a-z]+:)?\/\//i.test(String(path)) + } + + function getFullUrl(node, src) { + return"1"[0] ? node[src] : node.getAttribute(src, 4) + } + + function getCurrentScript() { + // inspireb by https://github.com/samyk/jiagra/blob/master/jiagra.js + var stack + try { + a.b.c() //强制报错,以便捕获e.stack + } catch (e) { //safari5的sourceURL,firefox的fileName,它们的效果与e.stack不一样 + stack = e.stack + if (!stack && window.opera) { + //opera 9没有e.stack,但有e.Backtrace,但不能直接取得,需要对e对象转字符串进行抽取 + stack = (String(e).match(/of linked script \S+/g) || []).join(" ") + } + } + if (stack) { + /**e.stack最后一行在所有支持的浏览器大致如下: + *chrome23: + * at http://113.93.50.63/data.js:4:1 + *firefox17: + *@http://113.93.50.63/query.js:4 + *opera12:http://www.oldapps.com/opera.php?system=Windows_XP + *@http://113.93.50.63/data.js:4 + *IE10: + * at Global code (http://113.93.50.63/data.js:4:1) + * //firefox4+ 可以用document.currentScript + */ + stack = stack.split(/[@ ]/g).pop() //取得最后一行,最后一个空格或@之后的部分 + stack = stack[0] === "(" ? stack.slice(1, -1) : stack.replace(/\s/, "") //去掉换行符 + return trimQuery(stack.replace(/(:\d+)?:\d+$/i, "")) //去掉行号与或许存在的出错字符起始位置 + } + var nodes = head.getElementsByTagName("script") //只在head标签中寻找 + for (var i = nodes.length, node; node = nodes[--i]; ) { + if (node.className === subscribers && node.readyState === "interactive") { + var url = getFullUrl(node, "src") + return node.className = trimQuery(url) + } + } + } + + var rcallback = /^callback\d+$/ + function fireFactory(id, deps, factory) { + var module = Object(modules[id]) + module.state = 4 + for (var i = 0, array = [], d; d = deps[i++]; ) { + if (d === "exports") { + var obj = module.exports || (module.exports = {}) + array.push(obj) + } else { + array.push(modules[d].exports) + } + } + try { + var ret = factory.apply(window, array) + } catch (e) { + log("执行[" + id + "]模块的factory抛错: ", e) + } + if (ret !== void 0) { + module.exports = ret + } + if (rcallback.test(id)) { + delete modules[id] + } + delete module.factory + return ret + } + function toUrl(id) { + if (id.indexOf(this.res + "!") === 0) { + id = id.slice(this.res.length + 1) //处理define("css!style",[], function(){})的情况 + } + var url = id + //1. 是否命中paths配置项 + var usePath = 0 + var baseUrl = this.baseUrl + var rootUrl = this.parentUrl || baseUrl + eachIndexArray(id, kernel.paths, function (value, key) { + url = url.replace(key, value) + usePath = 1 + }) + //2. 是否命中packages配置项 + if (!usePath) { + eachIndexArray(id, kernel.packages, function (value, key, item) { + url = url.replace(item.name, item.location) + }) + } + //3. 是否命中map配置项 + if (this.mapUrl) { + eachIndexArray(this.mapUrl, kernel.map, function (array) { + eachIndexArray(url, array, function (mdValue, mdKey) { + url = url.replace(mdKey, mdValue) + rootUrl = baseUrl + }) + }) + } + var ext = this.ext + if (ext && usePath && url.slice(-ext.length) === ext) { + url = url.slice(0, -ext.length) + } + //4. 转换为绝对路径 + if (!isAbsUrl(url)) { + rootUrl = this.built || /^\w/.test(url) ? baseUrl : rootUrl + url = joinPath(rootUrl, url) + } + //5. 还原扩展名,query + var urlNoQuery = url + ext + url = urlNoQuery + this.query + //6. 处理urlArgs + eachIndexArray(id, kernel.urlArgs, function (value) { + url += (url.indexOf("?") === -1 ? "?" : "&") + value; + }) + this.url = url + return this.urlNoQuery = urlNoQuery + } + + function makeIndexArray(hash, useStar, part) { + //创建一个经过特殊算法排好序的数组 + var index = hash2array(hash, useStar, part) + index.sort(descSorterByName) + return index + } + + function makeMatcher(prefix) { + return new RegExp('^' + prefix + '(/|$)') + } + + function makeExports(value) { + return function () { + var ret + if (value.init) { + ret = value.init.apply(window, arguments) + } + return ret || (value.exports && getGlobal(value.exports)) + } + } + + + function hash2array(hash, useStar, part) { + var array = []; + for (var key in hash) { + if (ohasOwn.call(hash, key)) { + var item = { + name: key, + val: hash[key] + } + array.push(item) + item.reg = key === "*" && useStar ? /^/ : makeMatcher(key) + if (part && key !== "*") { + item.reg = new RegExp('\/' + key.replace(/^\//, "") + '(/|$)') + } + } + } + return array + } + + function eachIndexArray(moduleID, array, matcher) { + array = array || [] + for (var i = 0, el; el = array[i++]; ) { + if (el.reg.test(moduleID)) { + matcher(el.val, el.name, el) + return false + } + } + } + // 根据元素的name项进行数组字符数逆序的排序函数 + function descSorterByName(a, b) { + var aaa = a.name + var bbb = b.name + if (bbb === "*") { + return -1 + } + if (aaa === "*") { + return 1 + } + return bbb.length - aaa.length + } + + var rdeuce = /\/\w+\/\.\./ + function joinPath(a, b) { + if (a.charAt(a.length - 1) !== "/") { + a += "/" + } + if (b.slice(0, 2) === "./") { //相对于兄弟路径 + return a + b.slice(2) + } + if (b.slice(0, 2) === "..") { //相对于父路径 + a += b + while (rdeuce.test(a)) { + a = a.replace(rdeuce, "") + } + return a + } + if (b.slice(0, 1) === "/") { + return a + b.slice(1) + } + return a + b + } + + function getGlobal(value) { + if (!value) { + return value + } + var g = window + value.split(".").forEach(function (part) { + g = g[part] + }) + return g + } + + var mainNode = DOC.scripts[DOC.scripts.length - 1] + var dataMain = mainNode.getAttribute("data-main") + if (dataMain) { + plugins.baseUrl(dataMain) + var href = kernel.baseUrl + kernel.baseUrl = href.slice(0, href.lastIndexOf("/") + 1) + loadJS(href.replace(rjsext, "") + ".js") + } else { + var loaderUrl = trimQuery(getFullUrl(mainNode, "src")) + kernel.baseUrl = loaderUrl.slice(0, loaderUrl.lastIndexOf("/") + 1) + } +}// jshint ignore:line + +/********************************************************************* + * DOMReady * + **********************************************************************/ + +var readyList = [], isReady +var fireReady = function(fn) { + isReady = true + if (innerRequire) { + modules["domReady!"].state = 4 + innerRequire.checkDeps() + } + while(fn = readyList.shift()){ + fn(avalon) + } +} + +function doScrollCheck() { + try { //IE下通过doScrollCheck检测DOM树是否建完 + root.doScroll("left") + fireReady() + } catch (e) { + setTimeout(doScrollCheck) + } +} + +if (DOC.readyState === "complete") { + setTimeout(fireReady) //如果在domReady之外加载 +} else if (W3C) { + DOC.addEventListener("DOMContentLoaded", fireReady) +} else { + DOC.attachEvent("onreadystatechange", function() { + if (DOC.readyState === "complete") { + fireReady() + } + }) + try { + var isTop = window.frameElement === null + } catch (e) { + } + if (root.doScroll && isTop && window.external) {//fix IE iframe BUG + doScrollCheck() + } +} +avalon.bind(window, "load", fireReady) + +avalon.ready = function(fn) { + if (!isReady) { + readyList.push(fn) + } else { + fn(avalon) + } +} + +avalon.config({ + loader: true +}) + +avalon.ready(function() { + avalon.scan(DOC.body) +}) + +// Register as a named AMD module, since avalon can be concatenated with other +// files that may use define, but not via a proper concatenation script that +// understands anonymous AMD modules. A named AMD is safest and most robust +// way to register. Lowercase avalon is used because AMD module names are +// derived from file names, and Avalon is normally delivered in a lowercase +// file name. Do this after creating the global so that if an AMD module wants +// to call noConflict to hide this version of avalon, it will work. + +// Note that for maximum portability, libraries that are not avalon should +// declare themselves as anonymous modules, and avoid setting a global if an +// AMD loader is present. avalon is a special case. For more information, see +// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon + if (typeof define === "function" && define.amd) { + define("avalon", [], function() { + return avalon + }) + } +// Map over avalon in case of overwrite + var _avalon = window.avalon + avalon.noConflict = function(deep) { + if (deep && window.avalon === avalon) { + window.avalon = _avalon + } + return avalon + } +// Expose avalon identifiers, even in AMD +// and CommonJS for browser emulators + if (noGlobal === void 0) { + window.avalon = avalon + } + return avalon + +})); \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/js/bootbox/bootbox.min.js b/apiroute/apiroute-service/src/main/resources/iui-route/js/bootbox/bootbox.min.js new file mode 100644 index 0000000..a7ea24f --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/iui-route/js/bootbox/bootbox.min.js @@ -0,0 +1,6 @@ +/** + * bootbox.js v4.3.0 + * + * http://bootboxjs.com/license.txt + */ +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.bootbox=b(a.jQuery)}(this,function a(b,c){"use strict";function d(a){var b=q[o.locale];return b?b[a]:q.en[a]}function e(a,c,d){a.stopPropagation(),a.preventDefault();var e=b.isFunction(d)&&d(a)===!1;e||c.modal("hide")}function f(a){var b,c=0;for(b in a)c++;return c}function g(a,c){var d=0;b.each(a,function(a,b){c(a,b,d++)})}function h(a){var c,d;if("object"!=typeof a)throw new Error("Please supply an object of options");if(!a.message)throw new Error("Please specify a message");return a=b.extend({},o,a),a.buttons||(a.buttons={}),a.backdrop=a.backdrop?"static":!1,c=a.buttons,d=f(c),g(c,function(a,e,f){if(b.isFunction(e)&&(e=c[a]={callback:e}),"object"!==b.type(e))throw new Error("button with key "+a+" must be an object");e.label||(e.label=a),e.className||(e.className=2>=d&&f===d-1?"btn-primary":"btn-default")}),a}function i(a,b){var c=a.length,d={};if(1>c||c>2)throw new Error("Invalid argument length");return 2===c||"string"==typeof a[0]?(d[b[0]]=a[0],d[b[1]]=a[1]):d=a[0],d}function j(a,c,d){return b.extend(!0,{},a,i(c,d))}function k(a,b,c,d){var e={className:"bootbox-"+a,buttons:l.apply(null,b)};return m(j(e,d,c),b)}function l(){for(var a={},b=0,c=arguments.length;c>b;b++){var e=arguments[b],f=e.toLowerCase(),g=e.toUpperCase();a[f]={label:d(g)}}return a}function m(a,b){var d={};return g(b,function(a,b){d[b]=!0}),g(a.buttons,function(a){if(d[a]===c)throw new Error("button key "+a+" is not allowed (options are "+b.join("\n")+")")}),a}var n={dialog:"",header:"",footer:"",closeButton:"",form:"
",inputs:{text:"",textarea:"",email:"",select:"",checkbox:"
",date:"",time:"",number:"",password:""}},o={locale:"en",backdrop:!0,animate:!0,className:null,closeButton:!0,show:!0,container:"body"},p={};p.alert=function(){var a;if(a=k("alert",["ok"],["message","callback"],arguments),a.callback&&!b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");return a.buttons.ok.callback=a.onEscape=function(){return b.isFunction(a.callback)?a.callback():!0},p.dialog(a)},p.confirm=function(){var a;if(a=k("confirm",["cancel","confirm"],["message","callback"],arguments),a.buttons.cancel.callback=a.onEscape=function(){return a.callback(!1)},a.buttons.confirm.callback=function(){return a.callback(!0)},!b.isFunction(a.callback))throw new Error("confirm requires a callback");return p.dialog(a)},p.prompt=function(){var a,d,e,f,h,i,k;if(f=b(n.form),d={className:"bootbox-prompt",buttons:l("cancel","confirm"),value:"",inputType:"text"},a=m(j(d,arguments,["title","callback"]),["cancel","confirm"]),i=a.show===c?!0:a.show,a.message=f,a.buttons.cancel.callback=a.onEscape=function(){return a.callback(null)},a.buttons.confirm.callback=function(){var c;switch(a.inputType){case"text":case"textarea":case"email":case"select":case"date":case"time":case"number":case"password":c=h.val();break;case"checkbox":var d=h.find("input:checked");c=[],g(d,function(a,d){c.push(b(d).val())})}return a.callback(c)},a.show=!1,!a.title)throw new Error("prompt requires a title");if(!b.isFunction(a.callback))throw new Error("prompt requires a callback");if(!n.inputs[a.inputType])throw new Error("invalid prompt type");switch(h=b(n.inputs[a.inputType]),a.inputType){case"text":case"textarea":case"email":case"date":case"time":case"number":case"password":h.val(a.value);break;case"select":var o={};if(k=a.inputOptions||[],!k.length)throw new Error("prompt with select requires options");g(k,function(a,d){var e=h;if(d.value===c||d.text===c)throw new Error("given options in wrong format");d.group&&(o[d.group]||(o[d.group]=b("").attr("label",d.group)),e=o[d.group]),e.append("")}),g(o,function(a,b){h.append(b)}),h.val(a.value);break;case"checkbox":var q=b.isArray(a.value)?a.value:[a.value];if(k=a.inputOptions||[],!k.length)throw new Error("prompt with checkbox requires options");if(!k[0].value||!k[0].text)throw new Error("given options in wrong format");h=b("
"),g(k,function(c,d){var e=b(n.inputs[a.inputType]);e.find("input").attr("value",d.value),e.find("label").append(d.text),g(q,function(a,b){b===d.value&&e.find("input").prop("checked",!0)}),h.append(e)})}return a.placeholder&&h.attr("placeholder",a.placeholder),a.pattern&&h.attr("pattern",a.pattern),f.append(h),f.on("submit",function(a){a.preventDefault(),a.stopPropagation(),e.find(".btn-primary").click()}),e=p.dialog(a),e.off("shown.bs.modal"),e.on("shown.bs.modal",function(){h.focus()}),i===!0&&e.modal("show"),e},p.dialog=function(a){a=h(a);var c=b(n.dialog),d=c.find(".modal-dialog"),f=c.find(".modal-body"),i=a.buttons,j="",k={onEscape:a.onEscape};if(g(i,function(a,b){j+="",k[a]=b.callback}),f.find(".bootbox-body").html(a.message),a.animate===!0&&c.addClass("fade"),a.className&&c.addClass(a.className),"large"===a.size&&d.addClass("modal-lg"),"small"===a.size&&d.addClass("modal-sm"),a.title&&f.before(n.header),a.closeButton){var l=b(n.closeButton);a.title?c.find(".modal-header").prepend(l):l.css("margin-top","-10px").prependTo(f)}return a.title&&c.find(".modal-title").html(a.title),j.length&&(f.after(n.footer),c.find(".modal-footer").html(j)),c.on("hidden.bs.modal",function(a){a.target===this&&c.remove()}),c.on("shown.bs.modal",function(){c.find(".btn-primary:first").focus()}),c.on("escape.close.bb",function(a){k.onEscape&&e(a,c,k.onEscape)}),c.on("click",".modal-footer button",function(a){var d=b(this).data("bb-handler");e(a,c,k[d])}),c.on("click",".bootbox-close-button",function(a){e(a,c,k.onEscape)}),c.on("keyup",function(a){27===a.which&&c.trigger("escape.close.bb")}),b(a.container).append(c),c.modal({backdrop:a.backdrop,keyboard:!1,show:!1}),a.show&&c.modal("show"),c},p.setDefaults=function(){var a={};2===arguments.length?a[arguments[0]]=arguments[1]:a=arguments[0],b.extend(o,a)},p.hideAll=function(){return b(".bootbox").modal("hide"),p};var q={br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},cs:{OK:"OK",CANCEL:"Zrušit",CONFIRM:"Potvrdit"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},el:{OK:"Εντάξει",CANCEL:"Ακύρωση",CONFIRM:"Επιβεβαίωση"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},et:{OK:"OK",CANCEL:"Katkesta",CONFIRM:"OK"},fi:{OK:"OK",CANCEL:"Peruuta",CONFIRM:"OK"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},he:{OK:"אישור",CANCEL:"ביטול",CONFIRM:"אישור"},id:{OK:"OK",CANCEL:"Batal",CONFIRM:"OK"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},ja:{OK:"OK",CANCEL:"キャンセル",CONFIRM:"確認"},lt:{OK:"Gerai",CANCEL:"Atšaukti",CONFIRM:"Patvirtinti"},lv:{OK:"Labi",CANCEL:"Atcelt",CONFIRM:"Apstiprināt"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},no:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},pt:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Confirmar"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},sv:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},tr:{OK:"Tamam",CANCEL:"İptal",CONFIRM:"Onayla"},zh_CN:{OK:"OK",CANCEL:"取消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"取消",CONFIRM:"確認"}};return p.init=function(c){return a(c||b)},p}); \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap-growl.min.js b/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap-growl.min.js new file mode 100644 index 0000000..ce6deb1 --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap-growl.min.js @@ -0,0 +1,2 @@ +/* Project: Bootstrap Growl - v2.0.0 | Author: Mouse0270 aka Robert McIntosh | License: MIT License | Website: https://github.com/mouse0270/bootstrap-growl */ +(function(e,t,n,r){var i="growl",s="plugin_"+i,o={element:"body",type:"info",allow_dismiss:true,placement:{from:"top",align:"right"},offset:20,spacing:10,z_index:1031,delay:5e3,timer:1e3,url_target:"_blank",mouse_over:false,animate:{enter:"animated fadeInDown",exit:"animated fadeOutUp"},icon_type:"class",template:''};var u=function(t,n){o=e.extend(true,{},o,n)},a=function(t,n,r){var n={content:{message:typeof n=="object"?n.message:n,title:n.title?n.title:null,icon:n.icon?n.icon:null,url:n.url?n.url:null}};r=e.extend(true,{},n,r);this.settings=e.extend(true,{},o,r);plugin=this;f(r,this.settings,plugin);this.$template=$template},f=function(t,n,r){var i={settings:n,$element:e(n.element),template:n.template};$template=l(i);c($template,i.settings);h($template,i.settings);p($template,i.settings,r)},l=function(t){var n=e(t.settings.template);n.addClass("alert-"+t.settings.type);n.attr("data-growl-position",t.settings.placement.from+"-"+t.settings.placement.align);n.find('[data-growl="dismiss"]').css("display","none");if(t.settings.allow_dismiss){n.find('[data-growl="dismiss"]').css("display","inline-block")}return n},c=function(e,t){e.find('[data-growl="dismiss"]').css({position:"absolute",top:"5px",right:"10px","z-index":t.z_index-1>=1?t.z_index-1:1});if(t.content.icon){if(t.icon_type.toLowerCase()=="class"){e.find('[data-growl="icon"]').addClass(t.content.icon)}else{if(e.find('[data-growl="icon"]').is("img")){e.find('[data-growl="icon"]').attr("src",t.content.icon)}else{e.find('[data-growl="icon"]').append('')}}}if(t.content.title){e.find('[data-growl="title"]').html(t.content.title)}if(t.content.message){e.find('[data-growl="message"]').html(t.content.message)}if(t.content.url){e.find('[data-growl="url"]').attr("href",t.content.url).attr("target",t.url_target);e.find('[data-growl="url"]').css({position:"absolute",top:"0px",left:"0px",width:"100%",height:"100%","z-index":t.z_index-2>=1?t.z_index-2:1})}},h=function(t,n){var r=n.offset,i={position:n.element==="body"?"fixed":"absolute",margin:0,"z-index":n.z_index,display:"inline-block"};e('[data-growl-position="'+n.placement.from+"-"+n.placement.align+'"]').each(function(){return r=Math.max(r,parseInt(e(this).css(n.placement.from))+e(this).outerHeight()+n.spacing)});i[n.placement.from]=r+"px";t.css(i);e(n.element).append(t);switch(n.placement.align){case"center":t.css({left:"50%",marginLeft:-(t.outerWidth()/2)+"px"});break;case"left":t.css("left",n.offset+"px");break;case"right":t.css("right",n.offset+"px");break}t.addClass("growl-animated")},p=function(e,t,n){e.addClass(t.animate.enter);e.find('[data-growl="dismiss"]').on("click",function(){n.close()});e.on("mouseover",function(t){e.addClass("hovering")}).on("mouseout",function(){e.removeClass("hovering")});if(t.delay>=1){e.data("growl-delay",t.delay);var r=setInterval(function(){var i=parseInt(e.data("growl-delay"))-t.timer;console.log();if(!e.hasClass("hovering")&&t.mouse_over=="pause"||t.mouse_over!="pause"){e.data("growl-delay",i)}if(i<=0){clearInterval(r);n.close()}},t.timer)}};a.prototype={update:function(e,t){switch(e){case"icon":if(this.settings.icon_type.toLowerCase()=="class"){this.$template.find('[data-growl="icon"]').removeClass(this.settings.content.icon);this.$template.find('[data-growl="icon"]').addClass(t)}else{if(this.$template.find('[data-growl="icon"]').is("img")){this.$template.find('[data-growl="icon"]')}else{this.$template.find('[data-growl="icon"]').find("img").attr().attr("src",t)}}break;case"url":this.$template.find('[data-growl="url"]').attr("href",t);break;case"type":this.$template.removeClass("alert-"+this.settings.type);this.$template.addClass("alert-"+t);break;default:this.$template.find('[data-growl="'+e+'"]').html(t)}return this},close:function(){var t=this.$template,n=this.settings,r=t.css(n.placement.from),i=false;t.addClass(this.settings.animate.exit);t.nextAll('[data-growl-position="'+this.settings.placement.from+"-"+this.settings.placement.align+'"]').each(function(){e(this).css(n.placement.from,r);r=parseInt(r)+n.spacing+e(this).outerHeight()});t.one("webkitAnimationStart oanimationstart MSAnimationStart animationstart",function(e){i=true});t.one("webkitAnimationEnd oanimationend MSAnimationEnd animationend",function(t){e(this).remove()});setTimeout(function(){console.log(i);if(!i){t.remove()}},100);return this}};e.growl=function(e,t){if(e==false){u(this,t);return false}var n=new a(this,e,t);return n}})(jQuery,window,document) \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/css/bootstrap-dt.css b/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/css/bootstrap-dt.css new file mode 100644 index 0000000..2828337 --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/css/bootstrap-dt.css @@ -0,0 +1,5804 @@ +/*! + * Bootstrap v3.1.1 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! normalize.css v3.0.0 | MIT License | git.io/normalize */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { +} +@media print { + * { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + select { + background: #fff !important; + } + .navbar { + display: none; + } + .table td, + .table th { + background-color: #fff !important; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +.dataTableWrapperDiv div { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 62.5%; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #428bca; + text-decoration: none; +} +a:hover, +a:focus { + color: #2a6496; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #999; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 200; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +cite { + font-style: normal; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-muted { + color: #999; +} +.text-primary { + color: #428bca; +} +a.text-primary:hover { + color: #3071a9; +} +.text-success { + color: #3c763d; +} +a.text-success:hover { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #428bca; +} +a.bg-primary:hover { + background-color: #3071a9; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +blockquote:before, +blockquote:after { + content: ""; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + white-space: nowrap; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: 0; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: 0; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .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 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: 0; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: 0; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .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 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: 0; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: 0; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .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 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: 0; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: 0; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + max-width: 100%; + background-color: transparent; +} +th { + text-align: left; +} +.table { + width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-child(odd) > td, +.table-striped > tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover > td, +.table-hover > tbody > tr:hover > th { + /*background-color: #f5f5f5;*/ + background-color:#dbedff; +} +.dataTableTdSelected{ + background-color:#dbedff !important; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +@media (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-x: scroll; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + /* IE8-9 */ + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eee; + opacity: 1; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +input[type="date"] { + line-height: 34px; +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + display: block; + min-height: 20px; + padding-left: 20px; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + display: inline; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + float: left; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +.radio[disabled], +.radio-inline[disabled], +.checkbox[disabled], +.checkbox-inline[disabled], +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"], +fieldset[disabled] .radio, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.has-feedback .form-control-feedback { + position: absolute; + top: 25px; + right: 0; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.form-control-static { + margin-bottom: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .control-label, +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +.form-horizontal .form-control-static { + padding-top: 7px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + top: 0; + right: 15px; +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +.btn.default { + color: #333333; + text-shadow: none; + background-color: #e5e5e5; + height:12px; +} +.btn.default:hover, +.btn.default:focus, +.btn.default:active, +.btn.default.active, +.btn.default[disabled], +.btn.default.disabled { + color: #333333; + background-color: #d8d8d8 !important; + outline: none !important; +} + +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + color: #333; + background-color: #ebebeb; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #428bca; + border-color: #357ebd; +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + color: #fff; + background-color: #3276b1; + border-color: #285e8e; +} +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #428bca; + border-color: #357ebd; +} +.btn-primary .badge { + color: #428bca; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + color: #fff; + background-color: #47a447; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + color: #fff; + background-color: #39b3d7; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ed9c28; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + color: #fff; + background-color: #d2322d; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #428bca; + cursor: pointer; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #2a6496; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #999; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-sm { + padding: 2px 10px 8px 10px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height .35s ease; + transition: height .35s ease; +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\2a"; +} +.glyphicon-plus:before { + content: "\2b"; +} +.glyphicon-euro:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #428bca; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #999; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px solid; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus { + outline: none; +} +.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 { + margin-left: -5px; +} +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child > .btn:last-child, +.btn-group > .btn-group:first-child > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.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:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +[data-toggle="buttons"] > .btn > input[type="radio"], +[data-toggle="buttons"] > .btn > input[type="checkbox"] { + display: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #428bca; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #428bca; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + max-height: 340px; + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: none; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #999; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #999; +} +.navbar-inverse .navbar-nav > li > a { + color: #999; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #999; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #999; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #999; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #428bca; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: #2a6496; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #fff; + cursor: default; + background-color: #428bca; + border-color: #428bca; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +.label[href]:hover, +.label[href]:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #999; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #808080; +} +.label-primary { + background-color: #428bca; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #3071a9; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #999; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #428bca; + background-color: #fff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.container .jumbotron { + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #428bca; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable { + padding-right: 35px; +} +.alert-dismissable .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #428bca; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-size: 40px 40px; +} +.progress.active .progress-bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media, +.media .media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media-object { + display: block; +} +.media-heading { + margin: 0 0 5px; +} +.media > .pull-left { + margin-right: 10px; +} +.media > .pull-right { + margin-left: 10px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +a.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +a.list-group-item:focus { + text-decoration: none; + background-color: #f5f5f5; +} +a.list-group-item.active, +a.list-group-item.active:hover, +a.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #428bca; + border-color: #428bca; +} +a.list-group-item.active .list-group-item-heading, +a.list-group-item.active:hover .list-group-item-heading, +a.list-group-item.active:focus .list-group-item-heading { + color: inherit; +} +a.list-group-item.active .list-group-item-text, +a.list-group-item.active:hover .list-group-item-text, +a.list-group-item.active:focus .list-group-item-text { + color: #e1edf7; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +a.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +a.list-group-item-success.active:hover, +a.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +a.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +a.list-group-item-info.active:hover, +a.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +a.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +a.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table { + margin-bottom: 0; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + overflow: hidden; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse .panel-body { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #428bca; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #428bca; + border-color: #428bca; +} +.panel-primary > .panel-heading + .panel-collapse .panel-body { + border-top-color: #428bca; +} +.panel-primary > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #428bca; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #ebccd1; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: auto; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -moz-transition: -moz-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + /*background-color: #000;*/ + background-color: #fff; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + min-height: 16.42857143px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 20px; +} +.modal-footer { + padding: 19px 20px 20px; + margin-top: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1030; + display: block; + font-size: 12px; + line-height: 1.4; + visibility: visible; + filter: alpha(opacity=0); + opacity: 0; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + text-decoration: none; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + bottom: 0; + left: 5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + right: 5px; + bottom: 0; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + left: 5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + right: 5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, .5) 0%), color-stop(rgba(0, 0, 0, .0001) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, .0001) 0%), color-stop(rgba(0, 0, 0, .5) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: none; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + margin-left: -10px; + font-family: serif; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + 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; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + margin-left: -15px; + font-size: 30px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/css/bootstrap.min.css b/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/css/bootstrap.min.css new file mode 100644 index 0000000..679272d --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/css/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.1.1 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*{text-shadow:none!important;color:#000!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-muted{color:#999}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;white-space:nowrap;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:0}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:0}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.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{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:0}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:0}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.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{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:0}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:0}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.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{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:0}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:0}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*=col-]{position:static;float:none;display:table-column}table td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date]{line-height:34px}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;margin-top:10px;margin-bottom:10px;padding-left:20px}.radio label,.checkbox label{display:inline;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:400;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.has-feedback .form-control-feedback{position:absolute;top:25px;right:0;display:block;width:34px;height:34px;line-height:34px;text-align:center}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{float:none;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-control-static{padding-top:7px}@media (min-width:768px){.form-horizontal .control-label{text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:400;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.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{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.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:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle=buttons]>.btn>input[type=radio],[data-toggle=buttons]>.btn>input[type=checkbox]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{float:none;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:gray}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#999;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px;overflow:hidden}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#faebcc}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#ebccd1}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ebccd1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:auto;overflow-y:scroll;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);background-clip:padding-box;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;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{margin-top:15px;padding:19px 20px 20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;right:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);white-space:normal}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.5) 0),color-stop(rgba(0,0,0,.0001) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.0001) 0),color-stop(rgba(0,0,0,.5) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/js/bootstrap.js b/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/js/bootstrap.js new file mode 100644 index 0000000..8ae571b --- /dev/null +++ b/apiroute/apiroute-service/src/main/resources/iui-route/js/bootstrap/js/bootstrap.js @@ -0,0 +1,1951 @@ +/*! + * Bootstrap v3.1.1 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') } + +/* ======================================================================== + * Bootstrap: transition.js v3.1.1 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + 'WebkitTransition' : 'webkitTransitionEnd', + 'MozTransition' : 'transitionend', + 'OTransition' : 'oTransitionEnd otransitionend', + 'transition' : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + + return false // explicit for ie8 ( ._.) + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false, $el = this + $(this).one($.support.transition.end, function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.1.1 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.hasClass('alert') ? $this : $this.parent() + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + $parent.trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one($.support.transition.end, removeElement) + .emulateTransitionEnd(150) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + var old = $.fn.alert + + $.fn.alert = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.1.1 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state = state + 'Text' + + if (!data.resetText) $el.data('resetText', $el[val]()) + + $el[val](data[state] || this.options[state]) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked') && this.$element.hasClass('active')) changed = false + else $parent.find('.active').removeClass('active') + } + if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } + + if (changed) this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + var old = $.fn.button + + $.fn.button = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + $btn.button('toggle') + e.preventDefault() + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.1.1 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = + this.sliding = + this.interval = + this.$active = + this.$items = null + + this.options.pause == 'hover' && this.$element + .on('mouseenter', $.proxy(this.pause, this)) + .on('mouseleave', $.proxy(this.cycle, this)) + } + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getActiveIndex = function () { + this.$active = this.$element.find('.item.active') + this.$items = this.$active.parent().children() + + return this.$items.index(this.$active) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getActiveIndex() + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || $active[type]() + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var fallback = type == 'next' ? 'first' : 'last' + var that = this + + if (!$next.length) { + if (!this.options.wrap) return + $next = this.$element.find('.item')[fallback]() + } + + if ($next.hasClass('active')) return this.sliding = false + + var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction }) + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + this.$element.one('slid.bs.carousel', function () { + var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) + $nextIndicator && $nextIndicator.addClass('active') + }) + } + + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one($.support.transition.end, function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0) + }) + .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger('slid.bs.carousel') + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + var old = $.fn.carousel + + $.fn.carousel = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var $this = $(this), href + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + $target.carousel(options) + + if (slideIndex = $this.attr('data-slide-to')) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + }) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + $carousel.carousel($carousel.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.1.1 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.transitioning = null + + if (this.options.parent) this.$parent = $(this.options.parent) + if (this.options.toggle) this.toggle() + } + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var actives = this.$parent && this.$parent.find('> .panel > .in') + + if (actives && actives.length) { + var hasData = actives.data('bs.collapse') + if (hasData && hasData.transitioning) return + actives.collapse('hide') + hasData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing') + [dimension](0) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in') + [dimension]('auto') + this.transitioning = 0 + this.$element.trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one($.support.transition.end, $.proxy(complete, this)) + .emulateTransitionEnd(350) + [dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element + [dimension](this.$element[dimension]()) + [0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse') + .removeClass('in') + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .trigger('hidden.bs.collapse') + .removeClass('collapsing') + .addClass('collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one($.support.transition.end, $.proxy(complete, this)) + .emulateTransitionEnd(350) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + var old = $.fn.collapse + + $.fn.collapse = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && option == 'show') option = !option + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) { + var $this = $(this), href + var target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 + var $target = $(target) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + var parent = $this.attr('data-parent') + var $parent = parent && $(parent) + + if (!data || !data.transitioning) { + if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed') + $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + } + + $target.collapse(option) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.1.1 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle=dropdown]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $('